@wipcomputer/wip-ldm-os 0.4.17 → 0.4.19

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 +116 -4
  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.17"
8
+ version: "0.4.19"
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';
@@ -784,6 +784,50 @@ async function cmdInstallCatalog() {
784
784
  const totalUpdates = npmUpdates.length;
785
785
 
786
786
  if (DRY_RUN) {
787
+ // Summary block (#80)
788
+ const cliLatest = (() => {
789
+ try {
790
+ return execSync('npm view @wipcomputer/wip-ldm-os version 2>/dev/null', {
791
+ encoding: 'utf8', timeout: 10000,
792
+ }).trim();
793
+ } catch { return null; }
794
+ })();
795
+
796
+ const agentDirs = (() => {
797
+ try {
798
+ return readdirSync(join(LDM_ROOT, 'agents'), { withFileTypes: true })
799
+ .filter(d => d.isDirectory() && d.name !== '_trash').map(d => d.name);
800
+ } catch { return []; }
801
+ })();
802
+
803
+ const totalExtensions = Object.keys(reconciled).length;
804
+ const majorBumps = npmUpdates.filter(e => {
805
+ const curMajor = parseInt(e.currentVersion.split('.')[0], 10);
806
+ const latMajor = parseInt(e.latestVersion.split('.')[0], 10);
807
+ return latMajor > curMajor;
808
+ });
809
+
810
+ console.log('');
811
+ console.log(' Summary');
812
+ console.log(' ────────────────────────────────────');
813
+ 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})`);
815
+ } else {
816
+ console.log(` LDM OS CLI v${PKG_VERSION} (latest)`);
817
+ }
818
+ if (npmUpdates.length > 0) {
819
+ console.log(` Extensions ${totalExtensions} installed, ${npmUpdates.length} update(s)`);
820
+ } else {
821
+ console.log(` Extensions ${totalExtensions} installed, all up to date`);
822
+ }
823
+ for (const m of majorBumps) {
824
+ console.log(` Major bump ${m.name} v${m.currentVersion} -> v${m.latestVersion}`);
825
+ }
826
+ if (agentDirs.length > 0) {
827
+ console.log(` Agents ${agentDirs.join(', ')} (no change)`);
828
+ }
829
+ console.log(` Data crystal.db, agent files, secrets (never touched)`);
830
+
787
831
  if (npmUpdates.length > 0) {
788
832
  // Table output
789
833
  const nameW = Math.max(10, ...npmUpdates.map(e => e.name.length));
@@ -803,10 +847,7 @@ async function cmdInstallCatalog() {
803
847
  console.log(` ${pad(e.name, nameW)} │ ${pad('v' + e.currentVersion, curW)} │ ${pad('v' + e.latestVersion, latW)} │ ${pad(e.catalogNpm, pkgW)}`);
804
848
  }
805
849
  console.log('');
806
- console.log(' No data (crystal.db, agent files) would be touched.');
807
850
  console.log(' Old versions would be moved to ~/.ldm/_trash/ (never deleted).');
808
- } else {
809
- console.log(' Everything is up to date. No changes needed.');
810
851
  }
811
852
 
812
853
  console.log('');
@@ -900,6 +941,77 @@ async function cmdInstallCatalog() {
900
941
  }
901
942
  }
902
943
 
944
+ // Health check: fix missing CLIs + dead symlinks (#90)
945
+ console.log('');
946
+ console.log(' Running health check...');
947
+ let healthFixes = 0;
948
+
949
+ // 1. Check catalog CLIs exist on disk
950
+ for (const comp of components) {
951
+ if (!comp.npm || !comp.cliMatches || comp.cliMatches.length === 0) continue;
952
+ if (!isCatalogItemInstalled(comp)) continue;
953
+
954
+ for (const binName of comp.cliMatches) {
955
+ try {
956
+ execSync(`which ${binName} 2>/dev/null`, { encoding: 'utf8' });
957
+ } catch {
958
+ // CLI binary missing. Reinstall from npm.
959
+ console.log(` ! CLI "${binName}" missing. Reinstalling ${comp.npm}...`);
960
+ try {
961
+ execSync(`npm install -g ${comp.npm}`, { stdio: 'inherit', timeout: 60000 });
962
+ healthFixes++;
963
+ ok(`CLI: ${binName} restored`);
964
+ } catch (e) {
965
+ console.error(` x Failed to restore ${binName}: ${e.message}`);
966
+ }
967
+ }
968
+ }
969
+ }
970
+
971
+ // 2. Check for /tmp/ symlinks in global npm modules
972
+ try {
973
+ const npmPrefix = execSync('npm config get prefix', { encoding: 'utf8', timeout: 5000 }).trim();
974
+ const globalModules = join(npmPrefix, 'lib', 'node_modules', '@wipcomputer');
975
+ if (existsSync(globalModules)) {
976
+ for (const entry of readdirSync(globalModules, { withFileTypes: true })) {
977
+ if (!entry.isSymbolicLink()) continue;
978
+ try {
979
+ const target = readlinkSync(join(globalModules, entry.name));
980
+ if (target.includes('/tmp/') || target.includes('/private/tmp/')) {
981
+ const pkgName = `@wipcomputer/${entry.name}`;
982
+ console.log(` ! ${pkgName} symlinked to ${target} (will break on reboot). Reinstalling...`);
983
+ try {
984
+ execSync(`npm install -g ${pkgName}`, { stdio: 'inherit', timeout: 60000 });
985
+ healthFixes++;
986
+ ok(`${pkgName}: replaced /tmp/ symlink with registry install`);
987
+ } catch (e) {
988
+ console.error(` x Failed to fix ${pkgName}: ${e.message}`);
989
+ }
990
+ }
991
+ } catch {}
992
+ }
993
+ }
994
+ } catch {}
995
+
996
+ // 3. Clean orphaned /tmp/ldm-install-* dirs
997
+ try {
998
+ const tmpDirs = readdirSync('/private/tmp').filter(d => d.startsWith('ldm-install-'));
999
+ if (tmpDirs.length > 0) {
1000
+ console.log(` Cleaning ${tmpDirs.length} orphaned /tmp/ldm-install-* dirs...`);
1001
+ for (const d of tmpDirs) {
1002
+ try { execSync(`rm -rf "/private/tmp/${d}"`, { stdio: 'pipe', timeout: 10000 }); } catch {}
1003
+ }
1004
+ healthFixes++;
1005
+ ok(`Cleaned ${tmpDirs.length} orphaned /tmp/ clone(s)`);
1006
+ }
1007
+ } catch {}
1008
+
1009
+ if (healthFixes > 0) {
1010
+ console.log(` ${healthFixes} health issue(s) fixed.`);
1011
+ } else {
1012
+ console.log(' All healthy.');
1013
+ }
1014
+
903
1015
  // Sync boot hook from npm package (#49)
904
1016
  if (syncBootHook()) {
905
1017
  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.17",
3
+ "version": "0.4.19",
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",