@wipcomputer/wip-ldm-os 0.4.18 → 0.4.20

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 (3) hide show
  1. package/SKILL.md +1 -1
  2. package/bin/ldm.js +98 -2
  3. package/package.json +1 -1
package/SKILL.md CHANGED
@@ -5,7 +5,7 @@ license: MIT
5
5
  interface: [cli, skill]
6
6
  metadata:
7
7
  display-name: "LDM OS"
8
- version: "0.4.18"
8
+ version: "0.4.20"
9
9
  homepage: "https://github.com/wipcomputer/wip-ldm-os"
10
10
  author: "Parker Todd Brooks"
11
11
  category: infrastructure
package/bin/ldm.js CHANGED
@@ -17,7 +17,7 @@
17
17
  * ldm --version Show version
18
18
  */
19
19
 
20
- import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, cpSync, chmodSync, unlinkSync } from 'node:fs';
20
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, cpSync, chmodSync, unlinkSync, readlinkSync } from 'node:fs';
21
21
  import { join, basename, resolve, dirname } from 'node:path';
22
22
  import { execSync } from 'node:child_process';
23
23
  import { fileURLToPath } from 'node:url';
@@ -655,6 +655,31 @@ function autoDetectExtensions() {
655
655
  async function cmdInstallCatalog() {
656
656
  // No lock here. cmdInstall() already holds it when calling this.
657
657
 
658
+ // Self-update: check if CLI itself is outdated. Update first, then re-exec.
659
+ // This breaks the chicken-and-egg: new features in ldm install are always
660
+ // available because the installer upgrades itself before doing anything else.
661
+ if (!DRY_RUN && !process.env.LDM_SELF_UPDATED) {
662
+ try {
663
+ const latest = execSync('npm view @wipcomputer/wip-ldm-os version 2>/dev/null', {
664
+ encoding: 'utf8', timeout: 15000,
665
+ }).trim();
666
+ if (latest && latest !== PKG_VERSION) {
667
+ console.log(` LDM OS CLI v${PKG_VERSION} -> v${latest}. Updating first...`);
668
+ try {
669
+ execSync(`npm install -g @wipcomputer/wip-ldm-os@${latest}`, { stdio: 'inherit', timeout: 60000 });
670
+ console.log(` CLI updated to v${latest}. Re-running with new code...`);
671
+ console.log('');
672
+ // Re-exec with the new binary. LDM_SELF_UPDATED prevents infinite loop.
673
+ const newArgs = process.argv.slice(1);
674
+ execSync(`LDM_SELF_UPDATED=1 ldm ${newArgs.join(' ')}`, { stdio: 'inherit' });
675
+ process.exit(0);
676
+ } catch (e) {
677
+ console.log(` ! Self-update failed: ${e.message}. Continuing with v${PKG_VERSION}.`);
678
+ }
679
+ }
680
+ } catch {}
681
+ }
682
+
658
683
  autoDetectExtensions();
659
684
 
660
685
  const { detectSystemState, reconcileState, formatReconciliation } = await import('../lib/state.mjs');
@@ -811,7 +836,7 @@ async function cmdInstallCatalog() {
811
836
  console.log(' Summary');
812
837
  console.log(' ────────────────────────────────────');
813
838
  if (cliLatest && cliLatest !== PKG_VERSION) {
814
- console.log(` LDM OS CLI v${PKG_VERSION} -> v${cliLatest} (run: npm install -g @wipcomputer/wip-ldm-os@${cliLatest})`);
839
+ console.log(` LDM OS CLI v${PKG_VERSION} -> v${cliLatest} (auto-updates on install)`);
815
840
  } else {
816
841
  console.log(` LDM OS CLI v${PKG_VERSION} (latest)`);
817
842
  }
@@ -941,6 +966,77 @@ async function cmdInstallCatalog() {
941
966
  }
942
967
  }
943
968
 
969
+ // Health check: fix missing CLIs + dead symlinks (#90)
970
+ console.log('');
971
+ console.log(' Running health check...');
972
+ let healthFixes = 0;
973
+
974
+ // 1. Check catalog CLIs exist on disk
975
+ for (const comp of components) {
976
+ if (!comp.npm || !comp.cliMatches || comp.cliMatches.length === 0) continue;
977
+ if (!isCatalogItemInstalled(comp)) continue;
978
+
979
+ for (const binName of comp.cliMatches) {
980
+ try {
981
+ execSync(`which ${binName} 2>/dev/null`, { encoding: 'utf8' });
982
+ } catch {
983
+ // CLI binary missing. Reinstall from npm.
984
+ console.log(` ! CLI "${binName}" missing. Reinstalling ${comp.npm}...`);
985
+ try {
986
+ execSync(`npm install -g ${comp.npm}`, { stdio: 'inherit', timeout: 60000 });
987
+ healthFixes++;
988
+ ok(`CLI: ${binName} restored`);
989
+ } catch (e) {
990
+ console.error(` x Failed to restore ${binName}: ${e.message}`);
991
+ }
992
+ }
993
+ }
994
+ }
995
+
996
+ // 2. Check for /tmp/ symlinks in global npm modules
997
+ try {
998
+ const npmPrefix = execSync('npm config get prefix', { encoding: 'utf8', timeout: 5000 }).trim();
999
+ const globalModules = join(npmPrefix, 'lib', 'node_modules', '@wipcomputer');
1000
+ if (existsSync(globalModules)) {
1001
+ for (const entry of readdirSync(globalModules, { withFileTypes: true })) {
1002
+ if (!entry.isSymbolicLink()) continue;
1003
+ try {
1004
+ const target = readlinkSync(join(globalModules, entry.name));
1005
+ if (target.includes('/tmp/') || target.includes('/private/tmp/')) {
1006
+ const pkgName = `@wipcomputer/${entry.name}`;
1007
+ console.log(` ! ${pkgName} symlinked to ${target} (will break on reboot). Reinstalling...`);
1008
+ try {
1009
+ execSync(`npm install -g ${pkgName}`, { stdio: 'inherit', timeout: 60000 });
1010
+ healthFixes++;
1011
+ ok(`${pkgName}: replaced /tmp/ symlink with registry install`);
1012
+ } catch (e) {
1013
+ console.error(` x Failed to fix ${pkgName}: ${e.message}`);
1014
+ }
1015
+ }
1016
+ } catch {}
1017
+ }
1018
+ }
1019
+ } catch {}
1020
+
1021
+ // 3. Clean orphaned /tmp/ldm-install-* dirs
1022
+ try {
1023
+ const tmpDirs = readdirSync('/private/tmp').filter(d => d.startsWith('ldm-install-'));
1024
+ if (tmpDirs.length > 0) {
1025
+ console.log(` Cleaning ${tmpDirs.length} orphaned /tmp/ldm-install-* dirs...`);
1026
+ for (const d of tmpDirs) {
1027
+ try { execSync(`rm -rf "/private/tmp/${d}"`, { stdio: 'pipe', timeout: 10000 }); } catch {}
1028
+ }
1029
+ healthFixes++;
1030
+ ok(`Cleaned ${tmpDirs.length} orphaned /tmp/ clone(s)`);
1031
+ }
1032
+ } catch {}
1033
+
1034
+ if (healthFixes > 0) {
1035
+ console.log(` ${healthFixes} health issue(s) fixed.`);
1036
+ } else {
1037
+ console.log(' All healthy.');
1038
+ }
1039
+
944
1040
  // Sync boot hook from npm package (#49)
945
1041
  if (syncBootHook()) {
946
1042
  ok('Boot hook updated (sessions, messages, updates now active)');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.4.18",
3
+ "version": "0.4.20",
4
4
  "type": "module",
5
5
  "description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
6
6
  "main": "src/boot/boot-hook.mjs",