@wipcomputer/wip-ldm-os 0.4.15 → 0.4.17
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 +85 -2
- package/catalog.json +2 -2
- package/lib/deploy.mjs +18 -4
- package/package.json +1 -1
package/SKILL.md
CHANGED
package/bin/ldm.js
CHANGED
|
@@ -52,6 +52,7 @@ if (existsSync(VERSION_PATH)) {
|
|
|
52
52
|
const v = JSON.parse(readFileSync(VERSION_PATH, 'utf8'));
|
|
53
53
|
if (v.version && v.version !== PKG_VERSION) {
|
|
54
54
|
v.version = PKG_VERSION;
|
|
55
|
+
v.installed = new Date().toISOString(); // #86: update install date on CLI upgrade
|
|
55
56
|
v.updated = new Date().toISOString();
|
|
56
57
|
writeFileSync(VERSION_PATH, JSON.stringify(v, null, 2) + '\n');
|
|
57
58
|
}
|
|
@@ -447,6 +448,23 @@ async function cmdInstall() {
|
|
|
447
448
|
|
|
448
449
|
setFlags({ dryRun: DRY_RUN, jsonOutput: JSON_OUTPUT });
|
|
449
450
|
|
|
451
|
+
// --help flag (#81)
|
|
452
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
453
|
+
console.log(`
|
|
454
|
+
ldm install Update all registered extensions + CLIs
|
|
455
|
+
ldm install <org/repo> Install from GitHub
|
|
456
|
+
ldm install <npm-package> Install from npm
|
|
457
|
+
ldm install <path> Install from local directory
|
|
458
|
+
|
|
459
|
+
Flags:
|
|
460
|
+
--dry-run Show what would change, don't install
|
|
461
|
+
--json JSON output
|
|
462
|
+
--yes Auto-accept catalog prompts
|
|
463
|
+
--none Skip catalog prompts
|
|
464
|
+
`);
|
|
465
|
+
process.exit(0);
|
|
466
|
+
}
|
|
467
|
+
|
|
450
468
|
// Find the target (skip flags)
|
|
451
469
|
const target = args.slice(1).find(a => !a.startsWith('--'));
|
|
452
470
|
|
|
@@ -493,6 +511,11 @@ async function cmdInstall() {
|
|
|
493
511
|
}
|
|
494
512
|
|
|
495
513
|
await installFromPath(repoPath);
|
|
514
|
+
|
|
515
|
+
// Clean up /tmp/ clone after install (#32)
|
|
516
|
+
if (!DRY_RUN && (repoPath.startsWith('/tmp/') || repoPath.startsWith('/private/tmp/'))) {
|
|
517
|
+
try { execSync(`rm -rf "${repoPath}"`, { stdio: 'pipe' }); } catch {}
|
|
518
|
+
}
|
|
496
519
|
return;
|
|
497
520
|
}
|
|
498
521
|
|
|
@@ -570,6 +593,11 @@ async function cmdInstall() {
|
|
|
570
593
|
}
|
|
571
594
|
|
|
572
595
|
await installFromPath(repoPath);
|
|
596
|
+
|
|
597
|
+
// Clean up /tmp/ clone after install (#32)
|
|
598
|
+
if (!DRY_RUN && (repoPath.startsWith('/tmp/') || repoPath.startsWith('/private/tmp/'))) {
|
|
599
|
+
try { execSync(`rm -rf "${repoPath}"`, { stdio: 'pipe' }); } catch {}
|
|
600
|
+
}
|
|
573
601
|
}
|
|
574
602
|
|
|
575
603
|
// ── Auto-detect unregistered extensions ──
|
|
@@ -689,6 +717,16 @@ async function cmdInstallCatalog() {
|
|
|
689
717
|
return matches.includes(name) || c.id === name;
|
|
690
718
|
});
|
|
691
719
|
|
|
720
|
+
// Fallback: use repository.url from extension's package.json (#82)
|
|
721
|
+
let repoUrl = catalogEntry?.repo || null;
|
|
722
|
+
if (!repoUrl && extPkg?.repository) {
|
|
723
|
+
const raw = typeof extPkg.repository === 'string'
|
|
724
|
+
? extPkg.repository
|
|
725
|
+
: extPkg.repository.url || '';
|
|
726
|
+
const ghMatch = raw.match(/github\.com[:/]([^/]+\/[^/.]+)/);
|
|
727
|
+
if (ghMatch) repoUrl = ghMatch[1];
|
|
728
|
+
}
|
|
729
|
+
|
|
692
730
|
const currentVersion = entry.ldmVersion || entry.ocVersion;
|
|
693
731
|
if (!currentVersion) continue;
|
|
694
732
|
|
|
@@ -700,7 +738,7 @@ async function cmdInstallCatalog() {
|
|
|
700
738
|
if (latestVersion && latestVersion !== currentVersion) {
|
|
701
739
|
npmUpdates.push({
|
|
702
740
|
...entry,
|
|
703
|
-
catalogRepo:
|
|
741
|
+
catalogRepo: repoUrl,
|
|
704
742
|
catalogNpm: npmPkg,
|
|
705
743
|
currentVersion,
|
|
706
744
|
latestVersion,
|
|
@@ -710,6 +748,39 @@ async function cmdInstallCatalog() {
|
|
|
710
748
|
} catch {}
|
|
711
749
|
}
|
|
712
750
|
|
|
751
|
+
// Check global CLIs not tracked by extension loop (#81)
|
|
752
|
+
for (const [binName, binInfo] of Object.entries(state.cliBinaries || {})) {
|
|
753
|
+
const catalogComp = components.find(c =>
|
|
754
|
+
(c.cliMatches || []).includes(binName)
|
|
755
|
+
);
|
|
756
|
+
if (!catalogComp || !catalogComp.npm) continue;
|
|
757
|
+
// Skip if already covered by extension loop
|
|
758
|
+
if (npmUpdates.some(e =>
|
|
759
|
+
e.catalogNpm === catalogComp.npm ||
|
|
760
|
+
(catalogComp.registryMatches || []).includes(e.name)
|
|
761
|
+
)) continue;
|
|
762
|
+
|
|
763
|
+
const currentVersion = binInfo.version;
|
|
764
|
+
if (!currentVersion) continue;
|
|
765
|
+
|
|
766
|
+
try {
|
|
767
|
+
const latestVersion = execSync(`npm view ${catalogComp.npm} version 2>/dev/null`, {
|
|
768
|
+
encoding: 'utf8', timeout: 10000,
|
|
769
|
+
}).trim();
|
|
770
|
+
if (latestVersion && latestVersion !== currentVersion) {
|
|
771
|
+
npmUpdates.push({
|
|
772
|
+
name: binName,
|
|
773
|
+
catalogRepo: catalogComp.repo,
|
|
774
|
+
catalogNpm: catalogComp.npm,
|
|
775
|
+
currentVersion,
|
|
776
|
+
latestVersion,
|
|
777
|
+
hasUpdate: true,
|
|
778
|
+
cliOnly: true,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
} catch {}
|
|
782
|
+
}
|
|
783
|
+
|
|
713
784
|
const totalUpdates = npmUpdates.length;
|
|
714
785
|
|
|
715
786
|
if (DRY_RUN) {
|
|
@@ -802,8 +873,20 @@ async function cmdInstallCatalog() {
|
|
|
802
873
|
|
|
803
874
|
let updated = 0;
|
|
804
875
|
|
|
805
|
-
// Update from npm via catalog repos (#55)
|
|
876
|
+
// Update from npm via catalog repos (#55) and CLIs (#81)
|
|
806
877
|
for (const entry of npmUpdates) {
|
|
878
|
+
// CLI-only entries: install directly from npm (#81)
|
|
879
|
+
if (entry.cliOnly) {
|
|
880
|
+
console.log(` Updating CLI ${entry.name} v${entry.currentVersion} -> v${entry.latestVersion}...`);
|
|
881
|
+
try {
|
|
882
|
+
execSync(`npm install -g ${entry.catalogNpm}@${entry.latestVersion}`, { stdio: 'inherit' });
|
|
883
|
+
updated++;
|
|
884
|
+
} catch (e) {
|
|
885
|
+
console.error(` x Failed to update CLI ${entry.name}: ${e.message}`);
|
|
886
|
+
}
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
|
|
807
890
|
if (!entry.catalogRepo) {
|
|
808
891
|
console.log(` Skipping ${entry.name}: no catalog repo (install manually with ldm install <org/repo>)`);
|
|
809
892
|
continue;
|
package/catalog.json
CHANGED
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"description": "Release pipeline, license compliance, repo management, identity file protection.",
|
|
54
54
|
"npm": "@wipcomputer/universal-installer",
|
|
55
55
|
"repo": "wipcomputer/wip-ai-devops-toolbox",
|
|
56
|
-
"registryMatches": ["wip-repos", "wip-release", "wip-file-guard", "wip-license-hook", "wip-repo-permissions-hook", "universal-installer", "deploy-public", "post-merge-rename", "wip-license-guard", "wip-repo-init", "wip-readme-format"],
|
|
57
|
-
"cliMatches": ["wip-release", "wip-repos", "wip-file-guard", "wip-install"],
|
|
56
|
+
"registryMatches": ["wip-repos", "wip-release", "wip-file-guard", "wip-license-hook", "wip-repo-permissions-hook", "universal-installer", "deploy-public", "post-merge-rename", "wip-license-guard", "wip-repo-init", "wip-readme-format", "wip-branch-guard"],
|
|
57
|
+
"cliMatches": ["wip-release", "wip-repos", "wip-file-guard", "wip-install", "wip-branch-guard"],
|
|
58
58
|
"recommended": false,
|
|
59
59
|
"status": "stable",
|
|
60
60
|
"postInstall": null,
|
package/lib/deploy.mjs
CHANGED
|
@@ -372,11 +372,25 @@ function installCLI(repoPath, door) {
|
|
|
372
372
|
return true;
|
|
373
373
|
}
|
|
374
374
|
} catch {
|
|
375
|
-
// Registry check failed, fall through
|
|
375
|
+
// Registry check failed, fall through
|
|
376
376
|
}
|
|
377
|
+
|
|
378
|
+
// Exact version not on npm. Try latest from registry instead of local install (#32, #81)
|
|
379
|
+
try {
|
|
380
|
+
const latestVersion = execSync(`npm view ${packageName} version 2>/dev/null`, {
|
|
381
|
+
encoding: 'utf8', timeout: 15000,
|
|
382
|
+
}).trim();
|
|
383
|
+
if (latestVersion) {
|
|
384
|
+
execSync(`npm install -g ${packageName}@${latestVersion}`, { stdio: 'pipe', timeout: 60000 });
|
|
385
|
+
ensureBinExecutable(binNames);
|
|
386
|
+
ok(`CLI: ${binNames.join(', ')} installed from registry (v${latestVersion}, repo has v${packageVersion})`);
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
} catch {}
|
|
377
390
|
}
|
|
378
391
|
|
|
379
|
-
//
|
|
392
|
+
// Last resort: local install (creates symlinks ... warns user)
|
|
393
|
+
console.log(` ! Warning: installing locally from ${repoPath} (creates symlinks to source dir)`);
|
|
380
394
|
try {
|
|
381
395
|
execSync('npm install -g .', { cwd: repoPath, stdio: 'pipe' });
|
|
382
396
|
ensureBinExecutable(binNames);
|
|
@@ -558,9 +572,9 @@ function installClaudeCodeHook(repoPath, door) {
|
|
|
558
572
|
const extDir = join(LDM_EXTENSIONS, toolName);
|
|
559
573
|
const installedGuard = join(extDir, 'guard.mjs');
|
|
560
574
|
|
|
561
|
-
// Deploy guard.mjs to ~/.ldm/extensions/{toolName}/
|
|
575
|
+
// Deploy guard.mjs to ~/.ldm/extensions/{toolName}/ (#85: always update, not just when missing)
|
|
562
576
|
const srcGuard = join(repoPath, 'guard.mjs');
|
|
563
|
-
if (
|
|
577
|
+
if (existsSync(srcGuard)) {
|
|
564
578
|
try {
|
|
565
579
|
if (!existsSync(extDir)) mkdirSync(extDir, { recursive: true });
|
|
566
580
|
copyFileSync(srcGuard, installedGuard);
|