@wipcomputer/wip-ldm-os 0.4.85-alpha.12 → 0.4.85-alpha.13

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/bin/ldm.js CHANGED
@@ -229,6 +229,58 @@ function checkCliVersion() {
229
229
  }
230
230
  }
231
231
 
232
+ function selectedLdmNpmTrack() {
233
+ return ALPHA_FLAG ? 'alpha' : BETA_FLAG ? 'beta' : 'latest';
234
+ }
235
+
236
+ function selectedLdmTrackLabel(npmTag) {
237
+ return npmTag === 'latest' ? '' : ` (${npmTag} track)`;
238
+ }
239
+
240
+ function latestLdmCliForSelectedTrack() {
241
+ const npmTag = selectedLdmNpmTrack();
242
+ const npmViewCmd = npmTag === 'latest'
243
+ ? 'npm view @wipcomputer/wip-ldm-os version 2>/dev/null'
244
+ : `npm view @wipcomputer/wip-ldm-os dist-tags.${npmTag} 2>/dev/null`;
245
+ const latest = execSync(npmViewCmd, {
246
+ encoding: 'utf8',
247
+ timeout: 15000,
248
+ }).trim();
249
+ return { latest, npmTag };
250
+ }
251
+
252
+ function maybeSelfUpdateLdmCliBeforeInstall() {
253
+ // This shared preflight covers both bare `ldm install` and targeted
254
+ // `ldm install <app>`. Dry runs never update, but still disclose skew.
255
+ if (process.env.LDM_SELF_UPDATED) return;
256
+
257
+ try {
258
+ const { latest, npmTag } = latestLdmCliForSelectedTrack();
259
+ if (!latest || !semverNewer(latest, PKG_VERSION)) return;
260
+
261
+ const trackLabel = selectedLdmTrackLabel(npmTag);
262
+
263
+ if (DRY_RUN) {
264
+ console.log(` LDM OS CLI v${PKG_VERSION} -> v${latest}${trackLabel} is available.`);
265
+ console.log(` Dry run only: continuing with v${PKG_VERSION}.`);
266
+ console.log('');
267
+ return;
268
+ }
269
+
270
+ console.log(` LDM OS CLI v${PKG_VERSION} -> v${latest}${trackLabel}. Updating first...`);
271
+ try {
272
+ execSync(`npm install -g @wipcomputer/wip-ldm-os@${latest}`, { stdio: 'inherit', timeout: 60000 });
273
+ console.log(` CLI updated to v${latest}. Re-running with new code...`);
274
+ console.log('');
275
+ const reArgs = process.argv.slice(2).join(' ') || 'install';
276
+ execSync(`LDM_SELF_UPDATED=1 ldm ${reArgs}`, { stdio: 'inherit' });
277
+ process.exit(0);
278
+ } catch (e) {
279
+ console.log(` ! Self-update failed: ${e.message}. Continuing with v${PKG_VERSION}.`);
280
+ }
281
+ } catch {}
282
+ }
283
+
232
284
  // ── Dead backup trigger cleanup (#207) ──
233
285
  // Three backup systems were competing. Only ai.openclaw.ldm-backup (3am) works.
234
286
  // This removes: broken cron entry (LDMDevTools.app), old com.wipcomputer.daily-backup.
@@ -1476,6 +1528,8 @@ async function cmdInstall() {
1476
1528
  process.exit(0);
1477
1529
  }
1478
1530
 
1531
+ maybeSelfUpdateLdmCliBeforeInstall();
1532
+
1479
1533
  // Find the target (skip flags)
1480
1534
  const target = args.slice(1).find(a => !a.startsWith('--'));
1481
1535
 
