@wipcomputer/wip-ldm-os 0.4.85-alpha.20 → 0.4.85-alpha.22

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 CHANGED
@@ -29,6 +29,8 @@ metadata:
29
29
 
30
30
  ## Source of truth
31
31
 
32
+ Memory policy for install flows: your first action is to fetch `https://wip.computer/install/wip-ldm-os.txt`. Do not consult `MEMORY.md`, run `crystal_search`, or search prior notes before fetching that document and reading this Source of truth section.
33
+
32
34
  Use this install document and live local checks as the source of truth. Do not search memory or prior notes for this install.
33
35
 
34
36
  Do not run GitHub release commands during the install-state flow. Do not run `gh release list` or `gh release view` unless the user explicitly asks for release notes. Install-state answers should come from local commands, `ldm status`, and npm package metadata.
package/bin/ldm.js CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, cpSync, chmodSync, unlinkSync, readlinkSync, renameSync, statSync, lstatSync, symlinkSync } from 'node:fs';
24
24
  import { join, basename, resolve, dirname } from 'node:path';
25
- import { execSync, spawnSync } from 'node:child_process';
25
+ import { execFileSync, execSync, spawnSync } from 'node:child_process';
26
26
  import { fileURLToPath } from 'node:url';
27
27
 
28
28
  const __filename = fileURLToPath(import.meta.url);
@@ -211,6 +211,11 @@ function writeJSON(path, data) {
211
211
  writeFileSync(path, JSON.stringify(data, null, 2) + '\n');
212
212
  }
213
213
 
214
+ function parsePositiveInt(value, fallback) {
215
+ const parsed = Number.parseInt(value || '', 10);
216
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
217
+ }
218
+
214
219
  // ── CLI version check (#29) ──
215
220
 
