@vtxmacro/cli 2026.9.69 → 2026.10.2

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.
@@ -243,9 +243,9 @@ import { fileURLToPath } from "node:url";
243
243
  import { randomBytes } from "node:crypto";
244
244
  import { spawn } from "node:child_process";
245
245
  import { constants } from "node:fs";
246
- import { lstat, mkdir, open, readFile, realpath, rename, rm } from "node:fs/promises";
246
+ import { lstat, mkdir, open, readdir, readFile, realpath, rename, rm } from "node:fs/promises";
247
247
  import { homedir } from "node:os";
248
- import { dirname, isAbsolute, join, relative, resolve, win32 as windowsPath } from "node:path";
248
+ import { basename, dirname, isAbsolute, join, relative, resolve, win32 as windowsPath } from "node:path";
249
249
  var InferenceHostProcessLockBusyError = class extends Error {
250
250
  constructor() {
251
251
  super("Another inference host process already owns this local host.");
@@ -785,10 +785,22 @@ var defaultProcessIdentity = async (pid) => {
785
785
  if (process.platform === "darwin") return await darwinProcessIdentity(pid);
786
786
  return null;
787
787
  };
788
+ var cachedCurrentProcessIdentity = null;
788
789
  var defaultCurrentProcessIdentity = async () => {
789
- const identity = await defaultProcessIdentity(process.pid);
790
- if (!identity) throw new Error("Inference host could not establish its process-start identity.");
791
- return identity;
790
+ if (!cachedCurrentProcessIdentity) {
791
+ cachedCurrentProcessIdentity = (async () => {
792
+ const identity = await defaultProcessIdentity(process.pid);
793
+ if (!identity) throw new Error("Inference host could not establish its process-start identity.");
794
+ return identity;
795
+ })();
796
+ }
797
+ const pending = cachedCurrentProcessIdentity;
798
+ try {
799
+ return await pending;
800
+ } catch (error) {
801
+ if (cachedCurrentProcessIdentity === pending) cachedCurrentProcessIdentity = null;
802
+ throw error;
803
+ }
792
804
  };
793
805
  var processLockObservationCache = /* @__PURE__ */ new Map();
794
806
  var inferenceProcessIdentitiesMatch = (stored, observed) => stored === observed;
@@ -937,150 +949,305 @@ async function writeAtomicInferencePrivateFile(path, contents, dependencies = {}
937
949
  throw error;
938
950
  }
939
951
  }
940
- async function acquireInferenceHostProcessLock(path, dependencies = {}) {
941
- const directory = dirname(path);
942
- await ensureInferencePrivateDirectory(directory);
943
- const ownerToken = randomBytes(24).toString("base64url");
944
- const observeIdentity = dependencies.processIdentity ?? defaultProcessIdentity;
945
- const currentBootIdentity = dependencies.currentBootIdentity ?? (() => readInferenceSystemBootIdentity());
946
- const isProcessAlive = dependencies.processAlive ?? ((pid) => {
952
+ var PROCESS_LOCK_PUBLICATION_GRACE_MS = 1e4;
953
+ var PROCESS_LOCK_MAX_RECORD_BYTES = 64 * 1024;
954
+ var PROCESS_LOCK_GUARD_ELECTION_LEASE_MS = 5e3;
955
+ var PROCESS_LOCK_GUARD_ELECTION_STALE_MS = 3e4;
956
+ var PROCESS_LOCK_GUARD_ELECTION_ATTEMPTS = 8;
957
+ var PROCESS_LOCK_GUARD_ELECTION_BACKOFF_MS = 10;
958
+ var PROCESS_LOCK_LABEL = "Inference host process lock";
959
+ var PROCESS_LOCK_GUARD_LABEL = "Inference host process lock reclamation guard";
960
+ var sameProcessLockFile = (left, right) => left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
961
+ var openExclusiveProcessLockFile = async (path) => {
962
+ try {
963
+ return await open(
964
+ path,
965
+ constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW,
966
+ 384
967
+ );
968
+ } catch (error) {
969
+ if (error.code === "EEXIST") return null;
970
+ throw error;
971
+ }
972
+ };
973
+ var inspectProcessLockFile = async (path, label, probes) => {
974
+ let before;
975
+ try {
976
+ before = await lstat(path);
977
+ } catch (error) {
978
+ if (error.code === "ENOENT") return { state: "missing" };
979
+ throw error;
980
+ }
981
+ if (before.isSymbolicLink() || !before.isFile() || process.platform !== "win32" && (before.mode & 63) !== 0 || typeof process.getuid === "function" && before.uid !== process.getuid()) {
982
+ throw new Error(`${label} is unsafe.`);
983
+ }
984
+ let handle;
985
+ try {
986
+ await secureExistingWindowsPath(path, "file");
987
+ handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
988
+ } catch (error) {
989
+ if (error.code === "ENOENT") return { state: "missing" };
990
+ throw error;
991
+ }
992
+ let stale = false;
993
+ try {
994
+ const opened = await handle.stat();
995
+ if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino) {
996
+ return { state: "busy" };
997
+ }
998
+ const raw = opened.size > PROCESS_LOCK_MAX_RECORD_BYTES ? "" : await handle.readFile("utf8");
999
+ const stat = await handle.stat();
1000
+ if (!sameProcessLockFile(opened, stat)) return { state: "busy" };
1001
+ let record = null;
947
1002
  try {
948
- process.kill(pid, 0);
949
- return true;
1003
+ const parsed = JSON.parse(raw);
1004
+ if (parsed && typeof parsed === "object") record = parsed;
1005
+ } catch {
1006
+ record = null;
1007
+ }
1008
+ if (!record || record.schema_version !== 2 || !Number.isSafeInteger(record.pid) || Number(record.pid) < 1 || typeof record.owner_token !== "string" || !record.owner_token || typeof record.process_identity !== "string" || !record.process_identity || typeof record.boot_identity !== "string" || !record.boot_identity) {
1009
+ if (Date.now() - stat.mtimeMs < PROCESS_LOCK_PUBLICATION_GRACE_MS) return { state: "busy" };
1010
+ stale = true;
1011
+ return { state: "stale", handle, stat };
1012
+ }
1013
+ const ownerPid = Number(record.pid);
1014
+ const storedIdentity = record.process_identity;
1015
+ const cacheKey = resolve(path);
1016
+ if (await probes.currentBootIdentity() === record.boot_identity && probes.isProcessAlive(ownerPid)) {
1017
+ const cached = processLockObservationCache.get(cacheKey);
1018
+ const observedIdentity = cached && cached.dev === stat.dev && cached.ino === stat.ino && cached.pid === ownerPid && cached.storedIdentity === storedIdentity && Date.now() - cached.observedAtMs < 5e3 ? cached.observedIdentity : await probes.observeIdentity(ownerPid);
1019
+ if (!observedIdentity) {
1020
+ throw new Error("Inference host could not verify the existing lock owner identity.");
1021
+ }
1022
+ processLockObservationCache.set(cacheKey, {
1023
+ dev: stat.dev,
1024
+ ino: stat.ino,
1025
+ pid: ownerPid,
1026
+ storedIdentity,
1027
+ observedIdentity,
1028
+ observedAtMs: Date.now()
1029
+ });
1030
+ if (inferenceProcessIdentitiesMatch(storedIdentity, observedIdentity)) {
1031
+ return { state: "busy" };
1032
+ }
1033
+ }
1034
+ stale = true;
1035
+ return { state: "stale", handle, stat };
1036
+ } finally {
1037
+ if (!stale) await handle.close().catch(() => void 0);
1038
+ }
1039
+ };
1040
+ var removeVerifiedStaleProcessLockFile = async (path, inspection, assertExclusive) => {
1041
+ try {
1042
+ await assertExclusive();
1043
+ let current;
1044
+ try {
1045
+ current = await lstat(path);
950
1046
  } catch (error) {
951
- if (error.code === "ESRCH") return false;
1047
+ if (error.code === "ENOENT") return;
952
1048
  throw error;
953
1049
  }
954
- });
955
- let handle;
956
- for (let acquisition = 0; acquisition < 2; acquisition += 1) {
1050
+ const opened = await inspection.handle.stat();
1051
+ if (current.isSymbolicLink() || !sameProcessLockFile(current, opened) || !sameProcessLockFile(opened, inspection.stat)) {
1052
+ throw new InferenceHostProcessLockBusyError();
1053
+ }
1054
+ if (process.platform === "win32") await inspection.handle.close();
957
1055
  try {
958
- handle = await open(
959
- path,
960
- constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW,
961
- 384
962
- );
963
- break;
1056
+ await rm(path);
964
1057
  } catch (error) {
965
- if (error.code !== "EEXIST") throw error;
966
- if (acquisition > 0) {
967
- throw new InferenceHostProcessLockBusyError();
1058
+ if (error.code !== "ENOENT") throw error;
1059
+ }
1060
+ } finally {
1061
+ await inspection.handle.close().catch(() => void 0);
1062
+ }
1063
+ processLockObservationCache.delete(resolve(path));
1064
+ invalidateWindowsAclCache(path);
1065
+ await syncInferenceDirectory(dirname(path));
1066
+ };
1067
+ var removeStaleProcessLockReclaimGuard = async (lockPath, guardPath, inspection) => {
1068
+ const directory = dirname(lockPath);
1069
+ const prefix = `${basename(lockPath)}.reclaim-ballot.`;
1070
+ try {
1071
+ for (let attempt = 0; attempt < PROCESS_LOCK_GUARD_ELECTION_ATTEMPTS; attempt += 1) {
1072
+ if (attempt > 0) {
1073
+ await new Promise((resolvePromise) => setTimeout(
1074
+ resolvePromise,
1075
+ Math.floor(Math.random() * PROCESS_LOCK_GUARD_ELECTION_BACKOFF_MS * 2 ** Math.min(attempt, 4))
1076
+ ));
1077
+ let current;
1078
+ try {
1079
+ current = await lstat(guardPath);
1080
+ } catch (error) {
1081
+ if (error.code === "ENOENT") return;
1082
+ throw error;
1083
+ }
1084
+ if (!sameProcessLockFile(current, inspection.stat)) return;
968
1085
  }
969
- let before;
1086
+ const ballotName = `${prefix}${randomBytes(16).toString("hex")}`;
1087
+ const ballotPath = join(directory, ballotName);
1088
+ const ballot = await openExclusiveProcessLockFile(ballotPath);
1089
+ if (!ballot) continue;
1090
+ await ballot.close();
1091
+ const electedAt = performance.now();
970
1092
  try {
971
- before = await lstat(path);
972
- } catch (error2) {
973
- if (error2.code === "ENOENT") continue;
974
- throw error2;
1093
+ let contested = false;
1094
+ for (const name of await readdir(directory)) {
1095
+ if (!name.startsWith(prefix) || name === ballotName) continue;
1096
+ const otherPath = join(directory, name);
1097
+ let other;
1098
+ try {
1099
+ other = await lstat(otherPath);
1100
+ } catch (error) {
1101
+ if (error.code === "ENOENT") continue;
1102
+ throw error;
1103
+ }
1104
+ if (!other.isDirectory() && Date.now() - other.mtimeMs > PROCESS_LOCK_GUARD_ELECTION_STALE_MS) {
1105
+ await rm(otherPath, { force: true });
1106
+ continue;
1107
+ }
1108
+ contested = true;
1109
+ }
1110
+ if (contested) continue;
1111
+ await removeVerifiedStaleProcessLockFile(guardPath, inspection, async () => {
1112
+ if (performance.now() - electedAt > PROCESS_LOCK_GUARD_ELECTION_LEASE_MS) {
1113
+ throw new InferenceHostProcessLockBusyError();
1114
+ }
1115
+ });
1116
+ return;
1117
+ } finally {
1118
+ await rm(ballotPath, { force: true }).catch(() => void 0);
975
1119
  }
976
- if (before.isSymbolicLink() || !before.isFile() || process.platform !== "win32" && (before.mode & 63) !== 0 || typeof process.getuid === "function" && before.uid !== process.getuid()) {
977
- throw new Error("Inference host process lock is unsafe.");
1120
+ }
1121
+ throw new InferenceHostProcessLockBusyError();
1122
+ } finally {
1123
+ await inspection.handle.close().catch(() => void 0);
1124
+ }
1125
+ };
1126
+ var acquireProcessLockReclaimGuard = async (lockPath, ownerRecord, probes) => {
1127
+ const guardPath = `${lockPath}.reclaim`;
1128
+ const guardToken = randomBytes(24).toString("base64url");
1129
+ for (let attempt = 0; attempt < 2; attempt += 1) {
1130
+ const handle = await openExclusiveProcessLockFile(guardPath);
1131
+ if (handle) {
1132
+ try {
1133
+ await handle.writeFile(ownerRecord(guardToken), "utf8");
1134
+ } catch (error) {
1135
+ await handle.close().catch(() => void 0);
1136
+ await rm(guardPath, { force: true }).catch(() => void 0);
1137
+ throw error;
978
1138
  }
979
- const reclaimPublicationResidue = async () => {
980
- let current2;
1139
+ await handle.close();
1140
+ await secureWindowsPrivatePath(guardPath, "file", "harden");
1141
+ const isHeld = async () => {
1142
+ let reader;
981
1143
  try {
982
- current2 = await lstat(path);
983
- } catch (error2) {
984
- if (error2.code === "ENOENT") return false;
985
- throw error2;
986
- }
987
- if (current2.isSymbolicLink() || !current2.isFile() || current2.dev !== before.dev || current2.ino !== before.ino || process.platform !== "win32" && (current2.mode & 63) !== 0 || typeof process.getuid === "function" && current2.uid !== process.getuid()) {
988
- throw new Error("Inference host process lock publication residue changed or is unsafe.");
1144
+ reader = await open(guardPath, constants.O_RDONLY | constants.O_NOFOLLOW);
1145
+ } catch (error) {
1146
+ if (error.code === "ENOENT") return false;
1147
+ throw error;
989
1148
  }
990
1149
  try {
991
- await rm(path);
992
- } catch (error2) {
993
- if (error2.code === "ENOENT") return false;
994
- throw error2;
1150
+ const [opened, current] = await Promise.all([reader.stat(), lstat(guardPath)]);
1151
+ if (current.isSymbolicLink() || current.dev !== opened.dev || current.ino !== opened.ino) {
1152
+ return false;
1153
+ }
1154
+ const parsed = JSON.parse(await reader.readFile("utf8"));
1155
+ return parsed.owner_token === guardToken;
1156
+ } catch {
1157
+ return false;
1158
+ } finally {
1159
+ await reader.close().catch(() => void 0);
1160
+ }
1161
+ };
1162
+ return {
1163
+ assertHeld: async () => {
1164
+ if (!await isHeld()) throw new InferenceHostProcessLockBusyError();
1165
+ },
1166
+ // Best effort: a guard left behind carries this live process identity
1167
+ // and becomes reclaimable once this process exits.
1168
+ release: async () => {
1169
+ try {
1170
+ if (!await isHeld()) return;
1171
+ await rm(guardPath);
1172
+ processLockObservationCache.delete(resolve(guardPath));
1173
+ invalidateWindowsAclCache(guardPath);
1174
+ } catch {
1175
+ }
995
1176
  }
996
- processLockObservationCache.delete(resolve(path));
997
- await syncInferenceDirectory(directory);
998
- return true;
999
1177
  };
1178
+ }
1179
+ if (attempt > 0) break;
1180
+ const inspection = await inspectProcessLockFile(guardPath, PROCESS_LOCK_GUARD_LABEL, probes);
1181
+ if (inspection.state === "busy") break;
1182
+ if (inspection.state === "stale") {
1183
+ await removeStaleProcessLockReclaimGuard(lockPath, guardPath, inspection);
1184
+ }
1185
+ }
1186
+ throw new InferenceHostProcessLockBusyError();
1187
+ };
1188
+ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
1189
+ const directory = dirname(path);
1190
+ await ensureInferencePrivateDirectory(directory);
1191
+ const ownerToken = randomBytes(24).toString("base64url");
1192
+ const probes = {
1193
+ currentBootIdentity: dependencies.currentBootIdentity ?? (() => readInferenceSystemBootIdentity()),
1194
+ observeIdentity: dependencies.processIdentity ?? defaultProcessIdentity,
1195
+ isProcessAlive: dependencies.processAlive ?? ((pid) => {
1000
1196
  try {
1001
- await secureExistingWindowsPath(path, "file");
1002
- } catch (error2) {
1003
- if (error2.code === "ENOENT") continue;
1004
- throw error2;
1197
+ process.kill(pid, 0);
1198
+ return true;
1199
+ } catch (error) {
1200
+ if (error.code === "ESRCH") return false;
1201
+ throw error;
1005
1202
  }
1006
- let staleOwner;
1203
+ })
1204
+ };
1205
+ const [ownerIdentity, bootIdentity] = await Promise.all([
1206
+ (dependencies.currentProcessIdentity ?? defaultCurrentProcessIdentity)(),
1207
+ probes.currentBootIdentity()
1208
+ ]);
1209
+ const ownerRecord = (token) => `${JSON.stringify({
1210
+ schema_version: 2,
1211
+ pid: process.pid,
1212
+ owner_token: token,
1213
+ process_identity: ownerIdentity,
1214
+ boot_identity: bootIdentity
1215
+ })}
1216
+ `;
1217
+ let handle = await openExclusiveProcessLockFile(path);
1218
+ if (!handle) {
1219
+ const inspection = await inspectProcessLockFile(path, PROCESS_LOCK_LABEL, probes);
1220
+ if (inspection.state === "busy") throw new InferenceHostProcessLockBusyError();
1221
+ if (inspection.state === "stale") {
1222
+ await inspection.handle.close();
1223
+ const guard = await acquireProcessLockReclaimGuard(path, ownerRecord, probes);
1007
1224
  try {
1008
- staleOwner = JSON.parse(await readFile(path, "utf8"));
1009
- } catch (error2) {
1010
- if (error2.code === "ENOENT") continue;
1011
- if (Date.now() - before.mtimeMs < 1e4) {
1012
- throw new InferenceHostProcessLockBusyError();
1013
- }
1014
- await reclaimPublicationResidue();
1015
- continue;
1016
- }
1017
- if (staleOwner.schema_version !== 2 || !Number.isSafeInteger(staleOwner.pid) || Number(staleOwner.pid) < 1 || typeof staleOwner.owner_token !== "string" || !staleOwner.owner_token || typeof staleOwner.process_identity !== "string" || !staleOwner.process_identity || typeof staleOwner.boot_identity !== "string" || !staleOwner.boot_identity) {
1018
- if (Date.now() - before.mtimeMs < 1e4) {
1019
- throw new InferenceHostProcessLockBusyError();
1020
- }
1021
- await reclaimPublicationResidue();
1022
- continue;
1023
- }
1024
- const stalePid = Number(staleOwner.pid);
1025
- const bootIdentity2 = await currentBootIdentity();
1026
- if (bootIdentity2 === staleOwner.boot_identity && isProcessAlive(stalePid)) {
1027
- const cached = processLockObservationCache.get(resolve(path));
1028
- const observedIdentity = cached && cached.dev === before.dev && cached.ino === before.ino && cached.pid === stalePid && cached.storedIdentity === staleOwner.process_identity && Date.now() - cached.observedAtMs < 5e3 ? cached.observedIdentity : await observeIdentity(stalePid);
1029
- if (!observedIdentity) {
1030
- throw new Error("Inference host could not verify the existing lock owner identity.");
1031
- }
1032
- processLockObservationCache.set(resolve(path), {
1033
- dev: before.dev,
1034
- ino: before.ino,
1035
- pid: stalePid,
1036
- storedIdentity: staleOwner.process_identity,
1037
- observedIdentity,
1038
- observedAtMs: Date.now()
1039
- });
1040
- if (inferenceProcessIdentitiesMatch(staleOwner.process_identity, observedIdentity)) {
1041
- throw new InferenceHostProcessLockBusyError();
1225
+ const guarded = await inspectProcessLockFile(path, PROCESS_LOCK_LABEL, probes);
1226
+ if (guarded.state === "busy") throw new InferenceHostProcessLockBusyError();
1227
+ if (guarded.state === "stale") {
1228
+ await removeVerifiedStaleProcessLockFile(path, guarded, guard.assertHeld);
1042
1229
  }
1230
+ handle = await openExclusiveProcessLockFile(path);
1231
+ } finally {
1232
+ await guard.release();
1043
1233
  }
1044
- let current;
1045
- try {
1046
- current = await lstat(path);
1047
- } catch (error2) {
1048
- if (error2.code === "ENOENT") continue;
1049
- throw error2;
1050
- }
1051
- if (current.dev !== before.dev || current.ino !== before.ino) {
1052
- throw new Error("Inference host process lock ownership changed.");
1053
- }
1054
- await rm(path);
1055
- processLockObservationCache.delete(resolve(path));
1056
- await syncInferenceDirectory(directory);
1234
+ } else {
1235
+ handle = await openExclusiveProcessLockFile(path);
1057
1236
  }
1237
+ if (!handle) throw new InferenceHostProcessLockBusyError();
1058
1238
  }
1059
- if (!handle) throw new Error("Inference host process lock could not be acquired.");
1060
- let ownerIdentity;
1061
- let bootIdentity;
1062
1239
  try {
1063
- [ownerIdentity, bootIdentity] = await Promise.all([
1064
- (dependencies.currentProcessIdentity ?? defaultCurrentProcessIdentity)(),
1065
- currentBootIdentity()
1066
- ]);
1240
+ await handle.writeFile(ownerRecord(ownerToken), "utf8");
1241
+ await handle.sync();
1067
1242
  } catch (error) {
1243
+ const created = await handle.stat().catch(() => null);
1068
1244
  await handle.close().catch(() => void 0);
1069
- await rm(path, { force: true }).catch(() => void 0);
1245
+ const current = await lstat(path).catch(() => null);
1246
+ if (created && current && current.dev === created.dev && current.ino === created.ino) {
1247
+ await rm(path, { force: true }).catch(() => void 0);
1248
+ }
1070
1249
  throw error;
1071
1250
  }
1072
- await handle.writeFile(
1073
- `${JSON.stringify({
1074
- schema_version: 2,
1075
- pid: process.pid,
1076
- owner_token: ownerToken,
1077
- process_identity: ownerIdentity,
1078
- boot_identity: bootIdentity
1079
- })}
1080
- `,
1081
- "utf8"
1082
- );
1083
- await handle.sync();
1084
1251
  await handle.close();
1085
1252
  await secureWindowsPrivatePath(path, "file", "harden");
1086
1253
  await syncInferenceDirectory(directory);