@groundnuty/macf 0.2.38 → 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/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.map +1 -1
- package/dist/cli/commands/init.js +8 -0
- 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/registry-prune.d.ts +43 -6
- package/dist/cli/commands/registry-prune.d.ts.map +1 -1
- package/dist/cli/commands/registry-prune.js +53 -14
- package/dist/cli/commands/registry-prune.js.map +1 -1
- 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/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 +89 -0
- package/dist/cli/index.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/emit-turn-receipt.sh +44 -4
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/** The three restart drivers (DR-031 §"Be-replaceable" — fault / upgrade / manual). */
|
|
2
|
+
export declare const RESTART_REASONS: readonly ["fault", "upgrade", "manual"];
|
|
3
|
+
export type RestartReason = (typeof RESTART_REASONS)[number];
|
|
4
|
+
/** Coerce an arbitrary `--reason` string to a known reason; defaults to `manual`. */
|
|
5
|
+
export declare function coerceReason(raw: string | undefined): RestartReason;
|
|
6
|
+
/** Result of a stash attempt. `stashed: false` when there was nothing to stash. */
|
|
7
|
+
export interface StashResult {
|
|
8
|
+
readonly stashed: boolean;
|
|
9
|
+
readonly ref?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Every side effect `runRestartSelf` performs, injected so tests verify the
|
|
13
|
+
* orchestration WITHOUT real stashes / kills / spawns. The git READS (branch,
|
|
14
|
+
* HEAD, dirty-state) are side effects too, so they live here as well.
|
|
15
|
+
*/
|
|
16
|
+
export interface RestartSelfDeps {
|
|
17
|
+
readonly now: () => Date;
|
|
18
|
+
readonly hasUncommittedTrackedChanges: () => boolean;
|
|
19
|
+
readonly currentBranch: () => string;
|
|
20
|
+
readonly headSha: () => string;
|
|
21
|
+
readonly stash: (label: string) => StashResult;
|
|
22
|
+
readonly writeFile: (path: string, content: string, mode?: number) => void;
|
|
23
|
+
readonly mkdirp: (path: string) => void;
|
|
24
|
+
readonly spawnDetached: (scriptPath: string, args: readonly string[]) => void;
|
|
25
|
+
readonly killSession: (session: string) => void;
|
|
26
|
+
}
|
|
27
|
+
/** Options for `runRestartSelf` (already-resolved identity; pure orchestrator input). */
|
|
28
|
+
export interface RunRestartSelfOptions {
|
|
29
|
+
/** Absolute workspace dir (holds `claude.sh` + `.claude/.macf/`). */
|
|
30
|
+
readonly workspaceDir: string;
|
|
31
|
+
/** Project name (for the `<project>@<agent>` session derivation). */
|
|
32
|
+
readonly project?: string;
|
|
33
|
+
/** Agent name (for the `<project>@<agent>` session derivation). */
|
|
34
|
+
readonly agentName?: string;
|
|
35
|
+
/** Explicit session override; when set it wins over the derived form. */
|
|
36
|
+
readonly session?: string;
|
|
37
|
+
readonly reason: RestartReason;
|
|
38
|
+
/** Without this, the command is DRY-RUN regardless. */
|
|
39
|
+
readonly confirm: boolean;
|
|
40
|
+
/** Force dry-run even with `--confirm` (the safer of the two wins). */
|
|
41
|
+
readonly dryRun: boolean;
|
|
42
|
+
readonly json: boolean;
|
|
43
|
+
}
|
|
44
|
+
/** The `--json` state-record (mirrors `fleet doctor`'s versioned shape). */
|
|
45
|
+
export declare const RESTART_SELF_JSON_SCHEMA_VERSION = 1;
|
|
46
|
+
export interface RestartSelfPlan {
|
|
47
|
+
readonly schema_version: number;
|
|
48
|
+
readonly dry_run: boolean;
|
|
49
|
+
readonly reason: RestartReason;
|
|
50
|
+
readonly session: string;
|
|
51
|
+
readonly stash_ref: string | null;
|
|
52
|
+
readonly resume_note_path: string;
|
|
53
|
+
readonly relauncher_path: string;
|
|
54
|
+
readonly killed: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** Derive `<project>@<agent>` (the canonical claude.sh self-wrap session), or null. */
|
|
57
|
+
export declare function resolveSession(opts: RunRestartSelfOptions): string | null;
|
|
58
|
+
/** The marked-stash label: `macf-restart-self/<ISO-8601-ts>/<reason>`. */
|
|
59
|
+
export declare function stashLabel(iso: string, reason: RestartReason): string;
|
|
60
|
+
/** The RESUME-note body — what a future session needs to pick the work back up. */
|
|
61
|
+
export declare function buildResumeNote(args: {
|
|
62
|
+
readonly reason: RestartReason;
|
|
63
|
+
readonly iso: string;
|
|
64
|
+
readonly branch: string;
|
|
65
|
+
readonly head: string;
|
|
66
|
+
readonly stashRef: string | null;
|
|
67
|
+
}): string;
|
|
68
|
+
/**
|
|
69
|
+
* The detached relauncher script. Waits for the OLD session to die (up to ~30s),
|
|
70
|
+
* then `cd`s to the workspace, sources the host-prelude IF it exists (decoupled
|
|
71
|
+
* from DR-031 piece 4 — proceed if absent), and `exec`s the launcher. Uses
|
|
72
|
+
* absolute paths so it does not depend on the dying session's env beyond what it
|
|
73
|
+
* re-establishes.
|
|
74
|
+
*/
|
|
75
|
+
export declare function buildRelauncherScript(args: {
|
|
76
|
+
readonly workspaceDir: string;
|
|
77
|
+
readonly session: string;
|
|
78
|
+
readonly iso: string;
|
|
79
|
+
}): string;
|
|
80
|
+
/**
|
|
81
|
+
* Pure orchestrator. Returns the shell exit code. DRY-RUN BY DEFAULT — only a
|
|
82
|
+
* `--confirm` (and not `--dry-run`) run stashes / writes / spawns / kills.
|
|
83
|
+
* Refuses (exit 1) when the session name cannot be resolved.
|
|
84
|
+
*/
|
|
85
|
+
export declare function runRestartSelf(opts: RunRestartSelfOptions, deps: RestartSelfDeps): Promise<number>;
|
|
86
|
+
/**
|
|
87
|
+
* Real side-effect implementations bound to a workspace dir. Git reads/stash run
|
|
88
|
+
* in `workspaceDir`; the spawn is FULLY DETACHED (`detached: true` opens a new
|
|
89
|
+
* session — the Node equivalent of `setsid` — plus `stdio: 'ignore'` + `unref()`
|
|
90
|
+
* so the relauncher outlives this process when its session is killed).
|
|
91
|
+
*/
|
|
92
|
+
export declare function createRealDeps(workspaceDir: string): RestartSelfDeps;
|
|
93
|
+
export interface RestartSelfCliOptions {
|
|
94
|
+
readonly reason?: string;
|
|
95
|
+
readonly confirm?: boolean;
|
|
96
|
+
readonly dryRun?: boolean;
|
|
97
|
+
readonly json?: boolean;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Resolve identity (workspace / project / agent) from env first (the running
|
|
101
|
+
* agent's `claude.sh`-exported values), falling back to `.macf/macf-agent.json`.
|
|
102
|
+
* The canonical session claude.sh self-wraps into is `${MACF_PROJECT}@${MACF_AGENT_NAME}`.
|
|
103
|
+
*/
|
|
104
|
+
export declare function resolveIdentity(projectDir: string, env?: NodeJS.ProcessEnv): {
|
|
105
|
+
readonly workspaceDir: string;
|
|
106
|
+
readonly project?: string;
|
|
107
|
+
readonly agentName?: string;
|
|
108
|
+
};
|
|
109
|
+
/** `macf restart-self` entry point — resolves config, wires real deps, runs. */
|
|
110
|
+
export declare function runRestartSelfCommand(projectDir: string, cliOpts: RestartSelfCliOptions): Promise<number>;
|
|
111
|
+
//# sourceMappingURL=restart-self.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-self.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/restart-self.ts"],"names":[],"mappings":"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"}
|
|
@@ -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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"}
|