@mutmutco/hub 4.3.5 → 4.3.7
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 +207 -276
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -66,7 +66,6 @@ __export(index_exports, {
|
|
|
66
66
|
retryHint: () => retryHint,
|
|
67
67
|
schedulePendingWatcher: () => schedulePendingWatcher,
|
|
68
68
|
schedulerStatus: () => schedulerStatus,
|
|
69
|
-
selfDist: () => selfDist,
|
|
70
69
|
sharedDocLockPath: () => sharedDocLockPath,
|
|
71
70
|
sharedDocLockTimeoutReceipt: () => sharedDocLockTimeoutReceipt,
|
|
72
71
|
shouldHandOff: () => shouldHandOff,
|
|
@@ -83,9 +82,9 @@ __export(index_exports, {
|
|
|
83
82
|
writePending: () => writePending
|
|
84
83
|
});
|
|
85
84
|
module.exports = __toCommonJS(index_exports);
|
|
86
|
-
var
|
|
87
|
-
var
|
|
88
|
-
var
|
|
85
|
+
var import_node_child_process9 = require("node:child_process");
|
|
86
|
+
var import_node_fs18 = require("node:fs");
|
|
87
|
+
var import_node_path13 = require("node:path");
|
|
89
88
|
|
|
90
89
|
// src/state.ts
|
|
91
90
|
var import_node_fs = require("node:fs");
|
|
@@ -1072,106 +1071,17 @@ function enumerateSurfaces(env = process.env) {
|
|
|
1072
1071
|
}));
|
|
1073
1072
|
}
|
|
1074
1073
|
|
|
1075
|
-
// src/arm-cli.ts
|
|
1076
|
-
var import_node_fs9 = require("node:fs");
|
|
1077
|
-
var import_node_child_process6 = require("node:child_process");
|
|
1078
|
-
var import_node_path8 = require("node:path");
|
|
1079
|
-
function globalCliDist(env) {
|
|
1080
|
-
const root = globalRoot(env);
|
|
1081
|
-
return root ? (0, import_node_path8.join)(root, "@mutmutco", "cli", "dist", "index.cjs") : null;
|
|
1082
|
-
}
|
|
1083
|
-
function probeCliVersion(distPath) {
|
|
1084
|
-
if (!fileExists(distPath)) return null;
|
|
1085
|
-
const probe = (0, import_node_child_process6.spawnSync)(process.execPath, [distPath, "--version"], { encoding: "utf8", windowsHide: true });
|
|
1086
|
-
const version = (probe.stdout || "").trim();
|
|
1087
|
-
return probe.status === 0 && /^\d+\.\d+\.\d+/.test(version) ? version : null;
|
|
1088
|
-
}
|
|
1089
|
-
function probeCliInstallation(env = process.env) {
|
|
1090
|
-
const location = globalCliDist(env);
|
|
1091
|
-
const version = location ? probeCliVersion(location) : null;
|
|
1092
|
-
return { version, location, detail: version ? `absolute binary reported ${version}` : "global @mutmutco/cli binary was not readable" };
|
|
1093
|
-
}
|
|
1094
|
-
function cliArm(options) {
|
|
1095
|
-
const env = options.env ?? process.env;
|
|
1096
|
-
const { target, dryRun, paths } = options;
|
|
1097
|
-
const dist = globalCliDist(env);
|
|
1098
|
-
const installed = dist ? probeCliVersion(dist) : null;
|
|
1099
|
-
if (installed && compareSemver(installed, target) > 0) {
|
|
1100
|
-
return { surface: "cli", from: installed, to: installed, verdict: "skip", detail: `installed ${installed} is above candidate ${target} (monotonic)` };
|
|
1101
|
-
}
|
|
1102
|
-
const highWater = journalHighWater(paths.journalPath, "cli");
|
|
1103
|
-
if (highWater && compareSemver(highWater, target) > 0) {
|
|
1104
|
-
return { surface: "cli", from: installed, to: highWater, verdict: "skip", detail: `journal high-water ${highWater} is above candidate ${target} (monotonic)` };
|
|
1105
|
-
}
|
|
1106
|
-
if (installed === target) {
|
|
1107
|
-
return { surface: "cli", from: installed, to: target, verdict: "ok", detail: `already at target ${target}` };
|
|
1108
|
-
}
|
|
1109
|
-
if (dryRun) {
|
|
1110
|
-
return { surface: "cli", from: installed, to: target, verdict: "ok", detail: `dry-run: would converge ${installed ?? "absent"} -> ${target}` };
|
|
1111
|
-
}
|
|
1112
|
-
const stageRoot = (0, import_node_path8.join)(paths.staging, "cli", target);
|
|
1113
|
-
const stageDist = (0, import_node_path8.join)(stageRoot, "node_modules", "@mutmutco", "cli", "dist", "index.cjs");
|
|
1114
|
-
if (!fileExists(stageDist)) {
|
|
1115
|
-
(0, import_node_fs9.mkdirSync)(stageRoot, { recursive: true });
|
|
1116
|
-
(0, import_node_fs9.writeFileSync)((0, import_node_path8.join)(stageRoot, "package.json"), JSON.stringify({ name: "mmi-updater-stage", private: true }) + "\n");
|
|
1117
|
-
const install = runNpm(["install", "--prefix", stageRoot, "--no-audit", "--no-fund", "--no-package-lock", `@mutmutco/cli@${target}`], env);
|
|
1118
|
-
if (install.status !== 0) {
|
|
1119
|
-
return { surface: "cli", from: installed, to: target, verdict: "defer", detail: `staging install failed: ${(install.stderr || install.stdout).split("\n").filter(Boolean).slice(-3).join(" | ")}` };
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
let stagedManifestVersion;
|
|
1123
|
-
try {
|
|
1124
|
-
stagedManifestVersion = JSON.parse((0, import_node_fs9.readFileSync)((0, import_node_path8.join)(stageRoot, "node_modules", "@mutmutco", "cli", "package.json"), "utf8")).version;
|
|
1125
|
-
} catch {
|
|
1126
|
-
}
|
|
1127
|
-
if (stagedManifestVersion !== target) {
|
|
1128
|
-
return { surface: "cli", from: installed, to: target, verdict: "defer", detail: `staged manifest is ${stagedManifestVersion ?? "unreadable"}, expected ${target}` };
|
|
1129
|
-
}
|
|
1130
|
-
const stagedVersion = probeCliVersion(stageDist);
|
|
1131
|
-
if (stagedVersion !== target) {
|
|
1132
|
-
return { surface: "cli", from: installed, to: target, verdict: "defer", detail: `staged binary probed ${stagedVersion ?? "dead"}, expected ${target}` };
|
|
1133
|
-
}
|
|
1134
|
-
const globalInstall = runNpm(["install", "-g", "--no-audit", "--no-fund", `@mutmutco/cli@${target}`], env);
|
|
1135
|
-
if (globalInstall.status !== 0) {
|
|
1136
|
-
const reason = `global install failed: ${(globalInstall.stderr || globalInstall.stdout).split("\n").filter(Boolean).slice(-3).join(" | ")}`;
|
|
1137
|
-
const root = globalRoot(env);
|
|
1138
|
-
const pending = lockShaped(reason) && root ? ` \u2014 ${noteBusyDefer(paths, env, { surface: "cli", target, reason, dirs: [root] })}` : "";
|
|
1139
|
-
return { surface: "cli", from: installed, to: target, verdict: "defer", detail: `${reason}${pending}` };
|
|
1140
|
-
}
|
|
1141
|
-
const switchedDist = globalCliDist(env);
|
|
1142
|
-
const switched = probeCliVersion(switchedDist ?? "");
|
|
1143
|
-
if (switched !== target) {
|
|
1144
|
-
return { surface: "cli", from: installed, to: switched ?? installed ?? "unknown", verdict: "fail", detail: `post-switch probe returned ${switched ?? "dead"} at ${switchedDist ?? "an unresolvable path"}, expected ${target}; the global install already ran \u2014 nothing was rolled back` };
|
|
1145
|
-
}
|
|
1146
|
-
return { surface: "cli", from: installed, to: target, verdict: "ok", detail: `converged ${installed ?? "absent"} -> ${target} (staged, verified, switched)` };
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
// src/arm-self.ts
|
|
1150
|
-
var SELF_PACKAGE = "@mutmutco/hub";
|
|
1151
|
-
var SELF_DIST_REL = "dist/index.cjs";
|
|
1152
|
-
function selfDist(env) {
|
|
1153
|
-
return globalDist(env, SELF_PACKAGE, SELF_DIST_REL);
|
|
1154
|
-
}
|
|
1155
|
-
function selfArm(options) {
|
|
1156
|
-
return npmGlobalArm({
|
|
1157
|
-
surface: "updater",
|
|
1158
|
-
packageName: SELF_PACKAGE,
|
|
1159
|
-
distRel: SELF_DIST_REL,
|
|
1160
|
-
...options
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
1074
|
// src/arm-cursor.ts
|
|
1165
|
-
var
|
|
1166
|
-
var
|
|
1075
|
+
var import_node_fs11 = require("node:fs");
|
|
1076
|
+
var import_node_child_process6 = require("node:child_process");
|
|
1167
1077
|
var import_node_os5 = require("node:os");
|
|
1168
|
-
var
|
|
1078
|
+
var import_node_path8 = require("node:path");
|
|
1169
1079
|
|
|
1170
1080
|
// src/compat.ts
|
|
1171
|
-
var
|
|
1081
|
+
var import_node_fs9 = require("node:fs");
|
|
1172
1082
|
function readManifestCompat(manifestPath) {
|
|
1173
1083
|
try {
|
|
1174
|
-
const parsed = JSON.parse((0,
|
|
1084
|
+
const parsed = JSON.parse((0, import_node_fs9.readFileSync)(manifestPath, "utf8"));
|
|
1175
1085
|
return typeof parsed.mmiCompat === "string" ? parsed.mmiCompat : void 0;
|
|
1176
1086
|
} catch {
|
|
1177
1087
|
return void 0;
|
|
@@ -1179,7 +1089,7 @@ function readManifestCompat(manifestPath) {
|
|
|
1179
1089
|
}
|
|
1180
1090
|
|
|
1181
1091
|
// src/host-doc-lock.ts
|
|
1182
|
-
var
|
|
1092
|
+
var import_node_fs10 = require("node:fs");
|
|
1183
1093
|
var import_node_os4 = require("node:os");
|
|
1184
1094
|
var SHARED_DOC_LOCK_STALE_MS = 3e4;
|
|
1185
1095
|
var SHARED_DOC_LOCK_ATTEMPTS = 50;
|
|
@@ -1193,7 +1103,7 @@ function lockContent(options) {
|
|
|
1193
1103
|
}
|
|
1194
1104
|
function readHolder(lockPath) {
|
|
1195
1105
|
try {
|
|
1196
|
-
const parsed = JSON.parse((0,
|
|
1106
|
+
const parsed = JSON.parse((0, import_node_fs10.readFileSync)(lockPath, "utf8"));
|
|
1197
1107
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
1198
1108
|
return parsed;
|
|
1199
1109
|
} catch {
|
|
@@ -1205,7 +1115,7 @@ function lockAgeMs(lockPath, now) {
|
|
|
1205
1115
|
const declared = typeof holder?.acquiredAt === "string" ? Date.parse(holder.acquiredAt) : Number.NaN;
|
|
1206
1116
|
if (!Number.isNaN(declared)) return Math.max(0, now - declared);
|
|
1207
1117
|
try {
|
|
1208
|
-
return Math.max(0, now - (0,
|
|
1118
|
+
return Math.max(0, now - (0, import_node_fs10.statSync)(lockPath).mtimeMs);
|
|
1209
1119
|
} catch {
|
|
1210
1120
|
return 0;
|
|
1211
1121
|
}
|
|
@@ -1228,9 +1138,9 @@ function acquireSharedDocLock(documentPath, options) {
|
|
|
1228
1138
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
1229
1139
|
let fd;
|
|
1230
1140
|
try {
|
|
1231
|
-
fd = (0,
|
|
1232
|
-
(0,
|
|
1233
|
-
(0,
|
|
1141
|
+
fd = (0, import_node_fs10.openSync)(lockPath, "wx");
|
|
1142
|
+
(0, import_node_fs10.writeSync)(fd, content);
|
|
1143
|
+
(0, import_node_fs10.closeSync)(fd);
|
|
1234
1144
|
return {
|
|
1235
1145
|
documentPath,
|
|
1236
1146
|
lockPath,
|
|
@@ -1243,11 +1153,11 @@ function acquireSharedDocLock(documentPath, options) {
|
|
|
1243
1153
|
} catch (error) {
|
|
1244
1154
|
if (fd !== void 0) {
|
|
1245
1155
|
try {
|
|
1246
|
-
(0,
|
|
1156
|
+
(0, import_node_fs10.closeSync)(fd);
|
|
1247
1157
|
} catch {
|
|
1248
1158
|
}
|
|
1249
1159
|
try {
|
|
1250
|
-
(0,
|
|
1160
|
+
(0, import_node_fs10.unlinkSync)(lockPath);
|
|
1251
1161
|
} catch {
|
|
1252
1162
|
}
|
|
1253
1163
|
}
|
|
@@ -1256,12 +1166,12 @@ function acquireSharedDocLock(documentPath, options) {
|
|
|
1256
1166
|
if (lockAgeMs(lockPath, Date.now()) >= staleAfterMs) {
|
|
1257
1167
|
const aside = `${lockPath}.stale-${Date.now()}`;
|
|
1258
1168
|
try {
|
|
1259
|
-
(0,
|
|
1169
|
+
(0, import_node_fs10.renameSync)(lockPath, aside);
|
|
1260
1170
|
} catch {
|
|
1261
1171
|
continue;
|
|
1262
1172
|
}
|
|
1263
1173
|
try {
|
|
1264
|
-
(0,
|
|
1174
|
+
(0, import_node_fs10.rmSync)(aside, { force: true });
|
|
1265
1175
|
} catch {
|
|
1266
1176
|
}
|
|
1267
1177
|
continue;
|
|
@@ -1274,7 +1184,7 @@ function releaseSharedDocLock(handle) {
|
|
|
1274
1184
|
try {
|
|
1275
1185
|
const holder = readHolder(handle.lockPath);
|
|
1276
1186
|
if (holder && holder.owner === handle.owner && Number(holder.pid) === handle.pid) {
|
|
1277
|
-
(0,
|
|
1187
|
+
(0, import_node_fs10.unlinkSync)(handle.lockPath);
|
|
1278
1188
|
}
|
|
1279
1189
|
} catch {
|
|
1280
1190
|
}
|
|
@@ -1296,14 +1206,14 @@ var KEEP_GENERATIONS = 2;
|
|
|
1296
1206
|
var STRANDED_TMP_AGE_MS = 24 * 60 * 6e4;
|
|
1297
1207
|
var TREE_FILES = [MANIFEST, "skills/bootstrap/SKILL.md"];
|
|
1298
1208
|
function cursorPluginsRoot() {
|
|
1299
|
-
return (0,
|
|
1209
|
+
return (0, import_node_path8.join)((0, import_node_os5.homedir)(), ".cursor", "plugins");
|
|
1300
1210
|
}
|
|
1301
1211
|
function treeHealthy(root) {
|
|
1302
|
-
return TREE_FILES.every((rel) => (0,
|
|
1212
|
+
return TREE_FILES.every((rel) => (0, import_node_fs11.existsSync)((0, import_node_path8.join)(root, ...rel.split("/"))));
|
|
1303
1213
|
}
|
|
1304
1214
|
function readJson(root, rel) {
|
|
1305
1215
|
try {
|
|
1306
|
-
return JSON.parse((0,
|
|
1216
|
+
return JSON.parse((0, import_node_fs11.readFileSync)((0, import_node_path8.join)(root, ...rel.split("/")), "utf8"));
|
|
1307
1217
|
} catch {
|
|
1308
1218
|
return null;
|
|
1309
1219
|
}
|
|
@@ -1312,7 +1222,7 @@ function manifest(root) {
|
|
|
1312
1222
|
return readJson(root, MANIFEST);
|
|
1313
1223
|
}
|
|
1314
1224
|
function probeCursorInstallation() {
|
|
1315
|
-
const location = (0,
|
|
1225
|
+
const location = (0, import_node_path8.join)(cursorPluginsRoot(), "local", "mmi");
|
|
1316
1226
|
const version = manifest(location)?.version ?? null;
|
|
1317
1227
|
return { version, location, detail: version ? "installed Cursor plugin manifest" : "installed Cursor plugin manifest was unreadable" };
|
|
1318
1228
|
}
|
|
@@ -1327,7 +1237,7 @@ function message(error) {
|
|
|
1327
1237
|
}
|
|
1328
1238
|
function discard(path) {
|
|
1329
1239
|
try {
|
|
1330
|
-
(0,
|
|
1240
|
+
(0, import_node_fs11.rmSync)(path, { recursive: true, force: true });
|
|
1331
1241
|
} catch {
|
|
1332
1242
|
}
|
|
1333
1243
|
}
|
|
@@ -1346,7 +1256,7 @@ function cursorHostEvidence(env) {
|
|
|
1346
1256
|
if (env[marker]?.trim()) return `env marker ${marker}`;
|
|
1347
1257
|
}
|
|
1348
1258
|
if (env.CURSOR_AGENT === "1") return "env marker CURSOR_AGENT=1";
|
|
1349
|
-
const result = process.platform === "win32" ? (0,
|
|
1259
|
+
const result = process.platform === "win32" ? (0, import_node_child_process6.spawnSync)("powershell.exe", ["-NoProfile", "-Command", "Get-CimInstance Win32_Process | ForEach-Object { $_.CommandLine }"], { encoding: "utf8", windowsHide: true, timeout: 15e3 }) : (0, import_node_child_process6.spawnSync)("ps", ["-eo", "args="], { encoding: "utf8", windowsHide: true, timeout: 15e3 });
|
|
1350
1260
|
if (result.status !== 0 || !(result.stdout ?? "").trim()) return "the process table is unreadable (unsafe to write)";
|
|
1351
1261
|
for (const line of result.stdout.split(/\r?\n/)) {
|
|
1352
1262
|
const executable = cursorExecutable(line);
|
|
@@ -1355,7 +1265,7 @@ function cursorHostEvidence(env) {
|
|
|
1355
1265
|
return null;
|
|
1356
1266
|
}
|
|
1357
1267
|
function resolvedPluginRoot(root) {
|
|
1358
|
-
return (0,
|
|
1268
|
+
return (0, import_node_path8.join)(root).replace(/\\/g, "/");
|
|
1359
1269
|
}
|
|
1360
1270
|
function isMmiEntry(entry, pluginRoot) {
|
|
1361
1271
|
if (typeof entry !== "object" || entry === null || typeof entry.command !== "string") return false;
|
|
@@ -1379,12 +1289,12 @@ function stripUserHooks(existing, pluginRoot) {
|
|
|
1379
1289
|
}
|
|
1380
1290
|
var CURSOR_HOOKS_LOCK_OWNER = "mmi-hub:cursor-hooks";
|
|
1381
1291
|
function reconcileCursorUserHooks(pluginRoot, homeDir, lock = {}) {
|
|
1382
|
-
const path = (0,
|
|
1292
|
+
const path = (0, import_node_path8.join)(homeDir, ".cursor", USER_HOOKS_FILE);
|
|
1383
1293
|
return withSharedDocLock(path, { owner: CURSOR_HOOKS_LOCK_OWNER, ...lock }, () => {
|
|
1384
|
-
if (!(0,
|
|
1294
|
+
if (!(0, import_node_fs11.existsSync)(path)) return "current";
|
|
1385
1295
|
let existing;
|
|
1386
1296
|
try {
|
|
1387
|
-
existing = JSON.parse((0,
|
|
1297
|
+
existing = JSON.parse((0, import_node_fs11.readFileSync)(path, "utf8"));
|
|
1388
1298
|
} catch {
|
|
1389
1299
|
return "user-conflict";
|
|
1390
1300
|
}
|
|
@@ -1392,8 +1302,8 @@ function reconcileCursorUserHooks(pluginRoot, homeDir, lock = {}) {
|
|
|
1392
1302
|
if (!stripped) return "user-conflict";
|
|
1393
1303
|
if (JSON.stringify(stripped) === JSON.stringify(existing)) return "current";
|
|
1394
1304
|
const tmp = `${path}.tmp-${process.pid}`;
|
|
1395
|
-
(0,
|
|
1396
|
-
(0,
|
|
1305
|
+
(0, import_node_fs11.writeFileSync)(tmp, JSON.stringify(stripped, null, 2) + "\n", "utf8");
|
|
1306
|
+
(0, import_node_fs11.renameSync)(tmp, path);
|
|
1397
1307
|
return "updated";
|
|
1398
1308
|
});
|
|
1399
1309
|
}
|
|
@@ -1408,41 +1318,41 @@ function userHooksDetail(pluginRoot, homeDir) {
|
|
|
1408
1318
|
case "updated":
|
|
1409
1319
|
return "; user hooks legacy entries stripped";
|
|
1410
1320
|
case "user-conflict":
|
|
1411
|
-
return `; USER HOOKS CONFLICT at ${(0,
|
|
1321
|
+
return `; USER HOOKS CONFLICT at ${(0, import_node_path8.join)(homeDir, ".cursor", USER_HOOKS_FILE)} \u2014 legacy entries were not changed`;
|
|
1412
1322
|
default:
|
|
1413
1323
|
return "";
|
|
1414
1324
|
}
|
|
1415
1325
|
}
|
|
1416
1326
|
function writeRollbackPointer(root, entry) {
|
|
1417
|
-
const path = (0,
|
|
1327
|
+
const path = (0, import_node_path8.join)(root, "quarantine", "rollback.json");
|
|
1418
1328
|
const tmp = `${path}.tmp-${process.pid}`;
|
|
1419
1329
|
try {
|
|
1420
|
-
(0,
|
|
1421
|
-
(0,
|
|
1330
|
+
(0, import_node_fs11.writeFileSync)(tmp, JSON.stringify({ surface: "cursor", ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry }, null, 2) + "\n", "utf8");
|
|
1331
|
+
(0, import_node_fs11.renameSync)(tmp, path);
|
|
1422
1332
|
} catch {
|
|
1423
1333
|
discard(tmp);
|
|
1424
1334
|
}
|
|
1425
1335
|
}
|
|
1426
1336
|
function mtimeMs(path) {
|
|
1427
1337
|
try {
|
|
1428
|
-
return (0,
|
|
1338
|
+
return (0, import_node_fs11.statSync)(path).mtimeMs;
|
|
1429
1339
|
} catch {
|
|
1430
1340
|
return 0;
|
|
1431
1341
|
}
|
|
1432
1342
|
}
|
|
1433
1343
|
function pruneQuarantine(root, keep) {
|
|
1434
|
-
const dir = (0,
|
|
1344
|
+
const dir = (0, import_node_path8.join)(root, "quarantine");
|
|
1435
1345
|
let generations;
|
|
1436
1346
|
try {
|
|
1437
|
-
generations = (0,
|
|
1347
|
+
generations = (0, import_node_fs11.readdirSync)(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("mmi-")).map((entry) => (0, import_node_path8.join)(dir, entry.name)).sort((a, b) => mtimeMs(b) - mtimeMs(a));
|
|
1438
1348
|
} catch {
|
|
1439
1349
|
return 0;
|
|
1440
1350
|
}
|
|
1441
1351
|
let stranded = 0;
|
|
1442
1352
|
try {
|
|
1443
|
-
for (const entry of (0,
|
|
1353
|
+
for (const entry of (0, import_node_fs11.readdirSync)(dir, { withFileTypes: true })) {
|
|
1444
1354
|
if (!entry.isFile() || !/^rollback\.json\.tmp-\d+$/.test(entry.name)) continue;
|
|
1445
|
-
const path = (0,
|
|
1355
|
+
const path = (0, import_node_path8.join)(dir, entry.name);
|
|
1446
1356
|
if (Date.now() - mtimeMs(path) < STRANDED_TMP_AGE_MS) continue;
|
|
1447
1357
|
discard(path);
|
|
1448
1358
|
stranded += 1;
|
|
@@ -1460,16 +1370,16 @@ function pruneQuarantine(root, keep) {
|
|
|
1460
1370
|
}
|
|
1461
1371
|
function restorePrevious(live, quarantined, broken) {
|
|
1462
1372
|
try {
|
|
1463
|
-
(0,
|
|
1373
|
+
(0, import_node_fs11.renameSync)(live, broken);
|
|
1464
1374
|
} catch {
|
|
1465
1375
|
return "kept-broken";
|
|
1466
1376
|
}
|
|
1467
1377
|
try {
|
|
1468
|
-
(0,
|
|
1378
|
+
(0, import_node_fs11.renameSync)(quarantined, live);
|
|
1469
1379
|
return "restored";
|
|
1470
1380
|
} catch {
|
|
1471
1381
|
try {
|
|
1472
|
-
(0,
|
|
1382
|
+
(0, import_node_fs11.renameSync)(broken, live);
|
|
1473
1383
|
return "kept-broken";
|
|
1474
1384
|
} catch {
|
|
1475
1385
|
return "lost";
|
|
@@ -1480,7 +1390,7 @@ function cursorArm(options) {
|
|
|
1480
1390
|
const env = options.env ?? process.env;
|
|
1481
1391
|
const { target, dryRun, paths } = options;
|
|
1482
1392
|
const root = cursorPluginsRoot();
|
|
1483
|
-
const live = (0,
|
|
1393
|
+
const live = (0, import_node_path8.join)(root, "local", "mmi");
|
|
1484
1394
|
const result = (from, to, verdict, detail, launchStaged = false) => ({
|
|
1485
1395
|
surface: "cursor",
|
|
1486
1396
|
from,
|
|
@@ -1490,7 +1400,7 @@ function cursorArm(options) {
|
|
|
1490
1400
|
launchStaged: launchStaged || void 0,
|
|
1491
1401
|
// C11: the constraint of the generation LEFT at `live` — read at result time, so a failed
|
|
1492
1402
|
// switch reports the restored generation's declaration, not the rejected one's (#4973).
|
|
1493
|
-
compat: readManifestCompat((0,
|
|
1403
|
+
compat: readManifestCompat((0, import_node_path8.join)(live, MANIFEST))
|
|
1494
1404
|
});
|
|
1495
1405
|
const current = manifest(live);
|
|
1496
1406
|
const installed = current?.version ?? null;
|
|
@@ -1504,7 +1414,7 @@ function cursorArm(options) {
|
|
|
1504
1414
|
if (installed === target && treeHealthy(live)) {
|
|
1505
1415
|
return result(installed, target, "ok", `already at target ${target}${userHooksDetail(live, (0, import_node_os5.homedir)())}`);
|
|
1506
1416
|
}
|
|
1507
|
-
if ((0,
|
|
1417
|
+
if ((0, import_node_fs11.existsSync)(live) && !mmiOwned(live)) {
|
|
1508
1418
|
return result(installed, target, "defer", `${live} carries no MMI ownership marker (plugin manifest name, or a ${PACKAGE} package.json) \u2014 refusing to displace an unmanaged directory (mmi-cli plugin heal adopts it)`);
|
|
1509
1419
|
}
|
|
1510
1420
|
if (dryRun) {
|
|
@@ -1518,16 +1428,16 @@ function cursorArm(options) {
|
|
|
1518
1428
|
if (stagedMarker !== target || !treeHealthy(staged.packageRoot)) {
|
|
1519
1429
|
return result(installed, target, "defer", `staged tree marker is ${stagedMarker ?? "unreadable"} and the tree is ${treeHealthy(staged.packageRoot) ? "complete" : "incomplete"}, expected ${target} and complete`);
|
|
1520
1430
|
}
|
|
1521
|
-
const launchTargetPath = (0,
|
|
1431
|
+
const launchTargetPath = (0, import_node_path8.join)(root, "staging", "launch-target.json");
|
|
1522
1432
|
const priorLaunch = readLaunchTarget(launchTargetPath);
|
|
1523
|
-
let incoming = priorLaunch?.target === target && priorLaunch.incoming.startsWith((0,
|
|
1433
|
+
let incoming = priorLaunch?.target === target && priorLaunch.incoming.startsWith((0, import_node_path8.join)(root, "staging")) && manifest(priorLaunch.incoming)?.version === target && treeHealthy(priorLaunch.incoming) ? priorLaunch.incoming : (0, import_node_path8.join)(root, "staging", `mmi-${safeSegment(target)}-${Date.now()}`);
|
|
1524
1434
|
const reuseIncoming = incoming === priorLaunch?.incoming;
|
|
1525
|
-
if (!reuseIncoming || !(0,
|
|
1526
|
-
incoming = (0,
|
|
1527
|
-
for (const dir of ["local", "staging", "quarantine"]) (0,
|
|
1435
|
+
if (!reuseIncoming || !(0, import_node_fs11.existsSync)(incoming)) {
|
|
1436
|
+
incoming = (0, import_node_path8.join)(root, "staging", `mmi-${safeSegment(target)}-${Date.now()}`);
|
|
1437
|
+
for (const dir of ["local", "staging", "quarantine"]) (0, import_node_fs11.mkdirSync)((0, import_node_path8.join)(root, dir), { recursive: true });
|
|
1528
1438
|
try {
|
|
1529
1439
|
discard(incoming);
|
|
1530
|
-
(0,
|
|
1440
|
+
(0, import_node_fs11.cpSync)(staged.packageRoot, incoming, { recursive: true });
|
|
1531
1441
|
} catch (error) {
|
|
1532
1442
|
discard(incoming);
|
|
1533
1443
|
return result(installed, target, "defer", `copy into ${root} failed: ${message(error)}`);
|
|
@@ -1550,8 +1460,8 @@ function cursorArm(options) {
|
|
|
1550
1460
|
return result(installed, target, "ok", `staged ${target} for next launch while Cursor is running (${busy}) \u2014 ${pending}`, true);
|
|
1551
1461
|
}
|
|
1552
1462
|
const stamp = Date.now();
|
|
1553
|
-
const quarantined = (0,
|
|
1554
|
-
for (const dir of ["local", "staging", "quarantine"]) (0,
|
|
1463
|
+
const quarantined = (0, import_node_path8.join)(root, "quarantine", `mmi-${safeSegment(installed ?? "unknown")}-${stamp}`);
|
|
1464
|
+
for (const dir of ["local", "staging", "quarantine"]) (0, import_node_fs11.mkdirSync)((0, import_node_path8.join)(root, dir), { recursive: true });
|
|
1555
1465
|
const started = cursorHostEvidence(env);
|
|
1556
1466
|
if (started) {
|
|
1557
1467
|
const pending = noteLaunchStaged(paths, env, {
|
|
@@ -1566,15 +1476,15 @@ function cursorArm(options) {
|
|
|
1566
1476
|
}
|
|
1567
1477
|
let displaced = false;
|
|
1568
1478
|
try {
|
|
1569
|
-
if ((0,
|
|
1570
|
-
(0,
|
|
1479
|
+
if ((0, import_node_fs11.existsSync)(live)) {
|
|
1480
|
+
(0, import_node_fs11.renameSync)(live, quarantined);
|
|
1571
1481
|
displaced = true;
|
|
1572
1482
|
}
|
|
1573
|
-
(0,
|
|
1483
|
+
(0, import_node_fs11.renameSync)(incoming, live);
|
|
1574
1484
|
} catch (error) {
|
|
1575
|
-
if (displaced && !(0,
|
|
1485
|
+
if (displaced && !(0, import_node_fs11.existsSync)(live)) {
|
|
1576
1486
|
try {
|
|
1577
|
-
(0,
|
|
1487
|
+
(0, import_node_fs11.renameSync)(quarantined, live);
|
|
1578
1488
|
} catch {
|
|
1579
1489
|
}
|
|
1580
1490
|
}
|
|
@@ -1585,7 +1495,7 @@ function cursorArm(options) {
|
|
|
1585
1495
|
}
|
|
1586
1496
|
const evidence = manifest(live)?.version;
|
|
1587
1497
|
if (evidence !== target || !treeHealthy(live)) {
|
|
1588
|
-
const broken = `${(0,
|
|
1498
|
+
const broken = `${(0, import_node_path8.join)(root, "quarantine", `mmi-${safeSegment(evidence ?? "broken")}-${stamp}`)}-failed`;
|
|
1589
1499
|
const outcome = displaced ? restorePrevious(live, quarantined, broken) : "none";
|
|
1590
1500
|
const detail = outcome === "restored" ? `restored the previous generation ${installed ?? "unknown"}, unverified tree kept at ${broken}` : outcome === "lost" ? `restore failed and ${live} is missing \u2014 the previous generation is at ${quarantined}` : displaced ? `restore failed, the unverified tree is still live and the previous generation is at ${quarantined}` : "nothing was displaced \u2014 there is no previous generation to restore";
|
|
1591
1501
|
writeRollbackPointer(root, outcome === "restored" ? { installed: installed ?? "unknown", previous: null, generation: null } : { installed: evidence ?? "unknown", previous: installed, generation: displaced ? quarantined : null });
|
|
@@ -1598,12 +1508,12 @@ function cursorArm(options) {
|
|
|
1598
1508
|
return result(installed, target, "ok", `switched ${installed ?? "absent"} -> ${target}; ${previous}${pruned ? `; pruned ${pruned} older generation${pruned === 1 ? "" : "s"}` : ""}${userHooksDetail(live, (0, import_node_os5.homedir)())}`);
|
|
1599
1509
|
}
|
|
1600
1510
|
function probeCursorLaunchStaging() {
|
|
1601
|
-
return probeLaunchTargetVersion((0,
|
|
1511
|
+
return probeLaunchTargetVersion((0, import_node_path8.join)(cursorPluginsRoot(), "staging", "launch-target.json"), (root) => manifest(root)?.version ?? null);
|
|
1602
1512
|
}
|
|
1603
1513
|
|
|
1604
1514
|
// src/arm-jervcode.ts
|
|
1605
|
-
var
|
|
1606
|
-
var
|
|
1515
|
+
var import_node_fs12 = require("node:fs");
|
|
1516
|
+
var import_node_path9 = require("node:path");
|
|
1607
1517
|
var PI_PACKAGE = "@mutmutco/pi-plugin";
|
|
1608
1518
|
function hostEnv(env) {
|
|
1609
1519
|
const dir = jervcodeAgentDir(env);
|
|
@@ -1614,7 +1524,7 @@ function hostCommands(_env) {
|
|
|
1614
1524
|
}
|
|
1615
1525
|
function readPackageSpecs(env) {
|
|
1616
1526
|
try {
|
|
1617
|
-
const settings = JSON.parse((0,
|
|
1527
|
+
const settings = JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path9.join)(jervcodeAgentDir(env), "settings.json"), "utf8"));
|
|
1618
1528
|
return Array.isArray(settings.packages) ? settings.packages.filter((entry) => typeof entry === "string") : [];
|
|
1619
1529
|
} catch {
|
|
1620
1530
|
return null;
|
|
@@ -1622,14 +1532,14 @@ function readPackageSpecs(env) {
|
|
|
1622
1532
|
}
|
|
1623
1533
|
function installedVersion(env) {
|
|
1624
1534
|
try {
|
|
1625
|
-
const manifest2 = JSON.parse((0,
|
|
1535
|
+
const manifest2 = JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path9.join)(jervcodeAgentDir(env), "npm", "node_modules", ...PI_PACKAGE.split("/"), "package.json"), "utf8"));
|
|
1626
1536
|
return typeof manifest2.version === "string" ? manifest2.version : null;
|
|
1627
1537
|
} catch {
|
|
1628
1538
|
return null;
|
|
1629
1539
|
}
|
|
1630
1540
|
}
|
|
1631
1541
|
function materializedManifestLocation(env) {
|
|
1632
|
-
return (0,
|
|
1542
|
+
return (0, import_node_path9.join)(jervcodeAgentDir(env), "npm", "node_modules", ...PI_PACKAGE.split("/"));
|
|
1633
1543
|
}
|
|
1634
1544
|
var PI_LIST_SPEC = new RegExp(`npm:${PI_PACKAGE.replace("/", "\\/")}@(\\S+)`);
|
|
1635
1545
|
function parseListText(stdout) {
|
|
@@ -1688,32 +1598,32 @@ function jervcodeArm(options) {
|
|
|
1688
1598
|
detail,
|
|
1689
1599
|
// C11: the constraint of the package pi LEFT materialised — read at result time, so defer/fail
|
|
1690
1600
|
// carry the previous payload's declaration (#4973 reconsult ruling).
|
|
1691
|
-
compat: readManifestCompat((0,
|
|
1601
|
+
compat: readManifestCompat((0, import_node_path9.join)(jervcodeAgentDir(env), "npm", "node_modules", ...PI_PACKAGE.split("/"), "package.json"))
|
|
1692
1602
|
});
|
|
1693
1603
|
const before = readPackageSpecs(env);
|
|
1694
|
-
if (!before) return result(null, options.target, "defer", `cannot read ${(0,
|
|
1604
|
+
if (!before) return result(null, options.target, "defer", `cannot read ${(0, import_node_path9.join)(jervcodeAgentDir(env), "settings.json")}`);
|
|
1695
1605
|
const from = installedVersion(env);
|
|
1696
1606
|
const legacy = before.filter(isLegacyMmiPiPath);
|
|
1697
1607
|
const highWater = journalHighWater(options.paths.journalPath, "jervcode");
|
|
1698
1608
|
const floor = from && compareSemver(from, options.target) > 0 ? `installed ${from} is above candidate ${options.target} (monotonic)` : highWater && compareSemver(highWater, options.target) > 0 ? `journal high-water ${highWater} is above candidate ${options.target} (monotonic)` : null;
|
|
1699
1609
|
if (options.dryRun) {
|
|
1700
1610
|
const action = floor ? `hold ${from ?? "the install"} and skip the install (${floor})` : before.includes(exact) && from === options.target ? "keep exact pin" : `install ${exact}`;
|
|
1701
|
-
return { surface: "jervcode", from, to: options.target, verdict: "ok", detail: `dry-run: would ${action}${legacy.length ? ` and retire ${legacy.length} legacy claude-cache path registration${legacy.length === 1 ? "" : "s"}` : ""}`, compat: readManifestCompat((0,
|
|
1611
|
+
return { surface: "jervcode", from, to: options.target, verdict: "ok", detail: `dry-run: would ${action}${legacy.length ? ` and retire ${legacy.length} legacy claude-cache path registration${legacy.length === 1 ? "" : "s"}` : ""}`, compat: readManifestCompat((0, import_node_path9.join)(jervcodeAgentDir(env), "npm", "node_modules", ...PI_PACKAGE.split("/"), "package.json")) };
|
|
1702
1612
|
}
|
|
1703
1613
|
const hosted = hostEnv(env);
|
|
1704
1614
|
const commands = hostCommands(env);
|
|
1705
1615
|
const command = commands.find((bin) => runner(bin, ["--version"], hosted).status === 0);
|
|
1706
|
-
if (!command) return { surface: "jervcode", from, to: options.target, verdict: "defer", detail: "neither `jervcode` nor `pi` answered --version \u2014 the host CLI is not on PATH", compat: readManifestCompat((0,
|
|
1616
|
+
if (!command) return { surface: "jervcode", from, to: options.target, verdict: "defer", detail: "neither `jervcode` nor `pi` answered --version \u2014 the host CLI is not on PATH", compat: readManifestCompat((0, import_node_path9.join)(jervcodeAgentDir(env), "npm", "node_modules", ...PI_PACKAGE.split("/"), "package.json")) };
|
|
1707
1617
|
if (!floor && (!before.includes(exact) || from !== options.target)) {
|
|
1708
1618
|
const install = runner(command, ["install", exact], hosted);
|
|
1709
1619
|
if (install.status !== 0) {
|
|
1710
|
-
return { surface: "jervcode", from, to: options.target, verdict: "defer", detail: `${command} install ${exact} failed: ${failure2(install)}`, compat: readManifestCompat((0,
|
|
1620
|
+
return { surface: "jervcode", from, to: options.target, verdict: "defer", detail: `${command} install ${exact} failed: ${failure2(install)}`, compat: readManifestCompat((0, import_node_path9.join)(jervcodeAgentDir(env), "npm", "node_modules", ...PI_PACKAGE.split("/"), "package.json")) };
|
|
1711
1621
|
}
|
|
1712
1622
|
}
|
|
1713
1623
|
for (const stale of legacy) {
|
|
1714
1624
|
const removed = runner(command, ["remove", stale], hosted);
|
|
1715
1625
|
if (removed.status !== 0) {
|
|
1716
|
-
return { surface: "jervcode", from, to: options.target, verdict: "defer", detail: `${command} remove of the legacy registration failed: ${failure2(removed)}`, compat: readManifestCompat((0,
|
|
1626
|
+
return { surface: "jervcode", from, to: options.target, verdict: "defer", detail: `${command} remove of the legacy registration failed: ${failure2(removed)}`, compat: readManifestCompat((0, import_node_path9.join)(jervcodeAgentDir(env), "npm", "node_modules", ...PI_PACKAGE.split("/"), "package.json")) };
|
|
1717
1627
|
}
|
|
1718
1628
|
}
|
|
1719
1629
|
const after = readPackageSpecs(env);
|
|
@@ -1721,7 +1631,7 @@ function jervcodeArm(options) {
|
|
|
1721
1631
|
const legacyAfter = after?.filter(isLegacyMmiPiPath) ?? [];
|
|
1722
1632
|
const retired = legacy.length ? `; retired ${legacy.length} legacy claude-cache path registration${legacy.length === 1 ? "" : "s"}` : "";
|
|
1723
1633
|
if (floor && !legacyAfter.length) {
|
|
1724
|
-
return { surface: "jervcode", from, to: converged ?? highWater ?? options.target, verdict: "skip", detail: `${floor}${retired}`, compat: readManifestCompat((0,
|
|
1634
|
+
return { surface: "jervcode", from, to: converged ?? highWater ?? options.target, verdict: "skip", detail: `${floor}${retired}`, compat: readManifestCompat((0, import_node_path9.join)(jervcodeAgentDir(env), "npm", "node_modules", ...PI_PACKAGE.split("/"), "package.json")) };
|
|
1725
1635
|
}
|
|
1726
1636
|
if (!after?.includes(exact) || converged !== options.target || legacyAfter.length) {
|
|
1727
1637
|
return {
|
|
@@ -1742,15 +1652,15 @@ function jervcodeArm(options) {
|
|
|
1742
1652
|
}
|
|
1743
1653
|
|
|
1744
1654
|
// src/arm-hermes.ts
|
|
1745
|
-
var
|
|
1746
|
-
var
|
|
1747
|
-
var
|
|
1655
|
+
var import_node_fs13 = require("node:fs");
|
|
1656
|
+
var import_node_child_process7 = require("node:child_process");
|
|
1657
|
+
var import_node_path10 = require("node:path");
|
|
1748
1658
|
var PACKAGE2 = "@mutmutco/hermes-plugin";
|
|
1749
1659
|
var MANIFEST2 = "plugin.yaml";
|
|
1750
1660
|
var TREE_MARKERS_FALLBACK = [MANIFEST2, "__init__.py", "skills"];
|
|
1751
1661
|
function yamlVersion(root) {
|
|
1752
1662
|
try {
|
|
1753
|
-
const text = (0,
|
|
1663
|
+
const text = (0, import_node_fs13.readFileSync)((0, import_node_path10.join)(root, MANIFEST2), "utf8");
|
|
1754
1664
|
try {
|
|
1755
1665
|
const version = JSON.parse(text).version;
|
|
1756
1666
|
if (typeof version === "string" && version.trim()) return version.trim();
|
|
@@ -1764,7 +1674,7 @@ function yamlVersion(root) {
|
|
|
1764
1674
|
}
|
|
1765
1675
|
function packageTreeMarkers(root) {
|
|
1766
1676
|
try {
|
|
1767
|
-
const files = JSON.parse((0,
|
|
1677
|
+
const files = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path10.join)(root, "package.json"), "utf8")).files;
|
|
1768
1678
|
if (Array.isArray(files) && files.length && files.every((entry) => typeof entry === "string" && entry.trim())) {
|
|
1769
1679
|
return files.map((entry) => entry.trim());
|
|
1770
1680
|
}
|
|
@@ -1773,10 +1683,10 @@ function packageTreeMarkers(root) {
|
|
|
1773
1683
|
return TREE_MARKERS_FALLBACK;
|
|
1774
1684
|
}
|
|
1775
1685
|
function markerPresent(root, rel) {
|
|
1776
|
-
const path = (0,
|
|
1777
|
-
if (!(0,
|
|
1686
|
+
const path = (0, import_node_path10.join)(root, ...rel.split("/"));
|
|
1687
|
+
if (!(0, import_node_fs13.existsSync)(path)) return false;
|
|
1778
1688
|
try {
|
|
1779
|
-
const stat = (0,
|
|
1689
|
+
const stat = (0, import_node_fs13.statSync)(path);
|
|
1780
1690
|
return stat.isFile() || stat.isDirectory();
|
|
1781
1691
|
} catch {
|
|
1782
1692
|
return false;
|
|
@@ -1795,13 +1705,13 @@ function treeIncompleteDetail(root, label) {
|
|
|
1795
1705
|
}
|
|
1796
1706
|
function packageIdentity(root) {
|
|
1797
1707
|
try {
|
|
1798
|
-
return JSON.parse((0,
|
|
1708
|
+
return JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path10.join)(root, "package.json"), "utf8")).name === PACKAGE2;
|
|
1799
1709
|
} catch {
|
|
1800
1710
|
return false;
|
|
1801
1711
|
}
|
|
1802
1712
|
}
|
|
1803
1713
|
function probeHermesInstallation(env = process.env) {
|
|
1804
|
-
const location = (0,
|
|
1714
|
+
const location = (0, import_node_path10.join)(hermesHome(env), "plugins", "mmi");
|
|
1805
1715
|
const version = yamlVersion(location);
|
|
1806
1716
|
return { version, location, detail: version ? "installed Hermes plugin manifest" : "installed Hermes plugin manifest was unreadable" };
|
|
1807
1717
|
}
|
|
@@ -1810,7 +1720,7 @@ function mmiOwned2(root) {
|
|
|
1810
1720
|
}
|
|
1811
1721
|
function discard2(path) {
|
|
1812
1722
|
try {
|
|
1813
|
-
(0,
|
|
1723
|
+
(0, import_node_fs13.rmSync)(path, { recursive: true, force: true });
|
|
1814
1724
|
} catch {
|
|
1815
1725
|
}
|
|
1816
1726
|
}
|
|
@@ -1829,7 +1739,7 @@ function hermesProcessToken(line, home) {
|
|
|
1829
1739
|
return hermesToken(tokens) ?? (home && normalized.toLowerCase().includes(home.toLowerCase()) ? home : null);
|
|
1830
1740
|
}
|
|
1831
1741
|
function realProcessListing() {
|
|
1832
|
-
const result = process.platform === "win32" ? (0,
|
|
1742
|
+
const result = process.platform === "win32" ? (0, import_node_child_process7.spawnSync)("powershell.exe", ["-NoProfile", "-Command", "Get-CimInstance Win32_Process | ForEach-Object { $_.Name + ' ' + $_.CommandLine }"], { encoding: "utf8", windowsHide: true, timeout: 15e3 }) : (0, import_node_child_process7.spawnSync)("ps", ["-eo", "args="], { encoding: "utf8", windowsHide: true, timeout: 15e3 });
|
|
1833
1743
|
return result.status === 0 ? result.stdout ?? "" : null;
|
|
1834
1744
|
}
|
|
1835
1745
|
function hermesHostEvidence(env, home, processListProvider) {
|
|
@@ -1842,19 +1752,19 @@ function hermesHostEvidence(env, home, processListProvider) {
|
|
|
1842
1752
|
return null;
|
|
1843
1753
|
}
|
|
1844
1754
|
function materializeHermesSkills(home) {
|
|
1845
|
-
const pluginSkills = (0,
|
|
1846
|
-
const skillsRoot = (0,
|
|
1847
|
-
const targetRoot = (0,
|
|
1755
|
+
const pluginSkills = (0, import_node_path10.join)(home, "plugins", "mmi", "skills");
|
|
1756
|
+
const skillsRoot = (0, import_node_path10.join)(home, "skills");
|
|
1757
|
+
const targetRoot = (0, import_node_path10.join)(skillsRoot, "mmi");
|
|
1848
1758
|
try {
|
|
1849
|
-
if (!(0,
|
|
1850
|
-
const names = (0,
|
|
1759
|
+
if (!(0, import_node_fs13.existsSync)(pluginSkills)) return { ok: false, detail: `plugin skills tree missing at ${pluginSkills}` };
|
|
1760
|
+
const names = (0, import_node_fs13.readdirSync)(pluginSkills, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_")).map((entry) => entry.name);
|
|
1851
1761
|
if (!names.length) return { ok: false, detail: `plugin skills tree is empty at ${pluginSkills}` };
|
|
1852
|
-
(0,
|
|
1853
|
-
const incoming = (0,
|
|
1762
|
+
(0, import_node_fs13.mkdirSync)(skillsRoot, { recursive: true });
|
|
1763
|
+
const incoming = (0, import_node_path10.join)(skillsRoot, ".mmi-incoming");
|
|
1854
1764
|
discard2(incoming);
|
|
1855
|
-
(0,
|
|
1765
|
+
(0, import_node_fs13.cpSync)(pluginSkills, incoming, { recursive: true });
|
|
1856
1766
|
discard2(targetRoot);
|
|
1857
|
-
(0,
|
|
1767
|
+
(0, import_node_fs13.renameSync)(incoming, targetRoot);
|
|
1858
1768
|
return { ok: true, detail: `provisioned ${names.length} skills to ${targetRoot}` };
|
|
1859
1769
|
} catch (error) {
|
|
1860
1770
|
return { ok: false, detail: message2(error) };
|
|
@@ -1863,8 +1773,8 @@ function materializeHermesSkills(home) {
|
|
|
1863
1773
|
function hermesArm(options) {
|
|
1864
1774
|
const env = options.env ?? process.env;
|
|
1865
1775
|
const home = hermesHome(env);
|
|
1866
|
-
const parent = (0,
|
|
1867
|
-
const live = (0,
|
|
1776
|
+
const parent = (0, import_node_path10.join)(home, "plugins");
|
|
1777
|
+
const live = (0, import_node_path10.join)(parent, "mmi");
|
|
1868
1778
|
const installed = yamlVersion(live);
|
|
1869
1779
|
const result = (from, to, verdict, detail, launchStaged = false) => ({
|
|
1870
1780
|
surface: "hermes",
|
|
@@ -1877,7 +1787,7 @@ function hermesArm(options) {
|
|
|
1877
1787
|
if (!hermesHostPresent(env, options.hostCommandRunner)) {
|
|
1878
1788
|
return result(null, options.target, "skip", `host absent (${hermesHostCommand(env)} CLI not on PATH) \u2014 skipped without writes`);
|
|
1879
1789
|
}
|
|
1880
|
-
if ((0,
|
|
1790
|
+
if ((0, import_node_fs13.existsSync)(live) && !mmiOwned2(live)) {
|
|
1881
1791
|
return result(installed, options.target, "defer", `${live} carries no ${PACKAGE2} package identity \u2014 refusing to displace an unmanaged Hermes mmi plugin`);
|
|
1882
1792
|
}
|
|
1883
1793
|
if (installed && compareSemver(installed, options.target) > 0) return result(installed, installed, "skip", `installed ${installed} is above candidate ${options.target} (monotonic)`);
|
|
@@ -1888,7 +1798,7 @@ function hermesArm(options) {
|
|
|
1888
1798
|
if (!skills2.ok) return result(installed, options.target, "fail", `plugin at target ${options.target}, but skills provisioning failed: ${skills2.detail}`);
|
|
1889
1799
|
return result(installed, options.target, "ok", `already at target ${options.target}; ${skills2.detail}`);
|
|
1890
1800
|
}
|
|
1891
|
-
if (options.dryRun) return result(installed, options.target, "ok", `dry-run: would stage ${PACKAGE2}@${options.target}, atomically switch ${installed ?? "absent"} -> ${options.target}, then provision skills into ${(0,
|
|
1801
|
+
if (options.dryRun) return result(installed, options.target, "ok", `dry-run: would stage ${PACKAGE2}@${options.target}, atomically switch ${installed ?? "absent"} -> ${options.target}, then provision skills into ${(0, import_node_path10.join)(home, "skills", "mmi")}`);
|
|
1892
1802
|
const staged = stagePackage({ surface: "hermes", packageName: PACKAGE2, target: options.target, witness: MANIFEST2, paths: options.paths, env });
|
|
1893
1803
|
if ("defer" in staged) return result(installed, options.target, "defer", staged.defer);
|
|
1894
1804
|
const stagedVersion = yamlVersion(staged.packageRoot);
|
|
@@ -1897,19 +1807,19 @@ function hermesArm(options) {
|
|
|
1897
1807
|
}
|
|
1898
1808
|
const stagedTree = treeIncompleteDetail(staged.packageRoot, "staged Hermes");
|
|
1899
1809
|
if (stagedTree) return result(installed, options.target, "defer", stagedTree);
|
|
1900
|
-
const scratch = (0,
|
|
1901
|
-
const launchTargetPath = (0,
|
|
1902
|
-
const incomingRoot = (0,
|
|
1903
|
-
const incoming = (0,
|
|
1904
|
-
const quarantine = (0,
|
|
1810
|
+
const scratch = (0, import_node_path10.join)(home, ".mmi-updater");
|
|
1811
|
+
const launchTargetPath = (0, import_node_path10.join)(scratch, "launch-target.json");
|
|
1812
|
+
const incomingRoot = (0, import_node_path10.join)(scratch, "incoming");
|
|
1813
|
+
const incoming = (0, import_node_path10.join)(incomingRoot, options.target);
|
|
1814
|
+
const quarantine = (0, import_node_path10.join)(scratch, "quarantine", `${installed ?? "unknown"}-${Date.now()}`);
|
|
1905
1815
|
const priorLaunch = readLaunchTarget(launchTargetPath);
|
|
1906
1816
|
const reuseIncoming = priorLaunch?.target === options.target && priorLaunch.incoming === incoming && yamlVersion(incoming) === options.target && treeHealthy2(incoming);
|
|
1907
1817
|
if (!reuseIncoming) {
|
|
1908
1818
|
try {
|
|
1909
|
-
(0,
|
|
1910
|
-
(0,
|
|
1819
|
+
(0, import_node_fs13.mkdirSync)(parent, { recursive: true });
|
|
1820
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path10.join)(scratch, "quarantine"), { recursive: true });
|
|
1911
1821
|
discard2(incomingRoot);
|
|
1912
|
-
(0,
|
|
1822
|
+
(0, import_node_fs13.cpSync)(staged.packageRoot, incoming, { recursive: true });
|
|
1913
1823
|
} catch (error) {
|
|
1914
1824
|
return result(installed, options.target, "defer", `cannot place incoming Hermes generation: ${message2(error)}`);
|
|
1915
1825
|
}
|
|
@@ -1937,13 +1847,13 @@ function hermesArm(options) {
|
|
|
1937
1847
|
});
|
|
1938
1848
|
return result(installed, options.target, "ok", `staged ${options.target} for next launch while Hermes is running (${busy}) \u2014 ${pending}`, true);
|
|
1939
1849
|
}
|
|
1940
|
-
const displaced = (0,
|
|
1850
|
+
const displaced = (0, import_node_fs13.existsSync)(live);
|
|
1941
1851
|
try {
|
|
1942
|
-
if (displaced) (0,
|
|
1943
|
-
(0,
|
|
1852
|
+
if (displaced) (0, import_node_fs13.renameSync)(live, quarantine);
|
|
1853
|
+
(0, import_node_fs13.renameSync)(incoming, live);
|
|
1944
1854
|
} catch (error) {
|
|
1945
|
-
if (displaced && !(0,
|
|
1946
|
-
(0,
|
|
1855
|
+
if (displaced && !(0, import_node_fs13.existsSync)(live)) try {
|
|
1856
|
+
(0, import_node_fs13.renameSync)(quarantine, live);
|
|
1947
1857
|
} catch {
|
|
1948
1858
|
}
|
|
1949
1859
|
discard2(incomingRoot);
|
|
@@ -1954,12 +1864,12 @@ function hermesArm(options) {
|
|
|
1954
1864
|
const liveVersion = yamlVersion(live);
|
|
1955
1865
|
const liveTree = treeIncompleteDetail(live, "live Hermes");
|
|
1956
1866
|
if (liveVersion !== options.target || liveTree) {
|
|
1957
|
-
const broken = (0,
|
|
1867
|
+
const broken = (0, import_node_path10.join)(scratch, "rejected", `${options.target}-${Date.now()}`);
|
|
1958
1868
|
const verifyDetail = liveVersion !== options.target ? `manifest is ${liveVersion ?? "unreadable"}, expected ${options.target}` : liveTree;
|
|
1959
1869
|
try {
|
|
1960
|
-
(0,
|
|
1961
|
-
(0,
|
|
1962
|
-
if (displaced) (0,
|
|
1870
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path10.join)(scratch, "rejected"), { recursive: true });
|
|
1871
|
+
(0, import_node_fs13.renameSync)(live, broken);
|
|
1872
|
+
if (displaced) (0, import_node_fs13.renameSync)(quarantine, live);
|
|
1963
1873
|
} catch (error) {
|
|
1964
1874
|
return result(installed, liveVersion ?? "unknown", "fail", `post-switch Hermes verification failed and recovery failed: ${message2(error)}`);
|
|
1965
1875
|
}
|
|
@@ -1972,17 +1882,17 @@ function hermesArm(options) {
|
|
|
1972
1882
|
return result(installed, options.target, "ok", `converged ${installed ?? "absent"} -> ${options.target} (staged, verified, atomically switched${displaced ? `; previous generation quarantined at ${quarantine}` : ""}); ${skills.detail}`);
|
|
1973
1883
|
}
|
|
1974
1884
|
function probeHermesLaunchStaging(env = process.env) {
|
|
1975
|
-
return probeLaunchTargetVersion((0,
|
|
1885
|
+
return probeLaunchTargetVersion((0, import_node_path10.join)(hermesHome(env), ".mmi-updater", "launch-target.json"), yamlVersion);
|
|
1976
1886
|
}
|
|
1977
1887
|
|
|
1978
1888
|
// src/reap.ts
|
|
1979
|
-
var
|
|
1980
|
-
var
|
|
1889
|
+
var import_node_fs14 = require("node:fs");
|
|
1890
|
+
var import_node_path11 = require("node:path");
|
|
1981
1891
|
var KEEP_GENERATIONS2 = 2;
|
|
1982
1892
|
var SCRATCH_AGE_MS = 24 * 60 * 6e4;
|
|
1983
1893
|
function listDirs(path) {
|
|
1984
1894
|
try {
|
|
1985
|
-
return (0,
|
|
1895
|
+
return (0, import_node_fs14.readdirSync)(path, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
1986
1896
|
} catch {
|
|
1987
1897
|
return [];
|
|
1988
1898
|
}
|
|
@@ -1990,7 +1900,7 @@ function listDirs(path) {
|
|
|
1990
1900
|
function generationSets(root) {
|
|
1991
1901
|
const children = listDirs(root);
|
|
1992
1902
|
if (children.some((name) => parseSemver(name))) return [root];
|
|
1993
|
-
return children.map((name) => (0,
|
|
1903
|
+
return children.map((name) => (0, import_node_path11.join)(root, name)).filter((path) => listDirs(path).some((name) => parseSemver(name)));
|
|
1994
1904
|
}
|
|
1995
1905
|
function pruneGenerations(root, category, dryRun) {
|
|
1996
1906
|
let reaped = 0;
|
|
@@ -2003,7 +1913,7 @@ function pruneGenerations(root, category, dryRun) {
|
|
|
2003
1913
|
continue;
|
|
2004
1914
|
}
|
|
2005
1915
|
try {
|
|
2006
|
-
(0,
|
|
1916
|
+
(0, import_node_fs14.rmSync)((0, import_node_path11.join)(set, version), { recursive: true, force: true });
|
|
2007
1917
|
reaped += 1;
|
|
2008
1918
|
} catch (error) {
|
|
2009
1919
|
failures.push(`${version}: ${error?.code ?? error?.message ?? error}`);
|
|
@@ -2017,8 +1927,8 @@ function pruneGenerations(root, category, dryRun) {
|
|
|
2017
1927
|
}
|
|
2018
1928
|
var LEASE_STEAL_SCRATCH = /^lease\.lock\.stale-\d+$/;
|
|
2019
1929
|
function atomicWriteScratch(path) {
|
|
2020
|
-
const literal = (0,
|
|
2021
|
-
return { dir: (0,
|
|
1930
|
+
const literal = (0, import_node_path11.basename)(path).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1931
|
+
return { dir: (0, import_node_path11.dirname)(path), pattern: new RegExp(`^${literal}\\.tmp-\\d+$`) };
|
|
2022
1932
|
}
|
|
2023
1933
|
function reapScratch(context, dryRun) {
|
|
2024
1934
|
const category = "scratch";
|
|
@@ -2036,7 +1946,7 @@ function reapScratch(context, dryRun) {
|
|
|
2036
1946
|
for (const [dir, patterns] of scanned) {
|
|
2037
1947
|
let entries;
|
|
2038
1948
|
try {
|
|
2039
|
-
entries = (0,
|
|
1949
|
+
entries = (0, import_node_fs14.readdirSync)(dir, { withFileTypes: true });
|
|
2040
1950
|
} catch {
|
|
2041
1951
|
unreadable.push(dir);
|
|
2042
1952
|
continue;
|
|
@@ -2044,10 +1954,10 @@ function reapScratch(context, dryRun) {
|
|
|
2044
1954
|
for (const entry of entries) {
|
|
2045
1955
|
if (!patterns.some((pattern) => pattern.test(entry.name))) continue;
|
|
2046
1956
|
if (!entry.isFile()) continue;
|
|
2047
|
-
const path = (0,
|
|
1957
|
+
const path = (0, import_node_path11.join)(dir, entry.name);
|
|
2048
1958
|
let ageMs;
|
|
2049
1959
|
try {
|
|
2050
|
-
ageMs = now - (0,
|
|
1960
|
+
ageMs = now - (0, import_node_fs14.statSync)(path).mtimeMs;
|
|
2051
1961
|
} catch {
|
|
2052
1962
|
continue;
|
|
2053
1963
|
}
|
|
@@ -2057,7 +1967,7 @@ function reapScratch(context, dryRun) {
|
|
|
2057
1967
|
continue;
|
|
2058
1968
|
}
|
|
2059
1969
|
try {
|
|
2060
|
-
(0,
|
|
1970
|
+
(0, import_node_fs14.rmSync)(path, { force: true });
|
|
2061
1971
|
reaped += 1;
|
|
2062
1972
|
} catch (error) {
|
|
2063
1973
|
failures.push(`${entry.name}: ${error?.code ?? error?.message ?? error}`);
|
|
@@ -2175,11 +2085,19 @@ function reconcile(options) {
|
|
|
2175
2085
|
say(`target v${summary.target} (${summary.gate})`);
|
|
2176
2086
|
options.onTargetResolved?.(summary.target, summary.gate);
|
|
2177
2087
|
say("updater: self-bootstrap");
|
|
2178
|
-
const self =
|
|
2088
|
+
const self = npmGlobalArm({
|
|
2089
|
+
surface: "updater",
|
|
2090
|
+
packageName: "@mutmutco/hub",
|
|
2091
|
+
distRel: "dist/index.cjs",
|
|
2092
|
+
target: summary.target,
|
|
2093
|
+
dryRun: options.dryRun,
|
|
2094
|
+
paths,
|
|
2095
|
+
env
|
|
2096
|
+
});
|
|
2179
2097
|
summary.updater = self;
|
|
2180
2098
|
recordArm(self);
|
|
2181
2099
|
say(`updater: ${self.verdict}${self.detail ? " \u2014 " + self.detail : ""}`);
|
|
2182
|
-
const handoffDist =
|
|
2100
|
+
const handoffDist = globalDist(env, "@mutmutco/hub", "dist/index.cjs");
|
|
2183
2101
|
if (shouldHandOff(self, { dryRun: options.dryRun, dist: handoffDist, guard: env.MMI_UPDATER_REEXEC })) {
|
|
2184
2102
|
summary.reexec = { dist: handoffDist, target: summary.target, from: self.from };
|
|
2185
2103
|
journal({ kind: "reexec", surface: "updater", from: self.from, to: summary.target, verdict: "ok", detail: `handing off to ${handoffDist} \u2014 the remaining surfaces converge under ${summary.target}` });
|
|
@@ -2187,7 +2105,15 @@ function reconcile(options) {
|
|
|
2187
2105
|
return summary;
|
|
2188
2106
|
}
|
|
2189
2107
|
say(`cli: converging to v${summary.target}`);
|
|
2190
|
-
const cli =
|
|
2108
|
+
const cli = npmGlobalArm({
|
|
2109
|
+
surface: "cli",
|
|
2110
|
+
packageName: "@mutmutco/cli",
|
|
2111
|
+
distRel: "dist/index.cjs",
|
|
2112
|
+
target: summary.target,
|
|
2113
|
+
dryRun: options.dryRun,
|
|
2114
|
+
paths,
|
|
2115
|
+
env
|
|
2116
|
+
});
|
|
2191
2117
|
summary.cli = cli;
|
|
2192
2118
|
recordArm(cli);
|
|
2193
2119
|
say(`cli: ${cli.verdict}${cli.detail ? " \u2014 " + cli.detail : ""}`);
|
|
@@ -2231,10 +2157,10 @@ function reconcile(options) {
|
|
|
2231
2157
|
}
|
|
2232
2158
|
|
|
2233
2159
|
// src/scheduler.ts
|
|
2234
|
-
var
|
|
2235
|
-
var
|
|
2160
|
+
var import_node_child_process8 = require("node:child_process");
|
|
2161
|
+
var import_node_fs15 = require("node:fs");
|
|
2236
2162
|
var import_node_os6 = require("node:os");
|
|
2237
|
-
var
|
|
2163
|
+
var import_node_path12 = require("node:path");
|
|
2238
2164
|
var TASK_NAME = "MMI Fleet Updater";
|
|
2239
2165
|
var LAUNCHER_FILE = "reconcile-hidden.vbs";
|
|
2240
2166
|
var RUN_BOUND = "PT15M";
|
|
@@ -2250,15 +2176,15 @@ var SYSTEMD_UNIT = "mmi-hub-updater";
|
|
|
2250
2176
|
var SYSTEMD_SERVICE_NAME = `${SYSTEMD_UNIT}.service`;
|
|
2251
2177
|
var SYSTEMD_TIMER_NAME = `${SYSTEMD_UNIT}.timer`;
|
|
2252
2178
|
function schtasks(args) {
|
|
2253
|
-
const result = (0,
|
|
2179
|
+
const result = (0, import_node_child_process8.spawnSync)("schtasks.exe", args, { encoding: "utf8", windowsHide: true });
|
|
2254
2180
|
return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
|
|
2255
2181
|
}
|
|
2256
2182
|
function launchctl(args) {
|
|
2257
|
-
const result = (0,
|
|
2183
|
+
const result = (0, import_node_child_process8.spawnSync)("launchctl", args, { encoding: "utf8", windowsHide: true });
|
|
2258
2184
|
return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
|
|
2259
2185
|
}
|
|
2260
2186
|
function systemctlUser(args) {
|
|
2261
|
-
const result = (0,
|
|
2187
|
+
const result = (0, import_node_child_process8.spawnSync)("systemctl", ["--user", ...args], { encoding: "utf8", windowsHide: true });
|
|
2262
2188
|
return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
|
|
2263
2189
|
}
|
|
2264
2190
|
function uid() {
|
|
@@ -2283,7 +2209,7 @@ WScript.Quit code
|
|
|
2283
2209
|
`;
|
|
2284
2210
|
}
|
|
2285
2211
|
function loginPath(fallback = process.env.PATH ?? "") {
|
|
2286
|
-
const probe = (0,
|
|
2212
|
+
const probe = (0, import_node_child_process8.spawnSync)("/bin/sh", ["-lc", 'printf %s "$PATH"'], { encoding: "utf8", windowsHide: true, timeout: 1e4 });
|
|
2287
2213
|
const captured = (probe.status === 0 ? probe.stdout ?? "" : "").trim();
|
|
2288
2214
|
const source = captured || fallback;
|
|
2289
2215
|
const durable = source.split(":").filter((entry) => entry && !/^\/(?:private\/)?(?:tmp|var\/folders)\//.test(entry));
|
|
@@ -2392,7 +2318,7 @@ WantedBy=timers.target
|
|
|
2392
2318
|
}
|
|
2393
2319
|
function readFileOrNull(path) {
|
|
2394
2320
|
try {
|
|
2395
|
-
return (0,
|
|
2321
|
+
return (0, import_node_fs15.readFileSync)(path, "utf8");
|
|
2396
2322
|
} catch {
|
|
2397
2323
|
return null;
|
|
2398
2324
|
}
|
|
@@ -2404,7 +2330,7 @@ function elevatedRelaunch(mode, env) {
|
|
|
2404
2330
|
if (env.MMI_UPDATER_ELEVATED_RETRY === "1") return false;
|
|
2405
2331
|
const self = process.argv[1];
|
|
2406
2332
|
const command = self ? `"${process.execPath}" "${self}" autoupdate ${mode}` : `mmi-hub autoupdate ${mode}`;
|
|
2407
|
-
const relaunch = (0,
|
|
2333
|
+
const relaunch = (0, import_node_child_process8.spawnSync)(
|
|
2408
2334
|
"powershell.exe",
|
|
2409
2335
|
["-NoProfile", "-Command", `Start-Process -Verb RunAs -Wait -WindowStyle Hidden -FilePath 'cmd.exe' -ArgumentList '/c',${psQuote(`set MMI_UPDATER_ELEVATED_RETRY=1&& ${command}`)}`],
|
|
2410
2336
|
{ encoding: "utf8", windowsHide: true }
|
|
@@ -2415,7 +2341,7 @@ var NEVER_RUN_RESULT = 267011;
|
|
|
2415
2341
|
var NEVER_RUN_YEAR = 1999;
|
|
2416
2342
|
var EMPTY_RUN_INFO = { lastRunTime: null, lastTaskResult: null, nextRunTime: null };
|
|
2417
2343
|
function taskRunInfo() {
|
|
2418
|
-
const result = (0,
|
|
2344
|
+
const result = (0, import_node_child_process8.spawnSync)(
|
|
2419
2345
|
"powershell.exe",
|
|
2420
2346
|
["-NoProfile", "-Command", `$stamp = { param($t) if ($t) { $t.ToString('yyyy-MM-dd HH:mm:ss') } }; Get-ScheduledTaskInfo -TaskName '${TASK_NAME}' | Select-Object @{n='LastRunTime';e={& $stamp $_.LastRunTime}},LastTaskResult,@{n='NextRunTime';e={& $stamp $_.NextRunTime}} | ConvertTo-Json -Compress`],
|
|
2421
2347
|
{ encoding: "utf8", windowsHide: true, timeout: 3e4 }
|
|
@@ -2441,7 +2367,7 @@ function windowsSchedulerStatus(env) {
|
|
|
2441
2367
|
return { ok: false, supported: true, enabled: false, ...EMPTY_RUN_INFO, detail: `"${TASK_NAME}" is not registered \u2014 run \`mmi-hub autoupdate on\`` };
|
|
2442
2368
|
}
|
|
2443
2369
|
const hubDist = globalDist(env, "@mutmutco/hub", "dist/index.cjs");
|
|
2444
|
-
const launcherPath = (0,
|
|
2370
|
+
const launcherPath = (0, import_node_path12.join)(statePaths(env).root, LAUNCHER_FILE);
|
|
2445
2371
|
const info = taskRunInfo();
|
|
2446
2372
|
const runInfo = info ?? EMPTY_RUN_INFO;
|
|
2447
2373
|
const fail = (detail) => ({ ok: false, supported: true, enabled: true, ...runInfo, detail });
|
|
@@ -2466,13 +2392,13 @@ function windowsSchedulerStatus(env) {
|
|
|
2466
2392
|
return { ok: true, supported: true, enabled: true, ...runInfo, detail: `"${TASK_NAME}" registered ${CADENCE}; ${points}; ${ran}; next ${runInfo.nextRunTime ?? "unscheduled"}` };
|
|
2467
2393
|
}
|
|
2468
2394
|
function darwinSchedulerStatus(env) {
|
|
2469
|
-
const plistPath = (0,
|
|
2395
|
+
const plistPath = (0, import_node_path12.join)((0, import_node_os6.homedir)(), "Library", "LaunchAgents", LAUNCHD_PLIST_NAME);
|
|
2470
2396
|
const plist = readFileOrNull(plistPath);
|
|
2471
2397
|
if (!plist) {
|
|
2472
2398
|
return { ok: false, supported: true, enabled: false, ...EMPTY_RUN_INFO, detail: `"${LAUNCHD_LABEL}" is not registered \u2014 run \`mmi-hub autoupdate on\`` };
|
|
2473
2399
|
}
|
|
2474
2400
|
const hubDist = globalDist(env, "@mutmutco/hub", "dist/index.cjs");
|
|
2475
|
-
const launcherPath = (0,
|
|
2401
|
+
const launcherPath = (0, import_node_path12.join)(statePaths(env).root, POSIX_LAUNCHER_FILE);
|
|
2476
2402
|
const fail = (detail) => ({ ok: false, supported: true, enabled: true, ...EMPTY_RUN_INFO, detail });
|
|
2477
2403
|
if (!plist.includes(launcherPath)) {
|
|
2478
2404
|
return fail(`"${LAUNCHD_LABEL}" does not run the reconcile launcher ${launcherPath} \u2014 run \`mmi-hub autoupdate on\``);
|
|
@@ -2492,15 +2418,15 @@ function darwinSchedulerStatus(env) {
|
|
|
2492
2418
|
return { ok: true, supported: true, enabled: true, ...EMPTY_RUN_INFO, detail: `"${LAUNCHD_LABEL}" registered ${POSIX_CADENCE}; ${points}` };
|
|
2493
2419
|
}
|
|
2494
2420
|
function linuxSchedulerStatus(env) {
|
|
2495
|
-
const unitDir = (0,
|
|
2496
|
-
const timerPath = (0,
|
|
2421
|
+
const unitDir = (0, import_node_path12.join)((0, import_node_os6.homedir)(), ".config", "systemd", "user");
|
|
2422
|
+
const timerPath = (0, import_node_path12.join)(unitDir, SYSTEMD_TIMER_NAME);
|
|
2497
2423
|
const timer = readFileOrNull(timerPath);
|
|
2498
|
-
const service = readFileOrNull((0,
|
|
2424
|
+
const service = readFileOrNull((0, import_node_path12.join)(unitDir, SYSTEMD_SERVICE_NAME));
|
|
2499
2425
|
if (!timer || !service) {
|
|
2500
2426
|
return { ok: false, supported: true, enabled: false, ...EMPTY_RUN_INFO, detail: `"${SYSTEMD_TIMER_NAME}" is not registered \u2014 run \`mmi-hub autoupdate on\`` };
|
|
2501
2427
|
}
|
|
2502
2428
|
const hubDist = globalDist(env, "@mutmutco/hub", "dist/index.cjs");
|
|
2503
|
-
const launcherPath = (0,
|
|
2429
|
+
const launcherPath = (0, import_node_path12.join)(statePaths(env).root, POSIX_LAUNCHER_FILE);
|
|
2504
2430
|
const fail = (detail) => ({ ok: false, supported: true, enabled: true, ...EMPTY_RUN_INFO, detail });
|
|
2505
2431
|
if (!service.includes(launcherPath)) {
|
|
2506
2432
|
return fail(`"${SYSTEMD_SERVICE_NAME}" does not run the reconcile launcher ${launcherPath} \u2014 run \`mmi-hub autoupdate on\``);
|
|
@@ -2539,10 +2465,10 @@ function installWindowsTask(env) {
|
|
|
2539
2465
|
const paths = ensureState(env);
|
|
2540
2466
|
const userId = `${env.USERDOMAIN || env.COMPUTERNAME || ""}\\${env.USERNAME || ""}`.replace(/^\\/, "");
|
|
2541
2467
|
if (!env.USERNAME) return { ok: false, detail: "cannot resolve the registering user (USERNAME unset) \u2014 the task principal must be explicit" };
|
|
2542
|
-
const launcherPath = (0,
|
|
2543
|
-
(0,
|
|
2544
|
-
const xmlPath = (0,
|
|
2545
|
-
(0,
|
|
2468
|
+
const launcherPath = (0, import_node_path12.join)(paths.root, LAUNCHER_FILE);
|
|
2469
|
+
(0, import_node_fs15.writeFileSync)(launcherPath, launcherVbs(process.execPath, hubDist), "utf8");
|
|
2470
|
+
const xmlPath = (0, import_node_path12.join)(paths.root, "task.xml");
|
|
2471
|
+
(0, import_node_fs15.writeFileSync)(xmlPath, "\uFEFF" + taskXml(launcherPath, paths.root, userId, localStartBoundary(/* @__PURE__ */ new Date())), "utf16le");
|
|
2546
2472
|
const create = schtasks(["/create", "/tn", TASK_NAME, "/xml", xmlPath, "/f"]);
|
|
2547
2473
|
if (create.status !== 0) {
|
|
2548
2474
|
const denied = /access is denied/i.test(create.stderr + create.stdout);
|
|
@@ -2558,11 +2484,11 @@ function refreshPosixLauncher(env) {
|
|
|
2558
2484
|
const hubDist = globalDist(env, "@mutmutco/hub", "dist/index.cjs");
|
|
2559
2485
|
if (!hubDist) return null;
|
|
2560
2486
|
const paths = ensureState(env);
|
|
2561
|
-
const launcherPath = (0,
|
|
2487
|
+
const launcherPath = (0, import_node_path12.join)(paths.root, POSIX_LAUNCHER_FILE);
|
|
2562
2488
|
const desired = launcherSh(process.execPath, hubDist);
|
|
2563
2489
|
if (readFileOrNull(launcherPath) === desired) return null;
|
|
2564
|
-
(0,
|
|
2565
|
-
(0,
|
|
2490
|
+
(0, import_node_fs15.writeFileSync)(launcherPath, desired, "utf8");
|
|
2491
|
+
(0, import_node_fs15.chmodSync)(launcherPath, 493);
|
|
2566
2492
|
return launcherPath;
|
|
2567
2493
|
}
|
|
2568
2494
|
function installLaunchd(env) {
|
|
@@ -2574,13 +2500,13 @@ function installLaunchd(env) {
|
|
|
2574
2500
|
const hubDist = globalDist(env, "@mutmutco/hub", "dist/index.cjs");
|
|
2575
2501
|
if (!hubDist) return { ok: false, detail: "cannot resolve the global @mutmutco/hub dist \u2014 run `npm install -g @mutmutco/hub` first" };
|
|
2576
2502
|
const paths = ensureState(env);
|
|
2577
|
-
const launcherPath = (0,
|
|
2578
|
-
(0,
|
|
2579
|
-
(0,
|
|
2580
|
-
const agentsDir = (0,
|
|
2581
|
-
(0,
|
|
2582
|
-
const plistPath = (0,
|
|
2583
|
-
(0,
|
|
2503
|
+
const launcherPath = (0, import_node_path12.join)(paths.root, POSIX_LAUNCHER_FILE);
|
|
2504
|
+
(0, import_node_fs15.writeFileSync)(launcherPath, launcherSh(process.execPath, hubDist), "utf8");
|
|
2505
|
+
(0, import_node_fs15.chmodSync)(launcherPath, 493);
|
|
2506
|
+
const agentsDir = (0, import_node_path12.join)((0, import_node_os6.homedir)(), "Library", "LaunchAgents");
|
|
2507
|
+
(0, import_node_fs15.mkdirSync)(agentsDir, { recursive: true });
|
|
2508
|
+
const plistPath = (0, import_node_path12.join)(agentsDir, LAUNCHD_PLIST_NAME);
|
|
2509
|
+
(0, import_node_fs15.writeFileSync)(plistPath, launchdPlist(launcherPath, paths.root), "utf8");
|
|
2584
2510
|
launchctl(["bootout", `gui/${uid()}/${LAUNCHD_LABEL}`]);
|
|
2585
2511
|
const load = launchctl(["bootstrap", `gui/${uid()}`, plistPath]);
|
|
2586
2512
|
if (load.status !== 0) {
|
|
@@ -2598,13 +2524,13 @@ function installSystemdTimer(env) {
|
|
|
2598
2524
|
const hubDist = globalDist(env, "@mutmutco/hub", "dist/index.cjs");
|
|
2599
2525
|
if (!hubDist) return { ok: false, detail: "cannot resolve the global @mutmutco/hub dist \u2014 run `npm install -g @mutmutco/hub` first" };
|
|
2600
2526
|
const paths = ensureState(env);
|
|
2601
|
-
const launcherPath = (0,
|
|
2602
|
-
(0,
|
|
2603
|
-
(0,
|
|
2604
|
-
const unitDir = (0,
|
|
2605
|
-
(0,
|
|
2606
|
-
(0,
|
|
2607
|
-
(0,
|
|
2527
|
+
const launcherPath = (0, import_node_path12.join)(paths.root, POSIX_LAUNCHER_FILE);
|
|
2528
|
+
(0, import_node_fs15.writeFileSync)(launcherPath, launcherSh(process.execPath, hubDist), "utf8");
|
|
2529
|
+
(0, import_node_fs15.chmodSync)(launcherPath, 493);
|
|
2530
|
+
const unitDir = (0, import_node_path12.join)((0, import_node_os6.homedir)(), ".config", "systemd", "user");
|
|
2531
|
+
(0, import_node_fs15.mkdirSync)(unitDir, { recursive: true });
|
|
2532
|
+
(0, import_node_fs15.writeFileSync)((0, import_node_path12.join)(unitDir, SYSTEMD_SERVICE_NAME), systemdServiceUnit(launcherPath, paths.root), "utf8");
|
|
2533
|
+
(0, import_node_fs15.writeFileSync)((0, import_node_path12.join)(unitDir, SYSTEMD_TIMER_NAME), systemdTimerUnit(), "utf8");
|
|
2608
2534
|
const reload = systemctlUser(["daemon-reload"]);
|
|
2609
2535
|
if (reload.status !== 0) {
|
|
2610
2536
|
return { ok: false, detail: `systemctl --user daemon-reload failed: ${(reload.stderr || reload.stdout).trim() || "unknown error \u2014 is a user systemd instance running?"}` };
|
|
@@ -2642,28 +2568,28 @@ function uninstallWindowsTask(env) {
|
|
|
2642
2568
|
return { ok: true, detail: `automatic updates off \u2014 removed "${TASK_NAME}"; installed tooling was kept` };
|
|
2643
2569
|
}
|
|
2644
2570
|
function uninstallLaunchd(env) {
|
|
2645
|
-
const plistPath = (0,
|
|
2571
|
+
const plistPath = (0, import_node_path12.join)((0, import_node_os6.homedir)(), "Library", "LaunchAgents", LAUNCHD_PLIST_NAME);
|
|
2646
2572
|
const boot = launchctl(["bootout", `gui/${uid()}/${LAUNCHD_LABEL}`]);
|
|
2647
|
-
if (boot.status !== 0 && (0,
|
|
2573
|
+
if (boot.status !== 0 && (0, import_node_fs15.existsSync)(plistPath)) {
|
|
2648
2574
|
return { ok: false, detail: `launchctl bootout failed: ${(boot.stderr || boot.stdout).trim() || "unknown error"}` };
|
|
2649
2575
|
}
|
|
2650
2576
|
try {
|
|
2651
|
-
(0,
|
|
2577
|
+
(0, import_node_fs15.rmSync)(plistPath, { force: true });
|
|
2652
2578
|
} catch {
|
|
2653
2579
|
}
|
|
2654
2580
|
dropLauncher(env, POSIX_LAUNCHER_FILE);
|
|
2655
2581
|
return { ok: true, detail: `automatic updates off \u2014 removed "${LAUNCHD_LABEL}"; installed tooling was kept` };
|
|
2656
2582
|
}
|
|
2657
2583
|
function uninstallSystemdTimer(env) {
|
|
2658
|
-
const unitDir = (0,
|
|
2659
|
-
const timerPath = (0,
|
|
2584
|
+
const unitDir = (0, import_node_path12.join)((0, import_node_os6.homedir)(), ".config", "systemd", "user");
|
|
2585
|
+
const timerPath = (0, import_node_path12.join)(unitDir, SYSTEMD_TIMER_NAME);
|
|
2660
2586
|
const disable = systemctlUser(["disable", "--now", SYSTEMD_TIMER_NAME]);
|
|
2661
|
-
if (disable.status !== 0 && (0,
|
|
2587
|
+
if (disable.status !== 0 && (0, import_node_fs15.existsSync)(timerPath)) {
|
|
2662
2588
|
return { ok: false, detail: `systemctl --user disable --now ${SYSTEMD_TIMER_NAME} failed: ${(disable.stderr || disable.stdout).trim() || "unknown error"}` };
|
|
2663
2589
|
}
|
|
2664
2590
|
try {
|
|
2665
|
-
(0,
|
|
2666
|
-
(0,
|
|
2591
|
+
(0, import_node_fs15.rmSync)(timerPath, { force: true });
|
|
2592
|
+
(0, import_node_fs15.rmSync)((0, import_node_path12.join)(unitDir, SYSTEMD_SERVICE_NAME), { force: true });
|
|
2667
2593
|
} catch {
|
|
2668
2594
|
}
|
|
2669
2595
|
systemctlUser(["daemon-reload"]);
|
|
@@ -2678,16 +2604,16 @@ function uninstallTask(env = process.env) {
|
|
|
2678
2604
|
}
|
|
2679
2605
|
function dropLauncher(env, file) {
|
|
2680
2606
|
try {
|
|
2681
|
-
(0,
|
|
2607
|
+
(0, import_node_fs15.rmSync)((0, import_node_path12.join)(statePaths(env).root, file), { force: true });
|
|
2682
2608
|
} catch {
|
|
2683
2609
|
}
|
|
2684
2610
|
}
|
|
2685
2611
|
|
|
2686
2612
|
// src/status.ts
|
|
2687
|
-
var
|
|
2613
|
+
var import_node_fs16 = require("node:fs");
|
|
2688
2614
|
function readJournal(path) {
|
|
2689
2615
|
try {
|
|
2690
|
-
const events = (0,
|
|
2616
|
+
const events = (0, import_node_fs16.readFileSync)(path, "utf8").split("\n").filter((line) => line.trim()).map((line) => {
|
|
2691
2617
|
try {
|
|
2692
2618
|
return JSON.parse(line);
|
|
2693
2619
|
} catch {
|
|
@@ -2718,8 +2644,14 @@ function hubStatus(hubVersion, env = process.env, invokedPath = process.argv[1])
|
|
|
2718
2644
|
const hubDist = globalDist(env, "@mutmutco/hub", "dist/index.cjs");
|
|
2719
2645
|
const globalHubVersion = hubDist ? probeVersion(hubDist) : null;
|
|
2720
2646
|
actual.set("hub", globalHubVersion ? { version: globalHubVersion, staged: null, location: hubDist, detail: "absolute global Hub binary" } : { version: hubVersion, staged: null, location: invokedPath ?? null, detail: "currently executing Hub bundle (global package location unavailable)" });
|
|
2721
|
-
const
|
|
2722
|
-
|
|
2647
|
+
const cliDist = globalDist(env, "@mutmutco/cli", "dist/index.cjs");
|
|
2648
|
+
const cliVersion = cliDist ? probeVersion(cliDist) : null;
|
|
2649
|
+
actual.set("cli", {
|
|
2650
|
+
version: cliVersion,
|
|
2651
|
+
staged: null,
|
|
2652
|
+
location: cliDist,
|
|
2653
|
+
detail: cliVersion ? `absolute binary reported ${cliVersion}` : "global @mutmutco/cli binary was not readable"
|
|
2654
|
+
});
|
|
2723
2655
|
for (const surface of probes.filter((probe) => probe.present)) {
|
|
2724
2656
|
if (surface.id === "claude") {
|
|
2725
2657
|
const probe = probeNativeHostInstallation("claude", env);
|
|
@@ -3087,14 +3019,14 @@ function startSpinner(enabled) {
|
|
|
3087
3019
|
}
|
|
3088
3020
|
|
|
3089
3021
|
// src/stamp.ts
|
|
3090
|
-
var
|
|
3022
|
+
var import_node_fs17 = require("node:fs");
|
|
3091
3023
|
var ALWAYS_ARMED = ["cli", "updater"];
|
|
3092
3024
|
var PLUGIN_ARMS = /* @__PURE__ */ new Set(["claude", "codex", "jervcode", "cursor", "hermes"]);
|
|
3093
3025
|
function fleetStamp(env = process.env) {
|
|
3094
3026
|
const paths = statePaths(env);
|
|
3095
3027
|
let events = [];
|
|
3096
3028
|
try {
|
|
3097
|
-
events = (0,
|
|
3029
|
+
events = (0, import_node_fs17.readFileSync)(paths.journalPath, "utf8").split("\n").filter((line) => line.trim()).map((line) => {
|
|
3098
3030
|
try {
|
|
3099
3031
|
return JSON.parse(line);
|
|
3100
3032
|
} catch {
|
|
@@ -3144,7 +3076,7 @@ function fleetStamp(env = process.env) {
|
|
|
3144
3076
|
// src/index.ts
|
|
3145
3077
|
function ownVersion() {
|
|
3146
3078
|
try {
|
|
3147
|
-
return JSON.parse((0,
|
|
3079
|
+
return JSON.parse((0, import_node_fs18.readFileSync)((0, import_node_path13.join)(__dirname, "..", "package.json"), "utf8")).version;
|
|
3148
3080
|
} catch {
|
|
3149
3081
|
return "0.0.0";
|
|
3150
3082
|
}
|
|
@@ -3183,7 +3115,7 @@ function handOff(reexec, args, json) {
|
|
|
3183
3115
|
|
|
3184
3116
|
`);
|
|
3185
3117
|
}
|
|
3186
|
-
const child = (0,
|
|
3118
|
+
const child = (0, import_node_child_process9.spawnSync)(process.execPath, [reexec.dist, ...args], {
|
|
3187
3119
|
stdio: "inherit",
|
|
3188
3120
|
windowsHide: true,
|
|
3189
3121
|
env: { ...process.env, MMI_UPDATER_REEXEC: "1" }
|
|
@@ -3346,7 +3278,6 @@ if (typeof require !== "undefined" && require.main === module) {
|
|
|
3346
3278
|
retryHint,
|
|
3347
3279
|
schedulePendingWatcher,
|
|
3348
3280
|
schedulerStatus,
|
|
3349
|
-
selfDist,
|
|
3350
3281
|
sharedDocLockPath,
|
|
3351
3282
|
sharedDocLockTimeoutReceipt,
|
|
3352
3283
|
shouldHandOff,
|
package/package.json
CHANGED