@mu-crew/mu 4.0.0 → 4.1.0
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/README.md +1 -1
- package/dist/{chunk-5HECDBK3.js → chunk-4ULY3ZKO.js} +16 -16
- package/dist/{chunk-AW2NONYU.js → chunk-7M634JMD.js} +81 -7
- package/dist/chunk-7M634JMD.js.map +1 -0
- package/dist/{chunk-GQNHIPJN.js → chunk-SS7NQSSZ.js} +21 -421
- package/dist/chunk-SS7NQSSZ.js.map +1 -0
- package/dist/{chunk-S424D5EP.js → chunk-UCZPJX7K.js} +628 -141
- package/dist/chunk-UCZPJX7K.js.map +1 -0
- package/dist/{cli-GB2CAVVW.js → cli-TV3N47AZ.js} +340 -42
- package/dist/cli-TV3N47AZ.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/{errors-KHOYKD7D.js → errors-GRONKWFS.js} +2 -2
- package/dist/extension/mu-pi.js +28 -14
- package/dist/extension/mu-pi.js.map +1 -1
- package/dist/index.d.ts +63 -11
- package/dist/index.js +15 -15
- package/dist/{tui-ALPHYTA2.js → tui-FUBQ326D.js} +102 -27
- package/dist/tui-FUBQ326D.js.map +1 -0
- package/docs/ARCHITECTURE.md +5 -4
- package/docs/VOCABULARY.md +6 -5
- package/docs/guide/cleanup.md +8 -0
- package/docs/guide/delegate.md +11 -3
- package/docs/guide/remote.md +19 -12
- package/docs/reference/env.md +1 -0
- package/docs/reference/naming.md +2 -0
- package/package.json +1 -1
- package/skills/mu/recipes/remote-workers.md +7 -6
- package/dist/chunk-AW2NONYU.js.map +0 -1
- package/dist/chunk-GQNHIPJN.js.map +0 -1
- package/dist/chunk-S424D5EP.js.map +0 -1
- package/dist/cli-GB2CAVVW.js.map +0 -1
- package/dist/tui-ALPHYTA2.js.map +0 -1
- /package/dist/{chunk-5HECDBK3.js.map → chunk-4ULY3ZKO.js.map} +0 -0
- /package/dist/{errors-KHOYKD7D.js.map → errors-GRONKWFS.js.map} +0 -0
|
@@ -41,11 +41,8 @@ import {
|
|
|
41
41
|
steerStallHandler,
|
|
42
42
|
waitForEvents,
|
|
43
43
|
waitWithActions
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-4ULY3ZKO.js";
|
|
45
45
|
import {
|
|
46
|
-
DelegateCallConflictError,
|
|
47
|
-
DelegateNotFoundError,
|
|
48
|
-
DelegateStateError,
|
|
49
46
|
FRONTIER_MAX_DEPTH,
|
|
50
47
|
HumanNeededInvalidError,
|
|
51
48
|
NEXT_BYTES,
|
|
@@ -56,7 +53,6 @@ import {
|
|
|
56
53
|
SyncPeerRefAmbiguousError,
|
|
57
54
|
SyncSourceNotFoundError,
|
|
58
55
|
SyncSourceTooNewError,
|
|
59
|
-
TERMINAL_DELEGATE_STATES,
|
|
60
56
|
TaskAutoArgError,
|
|
61
57
|
TaskAutoRefusedError,
|
|
62
58
|
TaskBriefMissingError,
|
|
@@ -75,27 +71,23 @@ import {
|
|
|
75
71
|
currentCallers,
|
|
76
72
|
encodeEnvelope,
|
|
77
73
|
escapeRecordText,
|
|
78
|
-
finishDelegate,
|
|
79
74
|
footerLine,
|
|
80
75
|
foreignVersionMessage,
|
|
81
|
-
getDelegateCall,
|
|
82
76
|
ingestFromDb,
|
|
83
77
|
jsonDocument,
|
|
84
78
|
linesBytes,
|
|
85
|
-
listDelegates,
|
|
86
79
|
liveAgentToOutput,
|
|
87
80
|
loadOpenTaskFrontier,
|
|
81
|
+
localMachineId,
|
|
88
82
|
logOut,
|
|
89
83
|
logPage,
|
|
90
84
|
logReceipt,
|
|
91
|
-
markDelegate,
|
|
92
85
|
noticesField,
|
|
93
86
|
okEnvelope,
|
|
94
87
|
pageOut,
|
|
95
88
|
pageRecords,
|
|
96
89
|
peerStatuses,
|
|
97
90
|
recordHumanNeeded,
|
|
98
|
-
registerDelegateCall,
|
|
99
91
|
renderArgv,
|
|
100
92
|
repairPeer,
|
|
101
93
|
resumeArgv,
|
|
@@ -108,9 +100,8 @@ import {
|
|
|
108
100
|
taskPage,
|
|
109
101
|
taskRecordCut,
|
|
110
102
|
taskToOutput,
|
|
111
|
-
verdictTally,
|
|
112
103
|
workstreamIdOf
|
|
113
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-SS7NQSSZ.js";
|
|
114
105
|
import {
|
|
115
106
|
AgentAbortNeedsCtlError,
|
|
116
107
|
AgentAbortTimeoutError,
|
|
@@ -141,6 +132,9 @@ import {
|
|
|
141
132
|
CrossWorkstreamEdgeError,
|
|
142
133
|
CtlDeliveryUnknownError,
|
|
143
134
|
CycleError,
|
|
135
|
+
DelegateCallConflictError,
|
|
136
|
+
DelegateNotFoundError,
|
|
137
|
+
DelegateStateError,
|
|
144
138
|
EXPECTED_TABLES,
|
|
145
139
|
GUARD_ALREADY_OWNED,
|
|
146
140
|
GUARD_HUGE_ID,
|
|
@@ -182,6 +176,7 @@ import {
|
|
|
182
176
|
SubstateReasonRequiredError,
|
|
183
177
|
TASK_STATUS_LIST,
|
|
184
178
|
TASK_SUBSTATES,
|
|
179
|
+
TERMINAL_DELEGATE_STATES,
|
|
185
180
|
TaskAlreadyOwnedError,
|
|
186
181
|
TaskExistsError,
|
|
187
182
|
TaskHasOpenBlockersError,
|
|
@@ -240,6 +235,8 @@ import {
|
|
|
240
235
|
deadStagings,
|
|
241
236
|
defaultDbPath,
|
|
242
237
|
delegateOutcome,
|
|
238
|
+
delegateProcessDir,
|
|
239
|
+
delegateSessionDir,
|
|
243
240
|
deleteTask,
|
|
244
241
|
dirtyFiles,
|
|
245
242
|
ensureWorkstream,
|
|
@@ -247,10 +244,12 @@ import {
|
|
|
247
244
|
errCode,
|
|
248
245
|
expectsCtl,
|
|
249
246
|
findEdgeCycles,
|
|
247
|
+
finishDelegate,
|
|
250
248
|
forget,
|
|
251
249
|
formatPair,
|
|
252
250
|
getAgent,
|
|
253
251
|
getAgentByPane,
|
|
252
|
+
getDelegateCall,
|
|
254
253
|
getHost,
|
|
255
254
|
getTask,
|
|
256
255
|
getTaskEdgesWithStatus,
|
|
@@ -274,6 +273,7 @@ import {
|
|
|
274
273
|
lastClaimActor,
|
|
275
274
|
latestSeq,
|
|
276
275
|
listAgents,
|
|
276
|
+
listDelegates,
|
|
277
277
|
listEmptyWorkstreams,
|
|
278
278
|
listForgetCandidates,
|
|
279
279
|
listHostChannels,
|
|
@@ -288,6 +288,7 @@ import {
|
|
|
288
288
|
listTornDownWorkstreams,
|
|
289
289
|
listWorkstreams,
|
|
290
290
|
listWorktrees,
|
|
291
|
+
markDelegate,
|
|
291
292
|
muTable,
|
|
292
293
|
murmurAvailable,
|
|
293
294
|
openDb,
|
|
@@ -307,6 +308,7 @@ import {
|
|
|
307
308
|
reconcile,
|
|
308
309
|
recoveriesFor,
|
|
309
310
|
recoveryWorkstreams,
|
|
311
|
+
registerDelegateCall,
|
|
310
312
|
registerOwnedWait,
|
|
311
313
|
registerWorktree,
|
|
312
314
|
reinitWorkstream,
|
|
@@ -340,20 +342,21 @@ import {
|
|
|
340
342
|
unparkTask,
|
|
341
343
|
updateTask,
|
|
342
344
|
usageLines,
|
|
345
|
+
verdictTally,
|
|
343
346
|
weakDecisionWarning,
|
|
344
347
|
withOpContext,
|
|
345
348
|
withTaskFence,
|
|
346
349
|
workstreamRevision,
|
|
347
350
|
worktreePath,
|
|
348
351
|
worktreesRoot
|
|
349
|
-
} from "./chunk-
|
|
352
|
+
} from "./chunk-UCZPJX7K.js";
|
|
350
353
|
import {
|
|
351
354
|
defaultStateDir
|
|
352
355
|
} from "./chunk-PZ5GOCWH.js";
|
|
353
356
|
|
|
354
357
|
// src/cli.ts
|
|
355
358
|
import { readFileSync as readFileSync3 } from "fs";
|
|
356
|
-
import { dirname as
|
|
359
|
+
import { dirname as dirname7, join as join5 } from "path";
|
|
357
360
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
358
361
|
|
|
359
362
|
// node_modules/commander/lib/error.js
|
|
@@ -4650,7 +4653,7 @@ async function parseWaitRef(db, raw, scope, legacy = {}) {
|
|
|
4650
4653
|
return { kind, workstream, id: parsed.name };
|
|
4651
4654
|
}
|
|
4652
4655
|
if (getAgent(db, parsed.name, workstream) === void 0) {
|
|
4653
|
-
const { AgentNotFoundError: AgentNotFoundError2 } = await import("./errors-
|
|
4656
|
+
const { AgentNotFoundError: AgentNotFoundError2 } = await import("./errors-GRONKWFS.js");
|
|
4654
4657
|
throw new AgentNotFoundError2(parsed.name);
|
|
4655
4658
|
}
|
|
4656
4659
|
return {
|
|
@@ -6994,6 +6997,7 @@ function assertWorktreeFlags(opts) {
|
|
|
6994
6997
|
return;
|
|
6995
6998
|
}
|
|
6996
6999
|
for (const flag of ["from", "root"]) {
|
|
7000
|
+
if (flag === "root" && opts.host !== void 0) continue;
|
|
6997
7001
|
if (opts[flag] !== void 0) throw new UsageError(say("agent.spawn_needs_worktree", flag));
|
|
6998
7002
|
}
|
|
6999
7003
|
}
|
|
@@ -7847,10 +7851,10 @@ function wireAgentCommands(program2) {
|
|
|
7847
7851
|
"run in its own git worktree (git worktree add --detach under the state dir); its path is the cwd"
|
|
7848
7852
|
).option("--from <ref>", "with --worktree: start at this commit or branch (default: HEAD)").option(
|
|
7849
7853
|
"--root <repo>",
|
|
7850
|
-
"with --worktree: the repo to add it to (default: the git root of your cwd)"
|
|
7854
|
+
"with --worktree: the repo to add it to (default: the git root of your cwd). With --host: a REMOTE repo (default the profile's --repo), also without --worktree, as the start directory"
|
|
7851
7855
|
).option(
|
|
7852
7856
|
"--host <profile>",
|
|
7853
|
-
"run on this host profile (mu host add): the pane runs its --shell template, which starts the remote pi and forwards its control socket here; --cwd and --worktree name REMOTE paths (the worktree is made by its --exec, under ~/.local/state/mu-next/remote-worktrees/<ws>/<name>); the pane dying is the agent dying. tmux only
|
|
7857
|
+
"run on this host profile (mu host add): the pane runs its --shell template, which starts the remote pi and forwards its control socket here; --cwd and --worktree name REMOTE paths (the worktree is made by its --exec, under ~/.local/state/mu-next/remote-worktrees/<ws>/<name>); --root names the remote repo; the pane dying is the agent dying. tmux only"
|
|
7854
7858
|
).option("--no-ctl", "skip the control-socket handshake with the mu pi extension").option(
|
|
7855
7859
|
"--next-free",
|
|
7856
7860
|
"if <name> is taken, take the next free one (worker-1 \u2192 worker-2, review \u2192 review-2); --json's agent.name says which"
|
|
@@ -8037,7 +8041,7 @@ function openDashboardOrHelp(program2) {
|
|
|
8037
8041
|
(syncDb) => withShutdown(syncDb, async (shutdown) => {
|
|
8038
8042
|
const key = "NODE_ENV";
|
|
8039
8043
|
process.env[key] ??= "production";
|
|
8040
|
-
const { runDashboard } = await import("./tui-
|
|
8044
|
+
const { runDashboard } = await import("./tui-FUBQ326D.js");
|
|
8041
8045
|
const first = program2.opts().workstream?.[0];
|
|
8042
8046
|
await runDashboard({ dbPath: defaultDbPath(), syncDb, shutdown, initialWorkstream: first });
|
|
8043
8047
|
}),
|
|
@@ -8176,7 +8180,7 @@ function checkStrayDbFiles() {
|
|
|
8176
8180
|
`Live DB: ${defaultDbPath()}`,
|
|
8177
8181
|
"",
|
|
8178
8182
|
"These are hand-made copies, pre-upgrade saves, and the backups",
|
|
8179
|
-
"`mu db compact` / `mu db forget` write first (mu.db.pre-<verb>-<time>).",
|
|
8183
|
+
"`mu db compact` / `mu db forget` / `mu delegates prune` write first (mu.db.pre-<verb>-<time>).",
|
|
8180
8184
|
"No mu code path reads them and none prunes them. Keep a pre-forget",
|
|
8181
8185
|
"backup until you are sure: it is the only way back from a forget.",
|
|
8182
8186
|
"Otherwise disaster recovery is the ops log, so a copy is a convenience:",
|
|
@@ -9106,7 +9110,7 @@ var LOG_TAIL_BYTES = 2e3;
|
|
|
9106
9110
|
var sha = (s, n) => createHash("sha256").update(s).digest("hex").slice(0, n);
|
|
9107
9111
|
function delegateProcessPaths(db, row) {
|
|
9108
9112
|
const base = dirname3(db.name);
|
|
9109
|
-
const dir =
|
|
9113
|
+
const dir = delegateProcessDir(db.name, row.callId);
|
|
9110
9114
|
return {
|
|
9111
9115
|
dir,
|
|
9112
9116
|
log: join2(dir, "pi.log"),
|
|
@@ -9437,6 +9441,196 @@ ${tail(p.log)}` : void 0
|
|
|
9437
9441
|
}
|
|
9438
9442
|
}
|
|
9439
9443
|
|
|
9444
|
+
// src/delegate-prune.ts
|
|
9445
|
+
import { lstatSync, readdirSync as readdirSync2, rmSync as rmSync2 } from "fs";
|
|
9446
|
+
import { dirname as dirname4, join as join3 } from "path";
|
|
9447
|
+
var PRUNE_DEFAULT_DAYS = 30;
|
|
9448
|
+
function sizeOf3(p) {
|
|
9449
|
+
let st;
|
|
9450
|
+
try {
|
|
9451
|
+
st = lstatSync(p);
|
|
9452
|
+
} catch {
|
|
9453
|
+
return 0;
|
|
9454
|
+
}
|
|
9455
|
+
if (!st.isDirectory()) return st.size;
|
|
9456
|
+
let n = 0;
|
|
9457
|
+
for (const e of readdirSync2(p)) n += sizeOf3(join3(p, e));
|
|
9458
|
+
return n;
|
|
9459
|
+
}
|
|
9460
|
+
var hasFileDir = (db) => db.name !== "" && db.name !== ":memory:";
|
|
9461
|
+
var LOOSE_MIN_AGE_MS = 864e5;
|
|
9462
|
+
function looseSessions(db, cutoff, workstream, now) {
|
|
9463
|
+
const root = join3(dirname4(db.name), "delegate-sessions");
|
|
9464
|
+
const before = Math.min(Date.parse(cutoff), now - LOOSE_MIN_AGE_MS);
|
|
9465
|
+
const out = [];
|
|
9466
|
+
const wss = db.prepare(
|
|
9467
|
+
"SELECT name FROM workstreams WHERE torn_down_at IS NULL AND (? IS NULL OR name = ?)"
|
|
9468
|
+
).all(workstream ?? null, workstream ?? null).map((r) => r.name);
|
|
9469
|
+
for (const ws of wss) {
|
|
9470
|
+
let names;
|
|
9471
|
+
try {
|
|
9472
|
+
names = readdirSync2(join3(root, ws));
|
|
9473
|
+
} catch {
|
|
9474
|
+
continue;
|
|
9475
|
+
}
|
|
9476
|
+
for (const f of names) {
|
|
9477
|
+
const p = join3(root, ws, f);
|
|
9478
|
+
if (!f.endsWith(".jsonl")) continue;
|
|
9479
|
+
try {
|
|
9480
|
+
const st = lstatSync(p);
|
|
9481
|
+
if (st.isFile() && st.mtimeMs < before) out.push(p);
|
|
9482
|
+
} catch {
|
|
9483
|
+
}
|
|
9484
|
+
}
|
|
9485
|
+
}
|
|
9486
|
+
return out.sort();
|
|
9487
|
+
}
|
|
9488
|
+
var callFiles = (db, c) => [
|
|
9489
|
+
delegateSessionDir(db.name, c.workstream, c.task),
|
|
9490
|
+
delegateProcessDir(db.name, c.callId)
|
|
9491
|
+
];
|
|
9492
|
+
var TASK_PUTS_WHERE = `entity IN ('task', 'note') AND op = 'put'
|
|
9493
|
+
AND key >= @k AND key < @k || '$' AND (key = @k OR key >= @k || '#')
|
|
9494
|
+
AND (@own = 1 OR machine_id <> @me)`;
|
|
9495
|
+
var MIN_DATE_MS = -864e13;
|
|
9496
|
+
function planDelegatePrune(db, opts = {}) {
|
|
9497
|
+
const days2 = opts.days ?? PRUNE_DEFAULT_DAYS;
|
|
9498
|
+
const ownHistory = opts.ownHistory ?? !syncEnabled();
|
|
9499
|
+
const cutoff = new Date(
|
|
9500
|
+
Math.max(MIN_DATE_MS, (opts.now ?? Date.now()) - days2 * 864e5)
|
|
9501
|
+
).toISOString();
|
|
9502
|
+
const rows = db.prepare(
|
|
9503
|
+
`SELECT t.id AS id, w.name AS workstream, t.local_id AS task, t.updated_at AS updated_at,
|
|
9504
|
+
d.call_id AS call_id, d.state AS call_state,
|
|
9505
|
+
(SELECT COUNT(*) FROM task_edges e WHERE e.from_task_id = t.id OR e.to_task_id = t.id) AS edges,
|
|
9506
|
+
EXISTS (SELECT 1 FROM tasks jt JOIN workstreams jw ON jw.id = jt.workstream_id
|
|
9507
|
+
JOIN task_auto ja ON ja.task_id = jt.id AND ja.enabled = 1
|
|
9508
|
+
WHERE jw.name = d.record_workstream AND jt.local_id = d.record_task) AS judged_auto
|
|
9509
|
+
FROM tasks t
|
|
9510
|
+
JOIN workstreams w ON w.id = t.workstream_id AND w.torn_down_at IS NULL
|
|
9511
|
+
JOIN delegate_calls d ON d.task_id = t.id
|
|
9512
|
+
WHERE t.kind = 'delegate' AND t.status = 'CLOSED' AND t.updated_at < ?
|
|
9513
|
+
AND (? IS NULL OR w.name = ?)
|
|
9514
|
+
ORDER BY w.name, t.updated_at, t.local_id`
|
|
9515
|
+
).all(cutoff, opts.workstream ?? null, opts.workstream ?? null);
|
|
9516
|
+
const plan = {
|
|
9517
|
+
cutoff,
|
|
9518
|
+
days: days2,
|
|
9519
|
+
candidates: [],
|
|
9520
|
+
kept: { edges: 0, refuteTarget: 0 },
|
|
9521
|
+
ownHistory,
|
|
9522
|
+
ops: 0,
|
|
9523
|
+
bytes: 0,
|
|
9524
|
+
files: { paths: [], bytes: 0 }
|
|
9525
|
+
};
|
|
9526
|
+
const cost = db.prepare(
|
|
9527
|
+
`SELECT COUNT(*) AS n, COALESCE(SUM(length(payload)), 0) AS b FROM ops WHERE ${TASK_PUTS_WHERE}`
|
|
9528
|
+
);
|
|
9529
|
+
const me = localMachineId(db);
|
|
9530
|
+
const own = ownHistory ? 1 : 0;
|
|
9531
|
+
for (const r of rows) {
|
|
9532
|
+
if (!["done", "failed", "cancelled"].includes(r.call_state)) continue;
|
|
9533
|
+
if (r.edges > 0) {
|
|
9534
|
+
plan.kept.edges++;
|
|
9535
|
+
continue;
|
|
9536
|
+
}
|
|
9537
|
+
if (r.call_id.startsWith("refute:") && r.judged_auto === 1) {
|
|
9538
|
+
plan.kept.refuteTarget++;
|
|
9539
|
+
continue;
|
|
9540
|
+
}
|
|
9541
|
+
plan.candidates.push({
|
|
9542
|
+
workstream: r.workstream,
|
|
9543
|
+
task: r.task,
|
|
9544
|
+
callId: r.call_id,
|
|
9545
|
+
closedAt: r.updated_at
|
|
9546
|
+
});
|
|
9547
|
+
const c = cost.get({ k: `${r.workstream}/${r.task}`, own, me });
|
|
9548
|
+
plan.ops += c.n;
|
|
9549
|
+
plan.bytes += c.b;
|
|
9550
|
+
}
|
|
9551
|
+
if (hasFileDir(db)) {
|
|
9552
|
+
const paths = [
|
|
9553
|
+
...plan.candidates.flatMap((c) => callFiles(db, c)).filter(exists2),
|
|
9554
|
+
...looseSessions(db, cutoff, opts.workstream, opts.now ?? Date.now())
|
|
9555
|
+
];
|
|
9556
|
+
plan.files = { paths, bytes: paths.reduce((n, p) => n + sizeOf3(p), 0) };
|
|
9557
|
+
}
|
|
9558
|
+
return plan;
|
|
9559
|
+
}
|
|
9560
|
+
function exists2(p) {
|
|
9561
|
+
try {
|
|
9562
|
+
lstatSync(p);
|
|
9563
|
+
return true;
|
|
9564
|
+
} catch {
|
|
9565
|
+
return false;
|
|
9566
|
+
}
|
|
9567
|
+
}
|
|
9568
|
+
function pruneDelegates(db, plan) {
|
|
9569
|
+
let tasks = 0;
|
|
9570
|
+
let ops = 0;
|
|
9571
|
+
const pruned = [];
|
|
9572
|
+
withOpContext(
|
|
9573
|
+
db,
|
|
9574
|
+
{ intent: "delegate.prune", actor: "mu delegates prune", group: "new" },
|
|
9575
|
+
() => db.transaction(() => {
|
|
9576
|
+
const del = db.prepare(
|
|
9577
|
+
`DELETE FROM tasks WHERE kind = 'delegate' AND status = 'CLOSED' AND updated_at < ?
|
|
9578
|
+
AND local_id = ? AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)
|
|
9579
|
+
AND NOT EXISTS (SELECT 1 FROM task_edges e
|
|
9580
|
+
WHERE e.from_task_id = tasks.id OR e.to_task_id = tasks.id)`
|
|
9581
|
+
);
|
|
9582
|
+
const puts = db.prepare(`DELETE FROM ops WHERE ${TASK_PUTS_WHERE}`);
|
|
9583
|
+
const me = localMachineId(db);
|
|
9584
|
+
const own = plan.ownHistory ? 1 : 0;
|
|
9585
|
+
const cursor = db.prepare("DELETE FROM wait_cursors WHERE ref = ?");
|
|
9586
|
+
for (const c of plan.candidates) {
|
|
9587
|
+
if (del.run(plan.cutoff, c.task, c.workstream).changes === 0) continue;
|
|
9588
|
+
tasks++;
|
|
9589
|
+
pruned.push(c);
|
|
9590
|
+
ops += puts.run({ k: `${c.workstream}/${c.task}`, own, me }).changes;
|
|
9591
|
+
cursor.run(`delegate:${c.workstream}/${c.callId}`);
|
|
9592
|
+
}
|
|
9593
|
+
}).immediate()
|
|
9594
|
+
);
|
|
9595
|
+
let files = 0;
|
|
9596
|
+
if (hasFileDir(db)) {
|
|
9597
|
+
const loose = new Set(plan.files.paths);
|
|
9598
|
+
const go = [
|
|
9599
|
+
...pruned.flatMap((c) => callFiles(db, c)),
|
|
9600
|
+
...looseSessions(db, plan.cutoff, void 0, Date.now()).filter((p) => loose.has(p))
|
|
9601
|
+
];
|
|
9602
|
+
for (const p of go) {
|
|
9603
|
+
if (!exists2(p)) continue;
|
|
9604
|
+
try {
|
|
9605
|
+
rmSync2(p, { recursive: true, force: true });
|
|
9606
|
+
files++;
|
|
9607
|
+
} catch {
|
|
9608
|
+
}
|
|
9609
|
+
}
|
|
9610
|
+
}
|
|
9611
|
+
return { tasks, ops, files };
|
|
9612
|
+
}
|
|
9613
|
+
function checkOldDelegates(db, opts = {}) {
|
|
9614
|
+
const plan = planDelegatePrune(db, opts);
|
|
9615
|
+
const n = plan.candidates.length;
|
|
9616
|
+
if (n === 0)
|
|
9617
|
+
return {
|
|
9618
|
+
name: "delegates-old",
|
|
9619
|
+
severity: "ok",
|
|
9620
|
+
detail: `no closed delegate task older than ${plan.days} days`
|
|
9621
|
+
};
|
|
9622
|
+
return {
|
|
9623
|
+
name: "delegates-old",
|
|
9624
|
+
severity: "ok",
|
|
9625
|
+
detail: `${n} closed delegate task(s) older than ${plan.days} days (${plan.ops} ops)`,
|
|
9626
|
+
remediation: [
|
|
9627
|
+
"Each holds its whole brief and report, synced to every peer. Their verdicts stay on the judged tasks.",
|
|
9628
|
+
" mu delegates prune # dry run: what goes, and what is kept",
|
|
9629
|
+
" mu delegates prune --yes # backs up beside the DB first; irreversible"
|
|
9630
|
+
]
|
|
9631
|
+
};
|
|
9632
|
+
}
|
|
9633
|
+
|
|
9440
9634
|
// src/workstream-scope.ts
|
|
9441
9635
|
function workstreamNameProblem(value, flag = "-w/--workstream") {
|
|
9442
9636
|
if (value.length > 0 && value.trim().length === 0) {
|
|
@@ -9648,6 +9842,82 @@ async function cmdDelegatesRun(db, callId, opts) {
|
|
|
9648
9842
|
}
|
|
9649
9843
|
if (r.ran && r.state !== "done") throw new CliExitError(1);
|
|
9650
9844
|
}
|
|
9845
|
+
function keptLine(plan) {
|
|
9846
|
+
const parts = [
|
|
9847
|
+
plan.kept.edges > 0 ? `${plan.kept.edges} with edges` : "",
|
|
9848
|
+
plan.kept.refuteTarget > 0 ? `${plan.kept.refuteTarget} refute checks of tasks still under mu task auto` : ""
|
|
9849
|
+
].filter((p) => p !== "");
|
|
9850
|
+
return parts.length > 0 ? `kept: ${parts.join(", ")}` : "";
|
|
9851
|
+
}
|
|
9852
|
+
async function cmdDelegatesPrune(db, opts) {
|
|
9853
|
+
if (opts.workstream !== void 0) await resolveExistingWorkstream(db, opts.workstream);
|
|
9854
|
+
const plan = planDelegatePrune(db, {
|
|
9855
|
+
...opts.olderThan !== void 0 ? { days: opts.olderThan } : {},
|
|
9856
|
+
...opts.workstream !== void 0 ? { workstream: opts.workstream } : {}
|
|
9857
|
+
});
|
|
9858
|
+
const n = plan.candidates.length;
|
|
9859
|
+
const files = plan.files.paths.length;
|
|
9860
|
+
const filesNote = files > 0 ? `, ${files} pi session(s) (${formatBytes(plan.files.bytes)})` : "";
|
|
9861
|
+
if (opts.yes === true && n === 0 && files === 0) {
|
|
9862
|
+
if (opts.json)
|
|
9863
|
+
return emitJson({ dryRun: false, ...plan, pruned: 0, deletedOps: 0, deletedFiles: 0 });
|
|
9864
|
+
console.log(pc.dim(`nothing to prune: no closed delegate task older than ${plan.days} days`));
|
|
9865
|
+
const kept2 = keptLine(plan);
|
|
9866
|
+
if (kept2) console.log(pc.dim(kept2));
|
|
9867
|
+
return;
|
|
9868
|
+
}
|
|
9869
|
+
if (opts.yes !== true) {
|
|
9870
|
+
if (opts.json) return emitJson({ dryRun: true, ...plan });
|
|
9871
|
+
if (n === 0 && files === 0)
|
|
9872
|
+
console.log(pc.dim(`nothing to prune: no closed delegate task older than ${plan.days} days`));
|
|
9873
|
+
else {
|
|
9874
|
+
const t = muTable({ head: ["TASK", "CALL", "CLOSED"] });
|
|
9875
|
+
for (const c of plan.candidates.slice(0, 20))
|
|
9876
|
+
t.push([`${c.workstream}/${c.task}`, c.callId, c.closedAt.slice(0, 10)]);
|
|
9877
|
+
console.log(t.toString());
|
|
9878
|
+
if (n > 20) console.log(pc.dim(`... and ${n - 20} more`));
|
|
9879
|
+
console.log(
|
|
9880
|
+
`${n} closed delegate task(s) older than ${plan.days} days: ${plan.ops} ops (${formatBytes(plan.bytes)})${filesNote}. Dry run; nothing changed (--yes applies; irreversible).`
|
|
9881
|
+
);
|
|
9882
|
+
if (!plan.ownHistory)
|
|
9883
|
+
console.log(
|
|
9884
|
+
pc.dim(
|
|
9885
|
+
"sync is on: the rows go everywhere, but this machine's own history stays (its segment is rebuilt from it)"
|
|
9886
|
+
)
|
|
9887
|
+
);
|
|
9888
|
+
}
|
|
9889
|
+
const kept2 = keptLine(plan);
|
|
9890
|
+
if (kept2) console.log(pc.dim(kept2));
|
|
9891
|
+
return;
|
|
9892
|
+
}
|
|
9893
|
+
if (n === 0) {
|
|
9894
|
+
const r2 = pruneDelegates(db, plan);
|
|
9895
|
+
if (opts.json)
|
|
9896
|
+
return emitJson({ dryRun: false, ...plan, pruned: 0, deletedOps: 0, deletedFiles: r2.files });
|
|
9897
|
+
console.log(`Pruned ${r2.files} pi session(s); no delegate task was old enough.`);
|
|
9898
|
+
return;
|
|
9899
|
+
}
|
|
9900
|
+
const backup = backupBeside(db, "prune");
|
|
9901
|
+
const r = pruneDelegates(db, plan);
|
|
9902
|
+
const size = vacuumAndCheck(db, backup);
|
|
9903
|
+
if (opts.json)
|
|
9904
|
+
return emitJson({
|
|
9905
|
+
dryRun: false,
|
|
9906
|
+
...plan,
|
|
9907
|
+
pruned: r.tasks,
|
|
9908
|
+
deletedOps: r.ops,
|
|
9909
|
+
deletedFiles: r.files,
|
|
9910
|
+
backup,
|
|
9911
|
+
...size
|
|
9912
|
+
});
|
|
9913
|
+
console.log(
|
|
9914
|
+
`Pruned ${r.tasks} delegate task(s), ${r.ops} ops${r.files > 0 ? `, ${r.files} pi session(s)` : ""}: ${formatBytes(size.before)} \u2192 ${formatBytes(size.after)}. Drift check clean.`
|
|
9915
|
+
);
|
|
9916
|
+
const kept = keptLine(plan);
|
|
9917
|
+
if (kept) console.log(pc.dim(kept));
|
|
9918
|
+
if (!size.checkpointed) console.log(pc.dim(CHECKPOINT_PENDING));
|
|
9919
|
+
console.log(pc.dim(`backup (the only way back): ${backup}`));
|
|
9920
|
+
}
|
|
9651
9921
|
async function cmdDelegatesReissue(_db, callId, _opts) {
|
|
9652
9922
|
throw new UsageError(
|
|
9653
9923
|
`reissue runs inside the pi session that owns the call: use ${reissueHint(callId)} there (a shell would leave it queued with nothing to start it)`,
|
|
@@ -9699,6 +9969,12 @@ function wireDelegatesCommands(program2) {
|
|
|
9699
9969
|
const opts = this.optsWithGlobals();
|
|
9700
9970
|
return handle((db) => cmdDelegatesRun(db, callId, opts), this)();
|
|
9701
9971
|
});
|
|
9972
|
+
d.command("prune").description(
|
|
9973
|
+
`Delete this machine's closed delegate tasks older than --older-than days (default ${PRUNE_DEFAULT_DAYS}) and their pi session files; the deletion syncs. Dry run without --yes; --yes backs up, VACUUMs, checks drift`
|
|
9974
|
+
).option("--older-than <days>", "age in days since the task closed", parsePositiveNumber).option("--yes", "apply (default: dry run)").option("-w, --workstream <name>", "only this workstream (default: every live one)").option(...JSON_OPT).action(function() {
|
|
9975
|
+
const opts = this.optsWithGlobals();
|
|
9976
|
+
return handle((db) => cmdDelegatesPrune(db, opts), this)();
|
|
9977
|
+
});
|
|
9702
9978
|
d.command("reissue <call-id>").description(
|
|
9703
9979
|
"Refuses: reissue a refused call with the `mu_delegate_reissue` tool in its pi session (only that runtime can start or drain it)"
|
|
9704
9980
|
).option(...JSON_OPT).action(function(callId) {
|
|
@@ -9708,15 +9984,15 @@ function wireDelegatesCommands(program2) {
|
|
|
9708
9984
|
}
|
|
9709
9985
|
|
|
9710
9986
|
// src/cli/doctor.ts
|
|
9711
|
-
import { dirname as
|
|
9987
|
+
import { dirname as dirname6 } from "path";
|
|
9712
9988
|
|
|
9713
9989
|
// src/doctor-summary.ts
|
|
9714
9990
|
import { accessSync, constants, existsSync as existsSync3, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
9715
|
-
import { delimiter, dirname as
|
|
9991
|
+
import { delimiter, dirname as dirname5, join as join4, parse } from "path";
|
|
9716
9992
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9717
9993
|
function resolveExecutable(name) {
|
|
9718
9994
|
for (const dir of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
|
|
9719
|
-
const candidate =
|
|
9995
|
+
const candidate = join4(dir, name);
|
|
9720
9996
|
try {
|
|
9721
9997
|
accessSync(candidate, constants.X_OK);
|
|
9722
9998
|
return realpathSync(candidate);
|
|
@@ -9726,10 +10002,10 @@ function resolveExecutable(name) {
|
|
|
9726
10002
|
return null;
|
|
9727
10003
|
}
|
|
9728
10004
|
function murmurVersion(executable) {
|
|
9729
|
-
let dir =
|
|
10005
|
+
let dir = dirname5(executable);
|
|
9730
10006
|
const root = parse(dir).root;
|
|
9731
10007
|
while (dir !== root) {
|
|
9732
|
-
const packagePath =
|
|
10008
|
+
const packagePath = join4(dir, "package.json");
|
|
9733
10009
|
if (existsSync3(packagePath)) {
|
|
9734
10010
|
try {
|
|
9735
10011
|
const pkg = JSON.parse(readFileSync2(packagePath, "utf8"));
|
|
@@ -9739,7 +10015,7 @@ function murmurVersion(executable) {
|
|
|
9739
10015
|
} catch {
|
|
9740
10016
|
}
|
|
9741
10017
|
}
|
|
9742
|
-
dir =
|
|
10018
|
+
dir = dirname5(dir);
|
|
9743
10019
|
}
|
|
9744
10020
|
return null;
|
|
9745
10021
|
}
|
|
@@ -9811,7 +10087,7 @@ function ctlExtensionDoctorCheck(reports) {
|
|
|
9811
10087
|
var DOCTOR_CTL_TIMEOUT_MS = 500;
|
|
9812
10088
|
function installedMuVersion() {
|
|
9813
10089
|
try {
|
|
9814
|
-
const pkgPath =
|
|
10090
|
+
const pkgPath = join4(dirname5(fileURLToPath2(import.meta.url)), "..", "package.json");
|
|
9815
10091
|
const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
|
|
9816
10092
|
const v = typeof pkg === "object" && pkg !== null ? pkg.version : null;
|
|
9817
10093
|
return typeof v === "string" ? v : null;
|
|
@@ -10055,7 +10331,7 @@ async function cmdDoctor(db, opts = {}) {
|
|
|
10055
10331
|
console.log(` ${pad(fact.name)}: ${fact.value ? pc.green(fact.value) : pc.dim("not set")}`);
|
|
10056
10332
|
}
|
|
10057
10333
|
}
|
|
10058
|
-
const ctlSockets = await ctlSocketsDoctorCheck(listAgents(db), { stateDir:
|
|
10334
|
+
const ctlSockets = await ctlSocketsDoctorCheck(listAgents(db), { stateDir: dirname6(db.name) });
|
|
10059
10335
|
const muExt = ctlExtensionDoctorCheck(ctlSockets.agents);
|
|
10060
10336
|
printCheck("agent state", agentStateCheck(health, extensionLive(ctlSockets.agents)));
|
|
10061
10337
|
printCheck("mu ext", muExt);
|
|
@@ -10199,6 +10475,14 @@ state (workstream=${ws})`));
|
|
|
10199
10475
|
console.log("");
|
|
10200
10476
|
for (const line2 of dormant.remediation) console.log(` ${line2}`);
|
|
10201
10477
|
}
|
|
10478
|
+
const oldDelegates = checkOldDelegates(db);
|
|
10479
|
+
console.log(
|
|
10480
|
+
` ${oldDelegates.name.padEnd(16)} : ${oldDelegates.remediation === void 0 ? pc.green("ok") : pc.yellow("note")} ${pc.dim(oldDelegates.detail)}`
|
|
10481
|
+
);
|
|
10482
|
+
if (oldDelegates.remediation !== void 0) {
|
|
10483
|
+
console.log("");
|
|
10484
|
+
for (const line2 of oldDelegates.remediation) console.log(` ${line2}`);
|
|
10485
|
+
}
|
|
10202
10486
|
console.log(pc.bold("\ndisk"));
|
|
10203
10487
|
const disk = checkDiskRecon(db);
|
|
10204
10488
|
if (printHazards(disk)) sawHazard = true;
|
|
@@ -10301,7 +10585,7 @@ function opsSizeHint(db) {
|
|
|
10301
10585
|
}
|
|
10302
10586
|
async function cmdDoctorJson(db, opts = {}) {
|
|
10303
10587
|
const health = await muxHealth();
|
|
10304
|
-
const ctlSockets = await ctlSocketsDoctorCheck(listAgents(db), { stateDir:
|
|
10588
|
+
const ctlSockets = await ctlSocketsDoctorCheck(listAgents(db), { stateDir: dirname6(db.name) });
|
|
10305
10589
|
const muExt = ctlExtensionDoctorCheck(ctlSockets.agents);
|
|
10306
10590
|
const env = {
|
|
10307
10591
|
// `mux` is the backend-agnostic key. `tmux` is kept as an alias for
|
|
@@ -10410,7 +10694,16 @@ async function cmdDoctorJson(db, opts = {}) {
|
|
|
10410
10694
|
remediation: dormantHazard.remediation ?? [],
|
|
10411
10695
|
workstreams: findDormantWorkstreams(db, { currentWorkstream }),
|
|
10412
10696
|
thresholds: { finishedIdleDays: FINISHED_IDLE_DAYS, abandonedIdleDays: ABANDONED_IDLE_DAYS }
|
|
10413
|
-
}
|
|
10697
|
+
},
|
|
10698
|
+
oldDelegates: (() => {
|
|
10699
|
+
const h = checkOldDelegates(db);
|
|
10700
|
+
return {
|
|
10701
|
+
name: h.name,
|
|
10702
|
+
severity: h.severity,
|
|
10703
|
+
detail: h.detail,
|
|
10704
|
+
remediation: h.remediation ?? []
|
|
10705
|
+
};
|
|
10706
|
+
})()
|
|
10414
10707
|
};
|
|
10415
10708
|
const worktreeUsage = opts.disk === true ? measureWorktreeUsage(db) : null;
|
|
10416
10709
|
let drift;
|
|
@@ -10514,7 +10807,7 @@ function cmdHostAdd(db, name, opts) {
|
|
|
10514
10807
|
shell: opts.shell,
|
|
10515
10808
|
exec: opts.exec,
|
|
10516
10809
|
fetch: opts.fetch,
|
|
10517
|
-
repo: opts.repo,
|
|
10810
|
+
repo: opts.repo ?? null,
|
|
10518
10811
|
max_sessions: opts.maxSessions ?? null
|
|
10519
10812
|
});
|
|
10520
10813
|
if (isJsonMode()) {
|
|
@@ -10535,9 +10828,9 @@ function cmdHostList(db) {
|
|
|
10535
10828
|
}
|
|
10536
10829
|
const held = /* @__PURE__ */ new Map();
|
|
10537
10830
|
for (const c of listHostChannels(db)) held.set(c.host, (held.get(c.host) ?? 0) + 1);
|
|
10538
|
-
const t = muTable({ head: ["HOST", "REPO", "CHANNELS"] });
|
|
10831
|
+
const t = muTable({ head: ["HOST", "DEFAULT REPO", "CHANNELS"] });
|
|
10539
10832
|
for (const h of hosts) {
|
|
10540
|
-
t.push([h.name, h.repo, `${held.get(h.name) ?? 0}/${h.max_sessions ?? "\u221E"}`]);
|
|
10833
|
+
t.push([h.name, h.repo ?? "-", `${held.get(h.name) ?? 0}/${h.max_sessions ?? "\u221E"}`]);
|
|
10541
10834
|
}
|
|
10542
10835
|
console.log(t.toString());
|
|
10543
10836
|
}
|
|
@@ -10549,7 +10842,7 @@ function cmdHostShow(db, name) {
|
|
|
10549
10842
|
return;
|
|
10550
10843
|
}
|
|
10551
10844
|
console.log(
|
|
10552
|
-
`${pc.bold(host.name)} repo ${host.repo} max-sessions ${host.max_sessions ?? "none"}`
|
|
10845
|
+
`${pc.bold(host.name)} default repo ${host.repo ?? "-"} max-sessions ${host.max_sessions ?? "none"}`
|
|
10553
10846
|
);
|
|
10554
10847
|
for (const f of ["shell", "exec", "fetch"]) console.log(` ${f.padEnd(6)} ${host[f]}`);
|
|
10555
10848
|
console.log(
|
|
@@ -10580,7 +10873,6 @@ function cmdHostInit(name, opts) {
|
|
|
10580
10873
|
`--shell ${shellQuote(s.shell)}`,
|
|
10581
10874
|
`--exec ${shellQuote(s.exec)}`,
|
|
10582
10875
|
`--fetch ${shellQuote(s.fetch)}`,
|
|
10583
|
-
"--repo '<remote repo path>'",
|
|
10584
10876
|
"--max-sessions 1"
|
|
10585
10877
|
].join(" \\\n ");
|
|
10586
10878
|
if (isJsonMode()) {
|
|
@@ -10593,7 +10885,10 @@ function cmdHostInit(name, opts) {
|
|
|
10593
10885
|
}
|
|
10594
10886
|
async function cmdHostCheck(db, name, opts) {
|
|
10595
10887
|
const agent = opts.agent === void 0 ? void 0 : { name: opts.agent, workstream: await resolveWorkstream(opts.workstream) };
|
|
10596
|
-
const report = await checkHost(db, name,
|
|
10888
|
+
const report = await checkHost(db, name, {
|
|
10889
|
+
...agent !== void 0 ? { agent } : {},
|
|
10890
|
+
...opts.root !== void 0 ? { root: opts.root } : {}
|
|
10891
|
+
});
|
|
10597
10892
|
if (isJsonMode()) {
|
|
10598
10893
|
if (!report.ok) throw failureWithData(failedError(report), report);
|
|
10599
10894
|
emitJson(report);
|
|
@@ -10617,7 +10912,10 @@ function wireHostCommands(program2) {
|
|
|
10617
10912
|
);
|
|
10618
10913
|
host.command("add <name>").description(
|
|
10619
10914
|
"Store a machine-local host profile of command templates: --shell opens the pane, --exec runs one remote command, --fetch brings commits home. Placeholders {env} {cmd} {local_sock} {remote_sock} {cwd}; grammar in docs/guide/remote.md."
|
|
10620
|
-
).requiredOption("--shell <template>", "pane command template (opens the remote shell)").requiredOption("--exec <template>", "template that runs one remote command ({cmd})").requiredOption("--fetch <template>", "local template that fetches commits home ({cwd})").
|
|
10915
|
+
).requiredOption("--shell <template>", "pane command template (opens the remote shell)").requiredOption("--exec <template>", "template that runs one remote command ({cmd})").requiredOption("--fetch <template>", "local template that fetches commits home ({cwd})").option(
|
|
10916
|
+
"--repo <path>",
|
|
10917
|
+
"default remote repo for `agent spawn --host` without --root (a profile names a machine, not a repo)"
|
|
10918
|
+
).option(
|
|
10621
10919
|
"--max-sessions <n>",
|
|
10622
10920
|
"cap on channels mu holds to this host at once (panes, --exec, --fetch)",
|
|
10623
10921
|
parseNonNegativeInt
|
|
@@ -10641,8 +10939,8 @@ function wireHostCommands(program2) {
|
|
|
10641
10939
|
return handle(async () => cmdHostInit(name, opts), this)();
|
|
10642
10940
|
});
|
|
10643
10941
|
host.command("check <name>").description(
|
|
10644
|
-
"Run the profile's --exec under one channel: host answers, repo is git, pi runs, `pi list` names mu. --agent also probes that agent's
|
|
10645
|
-
).option("--agent <name>", "also probe this agent's control socket (the forward rides its pane)").option(...WORKSTREAM_OPT).option(...JSON_OPT).action(function(name) {
|
|
10942
|
+
"Run the profile's --exec under one channel: host answers, the repo (--root, else the default) is git, pi runs, `pi list` names mu. --agent also probes that agent's control socket. Exit 5 if a step fails; retries nothing."
|
|
10943
|
+
).option("--agent <name>", "also probe this agent's control socket (the forward rides its pane)").option("--root <path>", "the remote repo the repo step checks (default: the profile's)").option(...WORKSTREAM_OPT).option(...JSON_OPT).action(function(name) {
|
|
10646
10944
|
const opts = this.optsWithGlobals();
|
|
10647
10945
|
return handle((db) => cmdHostCheck(db, name, opts), this)();
|
|
10648
10946
|
});
|
|
@@ -11994,8 +12292,8 @@ function emitJsonCollection(items) {
|
|
|
11994
12292
|
}
|
|
11995
12293
|
function readPackageVersion() {
|
|
11996
12294
|
try {
|
|
11997
|
-
const here =
|
|
11998
|
-
const pkgPath =
|
|
12295
|
+
const here = dirname7(fileURLToPath3(import.meta.url));
|
|
12296
|
+
const pkgPath = join5(here, "..", "package.json");
|
|
11999
12297
|
const raw = readFileSync3(pkgPath, "utf8");
|
|
12000
12298
|
const parsed = JSON.parse(raw);
|
|
12001
12299
|
return typeof parsed.version === "string" ? parsed.version : "unknown";
|
|
@@ -12170,4 +12468,4 @@ export {
|
|
|
12170
12468
|
truncate,
|
|
12171
12469
|
truncateFront
|
|
12172
12470
|
};
|
|
12173
|
-
//# sourceMappingURL=cli-
|
|
12471
|
+
//# sourceMappingURL=cli-TV3N47AZ.js.map
|