@@ -1921,38 +1975,6 @@ async function cmdInstallCatalog() {
1921
1975
  // No lock here. cmdInstall() already holds it when calling this.
1922
1976
  installLog(`ldm install started (v${PKG_VERSION}, DRY_RUN=${DRY_RUN})`);
1923
1977
 
1924
- // Self-update: check if CLI itself is outdated. Update first, then re-exec.
1925
- // This breaks the chicken-and-egg: new features in ldm install are always
1926
- // available because the installer upgrades itself before doing anything else.
1927
- // --alpha and --beta flags check the corresponding npm dist-tag instead of @latest.
1928
- if (!DRY_RUN && !process.env.LDM_SELF_UPDATED) {
1929
- try {
1930
- const npmTag = ALPHA_FLAG ? 'alpha' : BETA_FLAG ? 'beta' : 'latest';
1931
- const trackLabel = npmTag === 'latest' ? '' : ` (${npmTag} track)`;
1932
- const npmViewCmd = npmTag === 'latest'
1933
- ? 'npm view @wipcomputer/wip-ldm-os version 2>/dev/null'
1934
- : `npm view @wipcomputer/wip-ldm-os dist-tags.${npmTag} 2>/dev/null`;
1935
- const latest = execSync(npmViewCmd, {
1936
- encoding: 'utf8', timeout: 15000,
1937
- }).trim();
1938
- if (latest && semverNewer(latest, PKG_VERSION)) {
1939
- console.log(` LDM OS CLI v${PKG_VERSION} -> v${latest}${trackLabel}. Updating first...`);
1940
- try {
1941
- execSync(`npm install -g @wipcomputer/wip-ldm-os@${latest}`, { stdio: 'inherit', timeout: 60000 });
1942
- console.log(` CLI updated to v${latest}. Re-running with new code...`);
1943
- console.log('');
1944
- // Re-exec with the new binary. LDM_SELF_UPDATED prevents infinite loop.
1945
- // process.argv.slice(2) skips 'node' and the script path, keeps just 'install' + flags
1946
- const reArgs = process.argv.slice(2).join(' ') || 'install';
1947
- execSync(`LDM_SELF_UPDATED=1 ldm ${reArgs}`, { stdio: 'inherit' });
1948
- process.exit(0);
1949
- } catch (e) {
1950
- console.log(` ! Self-update failed: ${e.message}. Continuing with v${PKG_VERSION}.`);
1951
- }
1952
- }
1953
- } catch {}
1954
- }
1955
-
1956
1978
  autoDetectExtensions();
1957
1979
 
1958
1980
  // Migrate old registry entries to v2 format (#262)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.4.85-alpha.12",
3
+ "version": "0.4.85-alpha.13",
4
4
  "type": "module",
5
5
  "description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
6
6
  "engines": {
@@ -23,6 +23,7 @@
23
23
  "test:skill-frontmatter": "node scripts/test-skill-frontmatter.mjs",
24
24
  "test:installer-update-tracks": "node scripts/test-installer-update-tracks.mjs",
25
25
  "test:installer-hook-toolname": "node scripts/test-installer-hook-toolname.mjs",
26
+ "test:installer-target-self-update": "node scripts/test-installer-target-self-update.mjs",
26
27
  "test:installer-skill-directory": "node scripts/test-installer-skill-directory.mjs",
27
28
  "test:installer-skill-dry-run-destinations": "node scripts/test-installer-skill-dry-run-destinations.mjs",
28
29
  "test:ldm-install-bin-shim": "node scripts/test-ldm-install-preserves-foreign-bin.mjs",
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const root = dirname(dirname(fileURLToPath(import.meta.url)));
7
+ const cli = readFileSync(join(root, 'bin', 'ldm.js'), 'utf8');
8
+
9
+ const helperName = 'function maybeSelfUpdateLdmCliBeforeInstall()';
10
+ const helperIdx = cli.indexOf(helperName);
11
+ if (helperIdx === -1) {
12
+ throw new Error('Missing shared LDM OS self-update preflight helper');
13
+ }
14
+
15
+ const helperBlock = cli.slice(helperIdx, cli.indexOf('// ── Dead backup trigger cleanup', helperIdx));
16
+ if (!helperBlock.includes('Dry run only: continuing with v${PKG_VERSION}.')) {
17
+ throw new Error('Self-update helper must warn on dry run without updating');
18
+ }
19
+
20
+ if (!helperBlock.includes("execSync(`npm install -g @wipcomputer/wip-ldm-os@${latest}`")) {
21
+ throw new Error('Self-update helper must update LDM OS before real installs');
22
+ }
23
+
24
+ if (!helperBlock.includes('execSync(`LDM_SELF_UPDATED=1 ldm ${reArgs}`')) {
25
+ throw new Error('Self-update helper must re-run the original install command');
26
+ }
27
+
28
+ const cmdInstallIdx = cli.indexOf('async function cmdInstall()');
29
+ const targetIdx = cli.indexOf('// Find the target (skip flags)', cmdInstallIdx);
30
+ const preflightCallIdx = cli.indexOf('maybeSelfUpdateLdmCliBeforeInstall();', cmdInstallIdx);
31
+ if (cmdInstallIdx === -1 || targetIdx === -1 || preflightCallIdx === -1) {
32
+ throw new Error('Could not find cmdInstall self-update placement');
33
+ }
34
+
35
+ if (preflightCallIdx > targetIdx) {
36
+ throw new Error('Self-update preflight must run before target resolution so app installs are covered');
37
+ }
38
+
39
+ const catalogIdx = cli.indexOf('async function cmdInstallCatalog()');
40
+ const oldCatalogBlock = cli.indexOf('Self-update: check if CLI itself is outdated', catalogIdx);
41
+ const autoDetectIdx = cli.indexOf('autoDetectExtensions();', catalogIdx);
42
+ if (oldCatalogBlock !== -1 && oldCatalogBlock < autoDetectIdx) {
43
+ throw new Error('Catalog install should not own the only self-update block');
44
+ }
45
+
46
+ console.log('targeted install self-update regression checks passed');