@serviceme/devtools-core 0.3.4 → 0.4.1

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.mjs CHANGED
@@ -1994,6 +1994,7 @@ var FILE_MODE = 384;
1994
1994
  var LOCK_DIR_MODE = 448;
1995
1995
  var DEFAULT_LOCK_TIMEOUT_MS = 5e3;
1996
1996
  var DEFAULT_LOCK_RETRY_MS = 25;
1997
+ var LOCK_STALE_GRACE_MS = 200;
1997
1998
  var TMP_SUFFIX = ".tmp";
1998
1999
  var FsIdentityFileBackend = class {
1999
2000
  async exists(filePath) {
@@ -2098,14 +2099,20 @@ var FileLock = class {
2098
2099
  }
2099
2100
  }
2100
2101
  async isStaleLock() {
2102
+ let pidStr;
2101
2103
  try {
2102
- const pidStr = await fsp.readFile(this.pidFilePath, "utf8");
2103
- const pid = Number.parseInt(pidStr.trim(), 10);
2104
- if (!Number.isFinite(pid) || pid <= 0) return true;
2105
- return !isProcessAlive(pid);
2104
+ pidStr = await fsp.readFile(this.pidFilePath, "utf8");
2106
2105
  } catch {
2107
- return true;
2106
+ try {
2107
+ const stat7 = await fsp.stat(this.dirPath);
2108
+ return Date.now() - stat7.mtimeMs > LOCK_STALE_GRACE_MS;
2109
+ } catch {
2110
+ return false;
2111
+ }
2108
2112
  }
2113
+ const pid = Number.parseInt(pidStr.trim(), 10);
2114
+ if (!Number.isFinite(pid) || pid <= 0) return true;
2115
+ return !isProcessAlive(pid);
2109
2116
  }
2110
2117
  async release() {
2111
2118
  if (!this.acquired) return;
@@ -2405,9 +2412,9 @@ var DraftsStore = class {
2405
2412
  const dir = resolveDraftDir(kind, id);
2406
2413
  const manifestFilename = kind === "skill" ? "SKILL.md" : "AGENT.md";
2407
2414
  const manifestPath = path6.join(dir, manifestFilename);
2408
- let stat5;
2415
+ let stat7;
2409
2416
  try {
2410
- stat5 = await fs4.stat(manifestPath);
2417
+ stat7 = await fs4.stat(manifestPath);
2411
2418
  } catch {
2412
2419
  return null;
2413
2420
  }
@@ -2426,7 +2433,7 @@ var DraftsStore = class {
2426
2433
  dir,
2427
2434
  name,
2428
2435
  description,
2429
- modifiedAt: stat5.mtime.toISOString()
2436
+ modifiedAt: stat7.mtime.toISOString()
2430
2437
  };
2431
2438
  }
2432
2439
  /** Single draft detail (summary + all files). Throws when missing. */
@@ -4704,13 +4711,13 @@ var PidManager = class {
4704
4711
  fs13.writeFileSync(this.pidPath, String(pid), "utf-8");
4705
4712
  }
4706
4713
  readPid() {
4707
- let stat5;
4714
+ let stat7;
4708
4715
  try {
4709
- stat5 = fs13.statSync(this.pidPath);
4716
+ stat7 = fs13.statSync(this.pidPath);
4710
4717
  } catch {
4711
4718
  return null;
4712
4719
  }
4713
- if (!stat5.isFile()) return null;
4720
+ if (!stat7.isFile()) return null;
4714
4721
  const raw = fs13.readFileSync(this.pidPath, "utf-8").trim();
4715
4722
  const pid = Number.parseInt(raw, 10);
4716
4723
  return Number.isNaN(pid) ? null : pid;
@@ -6847,6 +6854,7 @@ var FILE_MODE2 = 384;
6847
6854
  var LOCK_DIR_MODE2 = 448;
6848
6855
  var DEFAULT_LOCK_TIMEOUT_MS2 = 5e3;
6849
6856
  var DEFAULT_LOCK_RETRY_MS2 = 25;
6857
+ var LOCK_STALE_GRACE_MS2 = 200;
6850
6858
  var TMP_SUFFIX2 = ".tmp";
6851
6859
  var WORKSPACE_TOOLBOX_RELATIVE_PATH = path25.join(".github", ".serviceme-toolbox.json");
6852
6860
  var LEGACY_WORKSPACE_TOOLBOX_FILENAME = ".ms-devtools-toolbox.json";
@@ -6865,6 +6873,9 @@ async function migrateLegacyWorkspaceToolboxFile(filePath) {
6865
6873
  }
6866
6874
  }
6867
6875
  var FsToolboxFileBackend = class {
6876
+ constructor() {
6877
+ this.maxBackupCount = 5;
6878
+ }
6868
6879
  async exists(filePath) {
6869
6880
  try {
6870
6881
  await fsp2.access(filePath);
@@ -6893,8 +6904,27 @@ var FsToolboxFileBackend = class {
6893
6904
  try {
6894
6905
  const backupPath = `${filePath}.corrupted.${Date.now()}.bak`;
6895
6906
  await fsp2.copyFile(filePath, backupPath);
6907
+ await this.purgeExcessBackups(filePath);
6908
+ } catch {
6909
+ }
6910
+ }
6911
+ async purgeExcessBackups(filePath) {
6912
+ const dir = path25.dirname(filePath);
6913
+ const base = path25.basename(filePath);
6914
+ let entries;
6915
+ try {
6916
+ entries = await fsp2.readdir(dir);
6896
6917
  } catch {
6918
+ return;
6897
6919
  }
6920
+ const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath: path25.join(dir, n) })).sort((a, b) => {
6921
+ return a.name.localeCompare(b.name);
6922
+ });
6923
+ const excess = backups.length - this.maxBackupCount;
6924
+ if (excess <= 0) return;
6925
+ await Promise.all(
6926
+ backups.slice(0, excess).map((b) => fsp2.rm(b.filePath).catch(() => void 0))
6927
+ );
6898
6928
  }
6899
6929
  async write(filePath, payload) {
6900
6930
  await fsp2.mkdir(path25.dirname(filePath), { recursive: true });
@@ -6968,14 +6998,20 @@ var ToolboxFileLock = class {
6968
6998
  }
6969
6999
  }
6970
7000
  async isStaleLock() {
7001
+ let pidStr;
6971
7002
  try {
6972
- const pidStr = await fsp2.readFile(this.pidFilePath, "utf8");
6973
- const pid = Number.parseInt(pidStr.trim(), 10);
6974
- if (!Number.isFinite(pid) || pid <= 0) return true;
6975
- return !isProcessAlive2(pid);
7003
+ pidStr = await fsp2.readFile(this.pidFilePath, "utf8");
6976
7004
  } catch {
6977
- return true;
7005
+ try {
7006
+ const stat7 = await fsp2.stat(this.dirPath);
7007
+ return Date.now() - stat7.mtimeMs > LOCK_STALE_GRACE_MS2;
7008
+ } catch {
7009
+ return false;
7010
+ }
6978
7011
  }
7012
+ const pid = Number.parseInt(pidStr.trim(), 10);
7013
+ if (!Number.isFinite(pid) || pid <= 0) return true;
7014
+ return !isProcessAlive2(pid);
6979
7015
  }
6980
7016
  async release() {
6981
7017
  if (!this.acquired) return;