@wipcomputer/wip-ldm-os 0.4.85-alpha.26 → 0.4.85-alpha.27

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
@@ -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 { execFile, execSync, spawnSync } from 'node:child_process';
25
+ import { execSync, spawnSync } from 'node:child_process';
26
26
  import { fileURLToPath } from 'node:url';
27
27
 
28
28
  const __filename = fileURLToPath(import.meta.url);
@@ -3285,21 +3285,27 @@ async function cmdDoctor() {
3285
3285
  const STATUS_NPM_TIMEOUT_MS = parsePositiveInt(process.env.LDM_STATUS_NPM_TIMEOUT_MS, 5000);
3286
3286
  const STATUS_TOTAL_BUDGET_MS = parsePositiveInt(process.env.LDM_STATUS_TOTAL_BUDGET_MS, 60000);
3287
3287
  const STATUS_NPM_CONCURRENCY = parsePositiveInt(process.env.LDM_STATUS_NPM_CONCURRENCY, 8);
3288
+ const STATUS_NPM_REGISTRY_URL = process.env.LDM_STATUS_NPM_REGISTRY_URL || 'https://registry.npmjs.org';
3288
3289
 
3289
- function npmViewVersionForStatus(pkg, timeoutMs) {
3290
- return new Promise((resolvePromise, rejectPromise) => {
3291
- execFile('npm', ['view', pkg, 'version'], {
3292
- encoding: 'utf8',
3293
- timeout: timeoutMs,
3294
- maxBuffer: 1024 * 1024,
3295
- }, (error, stdout) => {
3296
- if (error) {
3297
- rejectPromise(error);
3298
- return;
3299
- }
3300
- resolvePromise(String(stdout || '').trim());
3290
+ async function npmViewVersionForStatus(pkg, timeoutMs) {
3291
+ const controller = new AbortController();
3292
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
3293
+ try {
3294
+ const registry = STATUS_NPM_REGISTRY_URL.replace(/\/+$/, '');
3295
+ const response = await fetch(`${registry}/${encodeURIComponent(pkg)}`, {
3296
+ signal: controller.signal,
3297
+ headers: { accept: 'application/vnd.npm.install-v1+json, application/json' },
3301
3298
  });
3302
- });
3299
+ if (!response.ok) {
3300
+ const error = new Error(`npm registry returned ${response.status}`);
3301
+ error.statusCode = response.status;
3302
+ throw error;
3303
+ }
3304
+ const metadata = await response.json();
3305
+ return metadata?.['dist-tags']?.latest || '';
3306
+ } finally {
3307
+ clearTimeout(timeout);
3308
+ }
3303
3309
  }
3304
3310
 
3305
3311
  function remainingStatusBudgetMs(startedAt) {
@@ -3313,7 +3319,7 @@ function formatStatusElapsed(ms) {
3313
3319
  }
3314
3320
 
3315
3321
  function classifyStatusCheckError(error) {
3316
- if (error?.signal === 'SIGTERM' || error?.code === 'ETIMEDOUT' || String(error?.message || '').includes('ETIMEDOUT')) {
3322
+ if (error?.name === 'AbortError' || error?.signal === 'SIGTERM' || error?.code === 'ETIMEDOUT' || String(error?.message || '').includes('ETIMEDOUT')) {
3317
3323
  return 'timeout';
3318
3324
  }
3319
3325
  return 'unavailable';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.4.85-alpha.26",
3
+ "version": "0.4.85-alpha.27",
4
4
  "type": "module",
5
5
  "description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
6
6
  "engines": {
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { createServer } from 'node:http';
3
4
  import { tmpdir } from 'node:os';
4
5
  import { dirname, join } from 'node:path';
5
- import { spawnSync } from 'node:child_process';
6
+ import { spawn } from 'node:child_process';
6
7
  import { fileURLToPath } from 'node:url';
7
8
 
8
9
  const root = dirname(dirname(fileURLToPath(import.meta.url)));
@@ -13,7 +14,28 @@ function assert(condition, message) {
13
14
  if (!condition) throw new Error(message);
14
15
  }
15
16
 
16
- function writeFixture(home, fakeBin) {
17
+ function listen(server) {
18
+ return new Promise((resolve) => {
19
+ server.listen(0, '127.0.0.1', () => resolve(server.address()));
20
+ });
21
+ }
22
+
23
+ function createRegistryServer(delayMs) {
24
+ let active = 0;
25
+ let maxActive = 0;
26
+ const server = createServer((_req, res) => {
27
+ active += 1;
28
+ maxActive = Math.max(maxActive, active);
29
+ setTimeout(() => {
30
+ res.setHeader('content-type', 'application/json');
31
+ res.end(JSON.stringify({ 'dist-tags': { latest: '1.0.0' } }));
32
+ active -= 1;
33
+ }, delayMs);
34
+ });
35
+ return { server, getMaxActive: () => maxActive };
36
+ }
37
+
38
+ function writeFixture(home) {
17
39
  const extensions = join(home, '.ldm', 'extensions');
18
40
  mkdirSync(extensions, { recursive: true });
19
41
  writeFileSync(join(home, '.ldm', 'version.json'), JSON.stringify({
@@ -30,55 +52,63 @@ function writeFixture(home, fakeBin) {
30
52
  };
31
53
  }
32
54
  writeFileSync(join(extensions, 'registry.json'), JSON.stringify(registry, null, 2) + '\n');
33
-
34
- mkdirSync(fakeBin, { recursive: true });
35
- const fakeNpm = join(fakeBin, 'npm');
36
- writeFileSync(fakeNpm, `#!/usr/bin/env bash
37
- if [ "$1" = "view" ]; then
38
- sleep "\${FAKE_NPM_SLEEP:-1}"
39
- echo "1.0.0"
40
- exit 0
41
- fi
42
- echo "unexpected npm command: $*" >&2
43
- exit 64
44
- `);
45
- chmodSync(fakeNpm, 0o755);
46
55
  }
47
56
 
48
- function runStatus({ concurrency, sleepSeconds, timeoutMs }) {
49
- const home = join(tempRoot, `home-${concurrency}-${sleepSeconds}`);
50
- const fakeBin = join(tempRoot, `bin-${concurrency}-${sleepSeconds}`);
51
- writeFixture(home, fakeBin);
57
+ function runStatus({ concurrency, registryUrl, home }) {
58
+ return new Promise((resolve) => {
59
+ const child = spawn(process.execPath, [join(root, 'bin', 'ldm.js'), 'status'], {
60
+ cwd: root,
61
+ env: {
62
+ ...process.env,
63
+ HOME: home,
64
+ LDM_STATUS_NPM_REGISTRY_URL: registryUrl,
65
+ LDM_STATUS_NPM_CONCURRENCY: String(concurrency),
66
+ LDM_STATUS_NPM_TIMEOUT_MS: '2000',
67
+ LDM_STATUS_TOTAL_BUDGET_MS: '10000',
68
+ },
69
+ stdio: ['ignore', 'pipe', 'pipe'],
70
+ });
71
+
72
+ let stdout = '';
73
+ let stderr = '';
74
+ child.stdout.setEncoding('utf8');
75
+ child.stderr.setEncoding('utf8');
76
+ child.stdout.on('data', chunk => { stdout += chunk; });
77
+ child.stderr.on('data', chunk => { stderr += chunk; });
78
+ child.on('close', status => resolve({ status, stdout, stderr }));
79
+ });
80
+ }
52
81
 
82
+ async function runFixture({ concurrency, delayMs }) {
83
+ const home = join(tempRoot, `home-${concurrency}-${delayMs}`);
84
+ writeFixture(home);
85
+ const registry = createRegistryServer(delayMs);
86
+ const address = await listen(registry.server);
53
87
  const startedAt = Date.now();
54
- const result = spawnSync(process.execPath, [join(root, 'bin', 'ldm.js'), 'status'], {
55
- cwd: root,
56
- encoding: 'utf8',
57
- timeout: timeoutMs,
58
- env: {
59
- ...process.env,
60
- HOME: home,
61
- PATH: `${fakeBin}:${process.env.PATH || ''}`,
62
- FAKE_NPM_SLEEP: String(sleepSeconds),
63
- LDM_STATUS_NPM_CONCURRENCY: String(concurrency),
64
- LDM_STATUS_NPM_TIMEOUT_MS: '2000',
65
- LDM_STATUS_TOTAL_BUDGET_MS: '10000',
66
- },
88
+ const result = await runStatus({
89
+ concurrency,
90
+ home,
91
+ registryUrl: `http://${address.address}:${address.port}`,
67
92
  });
68
- return { result, elapsedMs: Date.now() - startedAt };
93
+ const elapsedMs = Date.now() - startedAt;
94
+ registry.server.closeAllConnections();
95
+ registry.server.close();
96
+ return { result, elapsedMs, maxActive: registry.getMaxActive() };
69
97
  }
70
98
 
71
99
  try {
72
- const concurrent = runStatus({ concurrency: 4, sleepSeconds: 1, timeoutMs: 7000 });
100
+ const concurrent = await runFixture({ concurrency: 4, delayMs: 500 });
73
101
  assert(concurrent.result.status === 0, `concurrent ldm status exited ${concurrent.result.status}\nstdout:\n${concurrent.result.stdout}\nstderr:\n${concurrent.result.stderr}`);
74
- assert(concurrent.elapsedMs < 6500, `concurrent ldm status should finish well before serial runtime; elapsed ${concurrent.elapsedMs}ms`);
102
+ assert(concurrent.elapsedMs < 3000, `concurrent ldm status should finish well before serial runtime; elapsed ${concurrent.elapsedMs}ms`);
103
+ assert(concurrent.maxActive >= 4, `registry server should see concurrent probes; max active ${concurrent.maxActive}`);
75
104
  assert(concurrent.result.stdout.includes(`LDM OS v${sourceVersion}`), `status should print installed LDM OS version\n${concurrent.result.stdout}`);
76
105
  assert(concurrent.result.stdout.includes('Extensions: 8'), `status should print extension count\n${concurrent.result.stdout}`);
77
106
  assert(concurrent.result.stdout.includes('ext-8: checking npm'), `status should check every staged extension\n${concurrent.result.stdout}`);
78
107
  assert(!concurrent.result.stdout.includes('Update checks skipped:'), `concurrent status should not skip checks in this fixture\n${concurrent.result.stdout}`);
79
108
 
80
- const serialFallback = runStatus({ concurrency: 1, sleepSeconds: 0.05, timeoutMs: 5000 });
109
+ const serialFallback = await runFixture({ concurrency: 1, delayMs: 10 });
81
110
  assert(serialFallback.result.status === 0, `serial fallback ldm status exited ${serialFallback.result.status}\nstdout:\n${serialFallback.result.stdout}\nstderr:\n${serialFallback.result.stderr}`);
111
+ assert(serialFallback.maxActive === 1, `serial fallback should only run one probe at a time; max active ${serialFallback.maxActive}`);
82
112
  assert(serialFallback.result.stdout.includes('ext-8: checking npm'), `serial fallback should still check every staged extension\n${serialFallback.result.stdout}`);
83
113
  assert(!serialFallback.result.stdout.includes('Update checks skipped:'), `serial fallback should not skip checks in this fixture\n${serialFallback.result.stdout}`);
84
114
  } finally {
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { createServer } from 'node:http';
3
4
  import { tmpdir } from 'node:os';
4
5
  import { dirname, join } from 'node:path';
5
- import { spawnSync } from 'node:child_process';
6
+ import { spawn } from 'node:child_process';
6
7
  import { fileURLToPath } from 'node:url';
7
8
 
8
9
  const root = dirname(dirname(fileURLToPath(import.meta.url)));
@@ -13,9 +14,38 @@ function assert(condition, message) {
13
14
  if (!condition) throw new Error(message);
14
15
  }
15
16
 
17
+ function runStatus({ home, registryUrl }) {
18
+ return new Promise((resolve) => {
19
+ const child = spawn(process.execPath, [join(root, 'bin', 'ldm.js'), 'status'], {
20
+ cwd: root,
21
+ env: {
22
+ ...process.env,
23
+ HOME: home,
24
+ LDM_STATUS_NPM_REGISTRY_URL: registryUrl,
25
+ LDM_STATUS_NPM_TIMEOUT_MS: '75',
26
+ LDM_STATUS_TOTAL_BUDGET_MS: '250',
27
+ },
28
+ stdio: ['ignore', 'pipe', 'pipe'],
29
+ });
30
+
31
+ let stdout = '';
32
+ let stderr = '';
33
+ child.stdout.setEncoding('utf8');
34
+ child.stderr.setEncoding('utf8');
35
+ child.stdout.on('data', chunk => { stdout += chunk; });
36
+ child.stderr.on('data', chunk => { stderr += chunk; });
37
+ child.on('close', status => resolve({ status, stdout, stderr }));
38
+ });
39
+ }
40
+
41
+ function listen(server) {
42
+ return new Promise((resolve) => {
43
+ server.listen(0, '127.0.0.1', () => resolve(server.address()));
44
+ });
45
+ }
46
+
16
47
  try {
17
48
  const home = join(tempRoot, 'home');
18
- const fakeBin = join(tempRoot, 'bin');
19
49
  const extensions = join(home, '.ldm', 'extensions');
20
50
 
21
51
  mkdirSync(extensions, { recursive: true });
@@ -37,33 +67,19 @@ try {
37
67
  },
38
68
  }, null, 2) + '\n');
39
69
 
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);
70
+ const server = createServer((_req, res) => {
71
+ setTimeout(() => {
72
+ res.setHeader('content-type', 'application/json');
73
+ res.end(JSON.stringify({ 'dist-tags': { latest: '9.9.9' } }));
74
+ }, 2000);
75
+ });
76
+ const address = await listen(server);
52
77
 
53
78
  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
- });
79
+ const result = await runStatus({ home, registryUrl: `http://${address.address}:${address.port}` });
66
80
  const elapsedMs = Date.now() - startedAt;
81
+ server.closeAllConnections();
82
+ server.close();
67
83
 
68
84
  assert(result.status === 0, `ldm status exited ${result.status}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`);
69
85
  assert(elapsedMs < 2500, `ldm status should return before the process timeout; elapsed ${elapsedMs}ms`);