@hybridlabor-api/aos 4.6.3 → 4.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/installer.js +55 -7
  2. package/package.json +1 -1
package/installer.js CHANGED
@@ -1072,6 +1072,15 @@ function moveIfExists(src, dest, label) {
1072
1072
  }
1073
1073
  }
1074
1074
 
1075
+ function safeRmDirSync(dirPath, maxRetries = 5, retryDelay = 200) {
1076
+ if (!dirPath || !fs.existsSync(dirPath)) return;
1077
+ try {
1078
+ fs.rmSync(dirPath, { recursive: true, force: true, maxRetries, retryDelay });
1079
+ } catch (err) {
1080
+ logDebug(err, `safeRmDirSync: ${dirPath}`);
1081
+ }
1082
+ }
1083
+
1075
1084
  function copyDirRecursiveSync(source, target, excludeList = [], manifest = null, knownSourceHashes = null) {
1076
1085
  // Fall back to session-level state when no explicit manifest is passed.
1077
1086
  const mfst = manifest !== null ? manifest : _sessionManifest;
@@ -1722,7 +1731,21 @@ function downloadOrUpdateModule(pkgName, targetDir, displayName) {
1722
1731
  // kept until the swap succeeds, and restored if it does not.
1723
1732
  const stagingDir = `${targetDir}.incoming-${timestamp}`;
1724
1733
  const retiredDir = `${targetDir}.previous-${timestamp}`;
1725
- fs.rmSync(stagingDir, { recursive: true, force: true });
1734
+
1735
+ // Clean up any stale staging or retired directories left behind by earlier interrupted runs
1736
+ try {
1737
+ const parentDir = path.dirname(targetDir);
1738
+ const baseName = path.basename(targetDir);
1739
+ if (fs.existsSync(parentDir)) {
1740
+ for (const item of fs.readdirSync(parentDir)) {
1741
+ if (item.startsWith(`${baseName}.incoming-`) || item.startsWith(`${baseName}.previous-`)) {
1742
+ safeRmDirSync(path.join(parentDir, item));
1743
+ }
1744
+ }
1745
+ }
1746
+ } catch (_) {}
1747
+
1748
+ safeRmDirSync(stagingDir);
1726
1749
  fs.mkdirSync(stagingDir, { recursive: true });
1727
1750
  execSync(`tar -xzf "${path.join(targetDir, tarball)}" --strip-components=1 -C "${stagingDir}"`, { stdio: 'ignore' });
1728
1751
  fs.unlinkSync(path.join(targetDir, tarball));
@@ -1733,7 +1756,11 @@ function downloadOrUpdateModule(pkgName, targetDir, displayName) {
1733
1756
  for (const keep of PRESERVE) {
1734
1757
  const from = path.join(targetDir, keep);
1735
1758
  if (fs.existsSync(from) && !fs.existsSync(path.join(stagingDir, keep))) {
1736
- fs.renameSync(from, path.join(stagingDir, keep));
1759
+ try {
1760
+ fs.renameSync(from, path.join(stagingDir, keep));
1761
+ } catch (preserveErr) {
1762
+ logDebug(preserveErr, `preserve ${keep}`);
1763
+ }
1737
1764
  }
1738
1765
  }
1739
1766
 
@@ -1741,11 +1768,17 @@ function downloadOrUpdateModule(pkgName, targetDir, displayName) {
1741
1768
  fs.renameSync(targetDir, retiredDir);
1742
1769
  fs.renameSync(stagingDir, targetDir);
1743
1770
  } catch (swapError) {
1744
- if (fs.existsSync(retiredDir) && !fs.existsSync(targetDir)) fs.renameSync(retiredDir, targetDir);
1745
- fs.rmSync(stagingDir, { recursive: true, force: true });
1771
+ if (fs.existsSync(retiredDir) && !fs.existsSync(targetDir)) {
1772
+ try { fs.renameSync(retiredDir, targetDir); } catch (_) {}
1773
+ }
1774
+ safeRmDirSync(stagingDir);
1746
1775
  throw swapError;
1747
1776
  }
1748
- fs.rmSync(retiredDir, { recursive: true, force: true });
1777
+
1778
+ // On Windows especially, deleting a directory that was just renamed or contains
1779
+ // locked file handles can throw EPERM / EBUSY. Since targetDir already has the
1780
+ // new version in place, failure to clean up retiredDir must be non-fatal.
1781
+ safeRmDirSync(retiredDir);
1749
1782
 
1750
1783
  s.stop(`${displayName} ready (v${status.remoteVer || 'latest'})`);
1751
1784
  return true;
@@ -1810,6 +1843,17 @@ async function installMemB(interactive) {
1810
1843
  if (!hasUv) {
1811
1844
  try { execSync('uv --version', { stdio: 'ignore' }); hasUv = true; } catch (e) { hasUv = false; }
1812
1845
  }
1846
+ if (!hasUv && fs.existsSync(venvPython)) {
1847
+ try {
1848
+ execSync(`"${venvPython}" -m pip --version`, { stdio: 'ignore' });
1849
+ } catch (_) {
1850
+ try {
1851
+ execSync(`"${venvPython}" -m ensurepip --default-pip`, { cwd: membDir, stdio: 'ignore' });
1852
+ } catch (ePip) {
1853
+ logDebug(ePip, 'ensurepip bootstrap for memB');
1854
+ }
1855
+ }
1856
+ }
1813
1857
  const uvInstall = (pkgsArg) => `uv pip install --python "${venvPython}" ${pkgsArg}`;
1814
1858
  const pipInstall = (pkgsArg) => `"${venvPython}" -m pip install ${pkgsArg} --timeout 30 --no-input`;
1815
1859
  const installCmd = (pkgsArg) => hasUv ? uvInstall(pkgsArg) : pipInstall(pkgsArg);
@@ -2076,11 +2120,12 @@ async function installOpenWikiVisualizer() {
2076
2120
  const ecosystemWiki = path.join(homeDir, '.openwiki', 'ecosystem-wiki');
2077
2121
  const syncScript = path.join(srcDir, 'skills', 'global_config', 'openwiki-skill', 'scripts', 'sync_ecosystem_wiki.py');
2078
2122
  if (!wikiPath) {
2123
+ const pyBin = hasExecutable('python3') ? 'python3' : hasExecutable('python') ? 'python' : null;
2079
2124
  if (looksLikeWiki(ecosystemWiki)) {
2080
2125
  wikiPath = ecosystemWiki;
2081
- } else if (fs.existsSync(syncScript) && hasExecutable('python3')) {
2126
+ } else if (fs.existsSync(syncScript) && pyBin) {
2082
2127
  try {
2083
- execSync(`python3 "${syncScript}"`, { stdio: 'ignore' });
2128
+ execSync(`"${pyBin}" "${syncScript}"`, { stdio: 'ignore' });
2084
2129
  if (looksLikeWiki(ecosystemWiki)) wikiPath = ecosystemWiki;
2085
2130
  } catch (e) { logDebug(e, 'ecosystem wiki sync'); }
2086
2131
  }
@@ -2091,6 +2136,9 @@ async function installOpenWikiVisualizer() {
2091
2136
  });
2092
2137
  if (!isCancel(answer) && answer && answer.trim()) {
2093
2138
  const candidate = answer.trim().replace(/^~(?=$|\/|\\)/, homeDir);
2139
+ if (!fs.existsSync(candidate)) {
2140
+ try { fs.mkdirSync(candidate, { recursive: true }); } catch (_) {}
2141
+ }
2094
2142
  if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
2095
2143
  wikiPath = candidate;
2096
2144
  if (!looksLikeWiki(candidate)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybridlabor-api/aos",
3
- "version": "4.6.3",
3
+ "version": "4.6.4",
4
4
  "description": "AOS — A Curated AI AGENT OS. Optimized agent skills and add-ons like memB, OpenWiki, Heimdall Token Saver, and Godmode architectures.",
5
5
  "main": "installer.js",
6
6
  "engines": {