@hybridlabor-api/aos 4.6.2 → 4.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/installer.js +64 -9
  2. package/package.json +1 -1
package/installer.js CHANGED
@@ -59,6 +59,12 @@ const {
59
59
  log
60
60
  } = clack;
61
61
 
62
+ // Defensive alias: @clack/prompts provides log.success, not log.ok.
63
+ // Ensure any legacy or third-party call to log.ok safely routes to log.success.
64
+ if (log && typeof log === 'object' && !log.ok) {
65
+ log.ok = log.success;
66
+ }
67
+
62
68
  const {
63
69
  renderKineticIntro,
64
70
  buildHeroHeader,
@@ -1066,6 +1072,15 @@ function moveIfExists(src, dest, label) {
1066
1072
  }
1067
1073
  }
1068
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
+
1069
1084
  function copyDirRecursiveSync(source, target, excludeList = [], manifest = null, knownSourceHashes = null) {
1070
1085
  // Fall back to session-level state when no explicit manifest is passed.
1071
1086
  const mfst = manifest !== null ? manifest : _sessionManifest;
@@ -1716,7 +1731,21 @@ function downloadOrUpdateModule(pkgName, targetDir, displayName) {
1716
1731
  // kept until the swap succeeds, and restored if it does not.
1717
1732
  const stagingDir = `${targetDir}.incoming-${timestamp}`;
1718
1733
  const retiredDir = `${targetDir}.previous-${timestamp}`;
1719
- 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);
1720
1749
  fs.mkdirSync(stagingDir, { recursive: true });
1721
1750
  execSync(`tar -xzf "${path.join(targetDir, tarball)}" --strip-components=1 -C "${stagingDir}"`, { stdio: 'ignore' });
1722
1751
  fs.unlinkSync(path.join(targetDir, tarball));
@@ -1727,7 +1756,11 @@ function downloadOrUpdateModule(pkgName, targetDir, displayName) {
1727
1756
  for (const keep of PRESERVE) {
1728
1757
  const from = path.join(targetDir, keep);
1729
1758
  if (fs.existsSync(from) && !fs.existsSync(path.join(stagingDir, keep))) {
1730
- 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
+ }
1731
1764
  }
1732
1765
  }
1733
1766
 
@@ -1735,11 +1768,17 @@ function downloadOrUpdateModule(pkgName, targetDir, displayName) {
1735
1768
  fs.renameSync(targetDir, retiredDir);
1736
1769
  fs.renameSync(stagingDir, targetDir);
1737
1770
  } catch (swapError) {
1738
- if (fs.existsSync(retiredDir) && !fs.existsSync(targetDir)) fs.renameSync(retiredDir, targetDir);
1739
- 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);
1740
1775
  throw swapError;
1741
1776
  }
1742
- 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);
1743
1782
 
1744
1783
  s.stop(`${displayName} ready (v${status.remoteVer || 'latest'})`);
1745
1784
  return true;
@@ -1804,6 +1843,17 @@ async function installMemB(interactive) {
1804
1843
  if (!hasUv) {
1805
1844
  try { execSync('uv --version', { stdio: 'ignore' }); hasUv = true; } catch (e) { hasUv = false; }
1806
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
+ }
1807
1857
  const uvInstall = (pkgsArg) => `uv pip install --python "${venvPython}" ${pkgsArg}`;
1808
1858
  const pipInstall = (pkgsArg) => `"${venvPython}" -m pip install ${pkgsArg} --timeout 30 --no-input`;
1809
1859
  const installCmd = (pkgsArg) => hasUv ? uvInstall(pkgsArg) : pipInstall(pkgsArg);
@@ -2022,7 +2072,7 @@ async function installOpenWikiVisualizer() {
2022
2072
  log.warn('Skipping OpenWiki Visualizer setup: the openwiki CLI is not on PATH. Install it with: npm install -g openwiki@latest');
2023
2073
  return;
2024
2074
  }
2025
- log.ok('OpenWiki CLI installed.');
2075
+ log.success('OpenWiki CLI installed.');
2026
2076
  }
2027
2077
  // launchd starts agents with a minimal PATH that never includes npm's global
2028
2078
  // bin dir, so resolve the absolute binary path now instead of relying on PATH at boot.
@@ -2070,11 +2120,12 @@ async function installOpenWikiVisualizer() {
2070
2120
  const ecosystemWiki = path.join(homeDir, '.openwiki', 'ecosystem-wiki');
2071
2121
  const syncScript = path.join(srcDir, 'skills', 'global_config', 'openwiki-skill', 'scripts', 'sync_ecosystem_wiki.py');
2072
2122
  if (!wikiPath) {
2123
+ const pyBin = hasExecutable('python3') ? 'python3' : hasExecutable('python') ? 'python' : null;
2073
2124
  if (looksLikeWiki(ecosystemWiki)) {
2074
2125
  wikiPath = ecosystemWiki;
2075
- } else if (fs.existsSync(syncScript) && hasExecutable('python3')) {
2126
+ } else if (fs.existsSync(syncScript) && pyBin) {
2076
2127
  try {
2077
- execSync(`python3 "${syncScript}"`, { stdio: 'ignore' });
2128
+ execSync(`"${pyBin}" "${syncScript}"`, { stdio: 'ignore' });
2078
2129
  if (looksLikeWiki(ecosystemWiki)) wikiPath = ecosystemWiki;
2079
2130
  } catch (e) { logDebug(e, 'ecosystem wiki sync'); }
2080
2131
  }
@@ -2085,6 +2136,9 @@ async function installOpenWikiVisualizer() {
2085
2136
  });
2086
2137
  if (!isCancel(answer) && answer && answer.trim()) {
2087
2138
  const candidate = answer.trim().replace(/^~(?=$|\/|\\)/, homeDir);
2139
+ if (!fs.existsSync(candidate)) {
2140
+ try { fs.mkdirSync(candidate, { recursive: true }); } catch (_) {}
2141
+ }
2088
2142
  if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
2089
2143
  wikiPath = candidate;
2090
2144
  if (!looksLikeWiki(candidate)) {
@@ -2151,7 +2205,8 @@ async function installOpenWikiVisualizer() {
2151
2205
  const stdoutLog = path.join(visualizerLogDir, 'visualize.stdout.log');
2152
2206
  const stderrLog = path.join(visualizerLogDir, 'visualize.stderr.log');
2153
2207
  const batPath = path.join(visualizerLogDir, 'run-openwiki-visualize.bat');
2154
- const runCmd = `"${openwikiBin}" visualize --port 4321 --no-open`;
2208
+ const targetWikiArg = wikiPath ? `"${wikiPath}" ` : '';
2209
+ const runCmd = `"${openwikiBin}" visualize ${targetWikiArg}--port 4321 --no-open`;
2155
2210
  const batContent = `@echo off\r\ncd /d "${homeDir}"\r\n${runCmd} >> "${stdoutLog}" 2>> "${stderrLog}"\r\n`;
2156
2211
  const vbsPath = path.join(startupDir, 'com.bdb.openwiki-visualize.vbs');
2157
2212
  const vbsContent = `Set WshShell = CreateObject("WScript.Shell")\r\nWshShell.CurrentDirectory = "${homeDir}"\r\nWshShell.Run """${batPath}""", 0, False\r\n`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybridlabor-api/aos",
3
- "version": "4.6.2",
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": {