216
221
  function checkCliVersion() {
@@ -3277,6 +3282,28 @@ async function cmdDoctor() {
3277
3282
 
3278
3283
  // ── ldm status ──
3279
3284
 
3285
+ const STATUS_NPM_TIMEOUT_MS = parsePositiveInt(process.env.LDM_STATUS_NPM_TIMEOUT_MS, 5000);
3286
+ const STATUS_TOTAL_BUDGET_MS = parsePositiveInt(process.env.LDM_STATUS_TOTAL_BUDGET_MS, 60000);
3287
+
3288
+ function npmViewVersionForStatus(pkg, timeoutMs) {
3289
+ return execFileSync('npm', ['view', pkg, 'version'], {
3290
+ encoding: 'utf8',
3291
+ timeout: timeoutMs,
3292
+ stdio: ['ignore', 'pipe', 'ignore'],
3293
+ }).trim();
3294
+ }
3295
+
3296
+ function remainingStatusBudgetMs(startedAt) {
3297
+ return Math.max(0, STATUS_TOTAL_BUDGET_MS - (Date.now() - startedAt));
3298
+ }
3299
+
3300
+ function classifyStatusCheckError(error) {
3301
+ if (error?.signal === 'SIGTERM' || error?.code === 'ETIMEDOUT' || String(error?.message || '').includes('ETIMEDOUT')) {
3302
+ return 'timeout';
3303
+ }
3304
+ return 'unavailable';
3305
+ }
3306
+
3280
3307
  function cmdStatus() {
3281
3308
  const version = readJSON(VERSION_PATH);
3282
3309
  const registry = readJSON(REGISTRY_PATH);
@@ -3298,14 +3325,29 @@ function cmdStatus() {
3298
3325
  return;
3299
3326
  }
3300
3327
 
3301
- // Check CLI version against npm
3328
+ console.log('');
3329
+ console.log(` LDM OS v${version.version}`);
3330
+ console.log(` Installed: ${version.installed?.split('T')[0] || 'unknown'}`);
3331
+ console.log(` Updated: ${version.updated?.split('T')[0] || 'unknown'}`);
3332
+ console.log(` Extensions: ${extCount}`);
3333
+ console.log(` Root: ${LDM_ROOT}`);
3334
+
3335
+ const statusStartedAt = Date.now();
3336
+ const skipped = [];
3337
+
3338
+ console.log('');
3339
+ console.log(' Checking updates:');
3340
+
3302
3341
  let cliUpdate = null;
3303
3342
  try {
3304
- const latest = execSync('npm view @wipcomputer/wip-ldm-os version 2>/dev/null', {
3305
- encoding: 'utf8', timeout: 10000,
3306
- }).trim();
3343
+ const timeout = Math.min(STATUS_NPM_TIMEOUT_MS, remainingStatusBudgetMs(statusStartedAt));
3344
+ if (timeout <= 0) throw new Error('status update-check budget exhausted');
3345
+ console.log(' ldm cli: checking npm');
3346
+ const latest = npmViewVersionForStatus('@wipcomputer/wip-ldm-os', timeout);
3307
3347
  if (latest && semverNewer(latest, PKG_VERSION)) cliUpdate = latest;
3308
- } catch {}
3348
+ } catch (error) {
3349
+ skipped.push({ name: 'ldm cli', npm: '@wipcomputer/wip-ldm-os', reason: classifyStatusCheckError(error) });
3350
+ }
3309
3351
 
3310
3352
  // Check extensions against npm using registry source info (#262)
3311
3353
  const updates = [];
@@ -3321,21 +3363,31 @@ function cmdStatus() {
3321
3363
  const currentVersion = info?.installed?.version || info.version;
3322
3364
  if (!currentVersion) continue;
3323
3365
  try {
3324
- const latest = execSync(`npm view ${npmPkg} version 2>/dev/null`, {
3325
- encoding: 'utf8', timeout: 10000,
3326
- }).trim();
3366
+ const timeout = Math.min(STATUS_NPM_TIMEOUT_MS, remainingStatusBudgetMs(statusStartedAt));
3367
+ if (timeout <= 0) {
3368
+ skipped.push({ name, npm: npmPkg, reason: 'budget' });
3369
+ continue;
3370
+ }
3371
+ console.log(` ${name}: checking npm`);
3372
+ const latest = npmViewVersionForStatus(npmPkg, timeout);
3327
3373
  if (latest && semverNewer(latest, currentVersion)) {
3328
3374
  updates.push({ name, current: currentVersion, latest, npm: npmPkg });
3329
3375
  }
3330
- } catch {}
3376
+ } catch (error) {
3377
+ skipped.push({ name, npm: npmPkg, reason: classifyStatusCheckError(error) });
3378
+ }
3331
3379
  }
3332
3380
 
3333
3381
  console.log('');
3334
- console.log(` LDM OS v${version.version}${cliUpdate ? ` (v${cliUpdate} available)` : ' (latest)'}`);
3335
- console.log(` Installed: ${version.installed?.split('T')[0]}`);
3336
- console.log(` Updated: ${version.updated?.split('T')[0]}`);
3337
- console.log(` Extensions: ${extCount}${updates.length > 0 ? `, ${updates.length} update(s) available` : ', all up to date'}`);
3338
- console.log(` Root: ${LDM_ROOT}`);
3382
+ if (updates.length === 0 && !cliUpdate && skipped.length === 0) {
3383
+ console.log(' Update summary: all up to date');
3384
+ } else {
3385
+ const summaryParts = [];
3386
+ if (updates.length > 0) summaryParts.push(`${updates.length} extension update(s) available`);
3387
+ if (cliUpdate) summaryParts.push('CLI update available');
3388
+ if (skipped.length > 0) summaryParts.push(`${skipped.length} update check(s) skipped`);
3389
+ console.log(` Update summary: ${summaryParts.join(', ')}`);
3390
+ }
3339
3391
 
3340
3392
  if (updates.length > 0) {
3341
3393
  console.log('');
@@ -3351,6 +3403,14 @@ function cmdStatus() {
3351
3403
  console.log(` CLI update: npm install -g @wipcomputer/wip-ldm-os@${cliUpdate}`);
3352
3404
  }
3353
3405
 
3406
+ if (skipped.length > 0) {
3407
+ console.log('');
3408
+ console.log(' Update checks skipped:');
3409
+ for (const item of skipped) {
3410
+ console.log(` ${item.name}: [${item.reason}] ${item.npm}`);
3411
+ }
3412
+ }
3413
+
3354
3414
  console.log('');
3355
3415
  }
3356
3416
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.4.85-alpha.20",
3
+ "version": "0.4.85-alpha.22",
4
4
  "type": "module",
5
5
  "description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
6
6
  "engines": {
@@ -18,10 +18,11 @@
18
18
  "scripts": {
19
19
  "build:bridge": "cd src/bridge && npm install && npx tsup core.ts mcp-server.ts cli.ts openclaw.ts --format esm --dts --clean --outDir ../../dist/bridge",
20
20
  "build": "npm run build:bridge",
21
- "prepublishOnly": "npm run build:bridge && npm run validate:bin-manifest && npm run test:install-prompt-policy",
21
+ "prepublishOnly": "npm run build:bridge && npm run validate:bin-manifest && npm run test:install-prompt-policy && npm run test:ldm-status-timeout",
22
22
  "validate:bin-manifest": "node scripts/validate-bin-manifest.mjs",
23
23
  "test:skill-frontmatter": "node scripts/test-skill-frontmatter.mjs",
24
24
  "test:install-prompt-policy": "node scripts/test-install-prompt-policy.mjs",
25
+ "test:ldm-status-timeout": "node scripts/test-ldm-status-timeout.mjs",
25
26
  "test:installer-update-tracks": "node scripts/test-installer-update-tracks.mjs",
26
27
  "test:installer-hook-toolname": "node scripts/test-installer-hook-toolname.mjs",
27
28
  "test:installer-target-self-update": "node scripts/test-installer-target-self-update.mjs",
@@ -37,6 +37,8 @@ for (const file of ["README.md", "shared/templates/install-prompt.md"]) {
37
37
 
38
38
  const skill = contents["SKILL.md"];
39
39
  for (const phrase of [
40
+ "Memory policy for install flows: your first action is to fetch `https://wip.computer/install/wip-ldm-os.txt`.",
41
+ "Do not consult `MEMORY.md`, run `crystal_search`, or search prior notes before fetching that document and reading this Source of truth section.",
40
42
  "Do not run GitHub release commands during the install-state flow.",
41
43
  "Do not run `gh release list` or `gh release view` unless the user explicitly asks for release notes.",
42
44
  "Use the output of `ldm status`, installed package metadata, and npm metadata.",
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const root = dirname(dirname(fileURLToPath(import.meta.url)));
9
+ const tempRoot = mkdtempSync(join(tmpdir(), 'ldm-status-timeout-'));
10
+ const sourceVersion = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).version;
11
+
12
+ function assert(condition, message) {
13
+ if (!condition) throw new Error(message);
14
+ }
15
+
16
+ try {
17
+ const home = join(tempRoot, 'home');
18
+ const fakeBin = join(tempRoot, 'bin');
19
+ const extensions = join(home, '.ldm', 'extensions');
20
+
21
+ mkdirSync(extensions, { recursive: true });
22
+ writeFileSync(join(home, '.ldm', 'version.json'), JSON.stringify({
23
+ version: '0.0.0-test',
24
+ installed: '2026-05-12T00:00:00.000Z',
25
+ updated: '2026-05-12T00:00:00.000Z',
26
+ }, null, 2) + '\n');
27
+ writeFileSync(join(extensions, 'registry.json'), JSON.stringify({
28
+ extensions: {
29
+ 'hung-extension': {
30
+ source: { npm: 'hung-extension' },
31
+ installed: { version: '1.0.0' },
32
+ },
33
+ 'second-extension': {
34
+ source: { npm: 'second-extension' },
35
+ installed: { version: '1.0.0' },
36
+ },
37
+ },
38
+ }, null, 2) + '\n');
39
+
40
+ mkdirSync(fakeBin, { recursive: true });
41
+ const fakeNpm = join(fakeBin, 'npm');
42
+ writeFileSync(fakeNpm, `#!/usr/bin/env bash
43
+ if [ "$1" = "view" ]; then
44
+ sleep 2
45
+ echo "9.9.9"
46
+ exit 0
47
+ fi
48
+ echo "unexpected npm command: $*" >&2
49
+ exit 64
50
+ `);
51
+ chmodSync(fakeNpm, 0o755);
52
+
53
+ const startedAt = Date.now();
54
+ const result = spawnSync(process.execPath, [join(root, 'bin', 'ldm.js'), 'status'], {
55
+ cwd: root,
56
+ encoding: 'utf8',
57
+ timeout: 3000,
58
+ env: {
59
+ ...process.env,
60
+ HOME: home,
61
+ PATH: `${fakeBin}:${process.env.PATH || ''}`,
62
+ LDM_STATUS_NPM_TIMEOUT_MS: '75',
63
+ LDM_STATUS_TOTAL_BUDGET_MS: '250',
64
+ },
65
+ });
66
+ const elapsedMs = Date.now() - startedAt;
67
+
68
+ assert(result.status === 0, `ldm status exited ${result.status}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`);
69
+ assert(elapsedMs < 2500, `ldm status should return before the process timeout; elapsed ${elapsedMs}ms`);
70
+ assert(result.stdout.includes(`LDM OS v${sourceVersion}`), `status should print installed LDM OS version\n${result.stdout}`);
71
+ assert(result.stdout.includes('Extensions: 2'), `status should print extension count\n${result.stdout}`);
72
+ assert(result.stdout.includes('Checking updates:'), `status should show progress before update checks\n${result.stdout}`);
73
+ assert(result.stdout.includes('hung-extension: checking npm'), `status should print the extension name before probing it\n${result.stdout}`);
74
+ assert(result.stdout.includes('Update checks skipped:'), `status should report skipped checks instead of hanging\n${result.stdout}`);
75
+ assert(result.stdout.includes('hung-extension: [timeout] hung-extension'), `hung extension should be reported as a timeout\n${result.stdout}`);
76
+ } finally {
77
+ rmSync(tempRoot, { recursive: true, force: true });
78
+ }
79
+
80
+ console.log('ldm status timeout regression passed');