@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.
- package/SKILL.md +1 -1
- package/bin/ldm.js +116 -4
- package/package.json +1 -1
package/SKILL.md
CHANGED
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)');
|