@mutmutco/hub 4.6.2 → 4.6.4
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/index.cjs +94 -17
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
buildWatcherCommand: () => buildWatcherCommand,
|
|
27
27
|
checkAtomicity: () => checkAtomicity,
|
|
28
28
|
claudeArm: () => claudeArm,
|
|
29
|
+
claudeCatalogMmiVersion: () => claudeCatalogMmiVersion,
|
|
29
30
|
clearLaunchTarget: () => clearLaunchTarget,
|
|
30
31
|
clearPending: () => clearPending,
|
|
31
32
|
codexArm: () => codexArm,
|
|
@@ -79,6 +80,7 @@ __export(index_exports, {
|
|
|
79
80
|
sharedDocLockTimeoutReceipt: () => sharedDocLockTimeoutReceipt,
|
|
80
81
|
shippedTargetFrom: () => shippedTargetFrom,
|
|
81
82
|
shouldHandOff: () => shouldHandOff,
|
|
83
|
+
stagingPrunePreview: () => stagingPrunePreview,
|
|
82
84
|
statusScreen: () => statusScreen,
|
|
83
85
|
surfaceArmPlan: () => surfaceArmPlan,
|
|
84
86
|
systemdServiceUnit: () => systemdServiceUnit,
|
|
@@ -2510,6 +2512,9 @@ function claudeHomeDir(env) {
|
|
|
2510
2512
|
function claudeMarketplacesPath(env) {
|
|
2511
2513
|
return env.MMI_UPDATER_CLAUDE_MARKETPLACES || (0, import_node_path10.join)(claudeHomeDir(env), "plugins", "known_marketplaces.json");
|
|
2512
2514
|
}
|
|
2515
|
+
function claudeLaunchTargetPath(env) {
|
|
2516
|
+
return (0, import_node_path10.join)(claudeHomeDir(env), "plugins", "staging", "launch-target.json");
|
|
2517
|
+
}
|
|
2513
2518
|
function ensureClaudeMarketplace(env) {
|
|
2514
2519
|
const path = claudeMarketplacesPath(env);
|
|
2515
2520
|
let body;
|
|
@@ -2555,7 +2560,30 @@ function claudeNpmEnv(env) {
|
|
|
2555
2560
|
return { ...env, npm_config_allow_remote: "all" };
|
|
2556
2561
|
}
|
|
2557
2562
|
function probeClaudeLaunchStaging(env = process.env) {
|
|
2558
|
-
return probeLaunchTargetVersion((
|
|
2563
|
+
return probeLaunchTargetVersion(claudeLaunchTargetPath(env), claudeMarkerVersion);
|
|
2564
|
+
}
|
|
2565
|
+
function catalogVersionAt(path, pick) {
|
|
2566
|
+
try {
|
|
2567
|
+
const version = pick(JSON.parse((0, import_node_fs14.readFileSync)(path, "utf8")));
|
|
2568
|
+
return typeof version === "string" && parseSemver(version) ? version : null;
|
|
2569
|
+
} catch {
|
|
2570
|
+
return null;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
function claudeCatalogMmiVersion(env) {
|
|
2574
|
+
let clone = null;
|
|
2575
|
+
try {
|
|
2576
|
+
const location = JSON.parse((0, import_node_fs14.readFileSync)(claudeMarketplacesPath(env), "utf8"))?.mutmutco?.installLocation;
|
|
2577
|
+
clone = typeof location === "string" && location.trim() ? location : null;
|
|
2578
|
+
} catch {
|
|
2579
|
+
clone = null;
|
|
2580
|
+
}
|
|
2581
|
+
const root = clone ?? (0, import_node_path10.join)(claudeHomeDir(env), "plugins", "marketplaces", "mutmutco");
|
|
2582
|
+
const fromCatalog = catalogVersionAt(
|
|
2583
|
+
(0, import_node_path10.join)(root, ".claude-plugin", "marketplace.json"),
|
|
2584
|
+
(body) => Array.isArray(body?.plugins) ? body.plugins.find((row) => row?.name === "mmi")?.version : null
|
|
2585
|
+
);
|
|
2586
|
+
return fromCatalog ?? catalogVersionAt((0, import_node_path10.join)(root, ".claude-plugin", "plugin.json"), (body) => body?.version);
|
|
2559
2587
|
}
|
|
2560
2588
|
function claudeArm(options) {
|
|
2561
2589
|
const env = options.env ?? process.env;
|
|
@@ -2588,7 +2616,7 @@ function claudeArm(options) {
|
|
|
2588
2616
|
}
|
|
2589
2617
|
if (!marketplaceTrust.ok) return result(from, target, "defer", marketplaceTrust.detail ?? "cannot trust the mutmutco marketplace registration");
|
|
2590
2618
|
const pluginsRoot = claudePluginsRoot(env);
|
|
2591
|
-
const launchTargetPath = (
|
|
2619
|
+
const launchTargetPath = claudeLaunchTargetPath(env);
|
|
2592
2620
|
if (options.dryRun) {
|
|
2593
2621
|
return result(from, target, "ok", `dry-run: would refresh mutmutco, stage ${target} on the Claude volume, and converge mmi@mutmutco ${from ?? "absent"} -> ${target}`);
|
|
2594
2622
|
}
|
|
@@ -2602,6 +2630,10 @@ function claudeArm(options) {
|
|
|
2602
2630
|
clearLaunchTarget(launchTargetPath);
|
|
2603
2631
|
return result(from, target, "ok", `catalog refreshed; installed+enabled at target ${target}`);
|
|
2604
2632
|
}
|
|
2633
|
+
const catalogVersion = claudeCatalogMmiVersion(env);
|
|
2634
|
+
if (catalogVersion && compareSemver(catalogVersion, target) < 0) {
|
|
2635
|
+
return result(from, target, "defer", `catalog not yet at target (catalog ${catalogVersion} < target ${target}; release-align pending)`);
|
|
2636
|
+
}
|
|
2605
2637
|
const incomingRoot = (0, import_node_path10.join)(pluginsRoot, "staging", "incoming");
|
|
2606
2638
|
const incoming = (0, import_node_path10.join)(incomingRoot, target);
|
|
2607
2639
|
const priorLaunch = readLaunchTarget(launchTargetPath);
|
|
@@ -3323,28 +3355,65 @@ function generationSets(root) {
|
|
|
3323
3355
|
if (children.some((name) => parseSemver(name))) return [root];
|
|
3324
3356
|
return children.map((name) => (0, import_node_path14.join)(root, name)).filter((path) => listDirs(path).some((name) => parseSemver(name)));
|
|
3325
3357
|
}
|
|
3326
|
-
function pruneGenerations(root, category, dryRun) {
|
|
3327
|
-
|
|
3358
|
+
function pruneGenerations(root, category, dryRun, options = {}) {
|
|
3359
|
+
const protectedPaths = options.protectedPaths ?? /* @__PURE__ */ new Set();
|
|
3360
|
+
const isProtected = (path) => {
|
|
3361
|
+
const dir = path.replaceAll("\\", "/");
|
|
3362
|
+
for (const ref of protectedPaths) {
|
|
3363
|
+
if (ref === dir || ref.startsWith(`${dir}/`)) return true;
|
|
3364
|
+
}
|
|
3365
|
+
return false;
|
|
3366
|
+
};
|
|
3367
|
+
const prunablePaths = (set, versions, incoming) => {
|
|
3368
|
+
const tree = incoming && parseSemver(incoming) && !versions.includes(incoming) ? [...versions, incoming] : versions;
|
|
3369
|
+
return [...tree].sort((a, b) => compareSemver(b, a)).slice(KEEP_GENERATIONS).map((version) => (0, import_node_path14.join)(set, version)).filter((path) => !isProtected(path));
|
|
3370
|
+
};
|
|
3371
|
+
const relName = (path) => (0, import_node_path14.relative)(root, path).replaceAll("\\", "/");
|
|
3372
|
+
const collect = (incoming) => {
|
|
3373
|
+
const candidates = [];
|
|
3374
|
+
for (const set of generationSets(root)) {
|
|
3375
|
+
candidates.push(...prunablePaths(set, listDirs(set).filter((name) => parseSemver(name)), incoming));
|
|
3376
|
+
}
|
|
3377
|
+
return candidates;
|
|
3378
|
+
};
|
|
3328
3379
|
const failures = [];
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
for (const
|
|
3332
|
-
if (dryRun) {
|
|
3333
|
-
reaped += 1;
|
|
3334
|
-
continue;
|
|
3335
|
-
}
|
|
3380
|
+
const removed = [];
|
|
3381
|
+
if (!dryRun) {
|
|
3382
|
+
for (const path of collect()) {
|
|
3336
3383
|
try {
|
|
3337
|
-
(0, import_node_fs20.rmSync)(
|
|
3338
|
-
|
|
3384
|
+
(0, import_node_fs20.rmSync)(path, { recursive: true, force: true });
|
|
3385
|
+
removed.push(path);
|
|
3339
3386
|
} catch (error) {
|
|
3340
|
-
failures.push(`${
|
|
3387
|
+
failures.push(`${(0, import_node_path14.basename)(path)}: ${error?.code ?? error?.message ?? error}`);
|
|
3341
3388
|
}
|
|
3342
3389
|
}
|
|
3343
3390
|
}
|
|
3344
|
-
|
|
3391
|
+
const pruned = dryRun ? collect() : removed;
|
|
3392
|
+
const preview = dryRun && options.incomingTarget ? collect(options.incomingTarget) : [];
|
|
3393
|
+
if (!pruned.length && !failures.length && !preview.length) return { category, reaped: 0, verdict: "skip", detail: `${root}: within keep-${KEEP_GENERATIONS}` };
|
|
3345
3394
|
const verb = dryRun ? "dry-run: would prune" : "pruned";
|
|
3346
3395
|
const stuck = failures.length ? `; ${failures.length} locked (${failures.join(", ")})` : "";
|
|
3347
|
-
|
|
3396
|
+
const segments = [`${verb} ${pruned.length} generation(s) below keep-${KEEP_GENERATIONS} under ${root}${pruned.length ? `: ${pruned.map(relName).join(", ")}` : ""}`];
|
|
3397
|
+
if (preview.length) segments.push(`after staging ${options.incomingTarget}: would prune ${preview.length} generation(s) below keep-${KEEP_GENERATIONS} under ${root}: ${preview.map(relName).join(", ")}`);
|
|
3398
|
+
return { category, reaped: pruned.length, verdict: failures.length ? "defer" : "ok", detail: `${segments.join("; ")}${stuck}` };
|
|
3399
|
+
}
|
|
3400
|
+
function liveGenerationRefs(env, paths) {
|
|
3401
|
+
const refs = /* @__PURE__ */ new Set();
|
|
3402
|
+
const launch = readLaunchTarget(claudeLaunchTargetPath(env));
|
|
3403
|
+
if (launch?.incoming) refs.add(launch.incoming.replaceAll("\\", "/"));
|
|
3404
|
+
for (const pending of readPending(paths)) {
|
|
3405
|
+
for (const dir of pending.dirs) refs.add(dir.replaceAll("\\", "/"));
|
|
3406
|
+
}
|
|
3407
|
+
return refs;
|
|
3408
|
+
}
|
|
3409
|
+
function stagingPrunePreview(options) {
|
|
3410
|
+
const env = options.env ?? process.env;
|
|
3411
|
+
const startedAt = import_node_perf_hooks.performance.now();
|
|
3412
|
+
try {
|
|
3413
|
+
return { ...pruneGenerations(options.paths.staging, "staging-prune-preview", true, { incomingTarget: options.incomingTarget, protectedPaths: liveGenerationRefs(env, options.paths) }), durationMs: elapsedMs(startedAt) };
|
|
3414
|
+
} catch (error) {
|
|
3415
|
+
return { category: "staging-prune-preview", reaped: 0, verdict: "fail", detail: `reap failed: ${error?.message ?? error}`, durationMs: elapsedMs(startedAt) };
|
|
3416
|
+
}
|
|
3348
3417
|
}
|
|
3349
3418
|
var LEASE_STEAL_SCRATCH = /^lease\.lock\.stale-\d+$/;
|
|
3350
3419
|
function atomicWriteScratch(path) {
|
|
@@ -3413,8 +3482,9 @@ function reap(options) {
|
|
|
3413
3482
|
}
|
|
3414
3483
|
};
|
|
3415
3484
|
const atomicWritePaths = [claudeMarketplacesPath(env)];
|
|
3485
|
+
const protectedPaths = liveGenerationRefs(env, paths);
|
|
3416
3486
|
return [
|
|
3417
|
-
guarded("staging-prune", () => pruneGenerations(paths.staging, "staging-prune", dryRun)),
|
|
3487
|
+
guarded("staging-prune", () => pruneGenerations(paths.staging, "staging-prune", dryRun, { protectedPaths })),
|
|
3418
3488
|
guarded("scratch", () => reapScratch({ root: paths.root, atomicWritePaths }, dryRun)),
|
|
3419
3489
|
guarded("cursor-retire", () => retireCursor({ paths, dryRun, env }))
|
|
3420
3490
|
];
|
|
@@ -3533,6 +3603,11 @@ async function reconcile(options) {
|
|
|
3533
3603
|
say(`target v${summary.target} (${summary.gate})`);
|
|
3534
3604
|
phase("resolve", "ok");
|
|
3535
3605
|
options.onTargetResolved?.(summary.target, summary.gate);
|
|
3606
|
+
if (options.dryRun && summary.target) {
|
|
3607
|
+
const preview = stagingPrunePreview({ paths, env, incomingTarget: summary.target });
|
|
3608
|
+
summary.reap.push(preview);
|
|
3609
|
+
journal({ kind: "reap", surface: preview.category, verdict: preview.verdict, count: preview.reaped, detail: preview.detail, durationMs: preview.durationMs });
|
|
3610
|
+
}
|
|
3536
3611
|
phase("check", "running");
|
|
3537
3612
|
say("updater: self-bootstrap");
|
|
3538
3613
|
const selfStartedAt = import_node_perf_hooks2.performance.now();
|
|
@@ -4457,6 +4532,7 @@ if (typeof require !== "undefined" && require.main === module) {
|
|
|
4457
4532
|
buildWatcherCommand,
|
|
4458
4533
|
checkAtomicity,
|
|
4459
4534
|
claudeArm,
|
|
4535
|
+
claudeCatalogMmiVersion,
|
|
4460
4536
|
clearLaunchTarget,
|
|
4461
4537
|
clearPending,
|
|
4462
4538
|
codexArm,
|
|
@@ -4510,6 +4586,7 @@ if (typeof require !== "undefined" && require.main === module) {
|
|
|
4510
4586
|
sharedDocLockTimeoutReceipt,
|
|
4511
4587
|
shippedTargetFrom,
|
|
4512
4588
|
shouldHandOff,
|
|
4589
|
+
stagingPrunePreview,
|
|
4513
4590
|
statusScreen,
|
|
4514
4591
|
surfaceArmPlan,
|
|
4515
4592
|
systemdServiceUnit,
|
package/package.json
CHANGED