@wipcomputer/wip-ldm-os 0.4.63 → 0.4.65

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
@@ -9,7 +9,7 @@ license: MIT
9
9
  compatibility: Requires git, npm, node. Node.js 18+.
10
10
  metadata:
11
11
  display-name: "LDM OS"
12
- version: "0.4.63"
12
+ version: "0.4.65"
13
13
  homepage: "https://github.com/wipcomputer/wip-ldm-os"
14
14
  author: "Parker Todd Brooks"
15
15
  category: infrastructure
package/bin/ldm.js CHANGED
@@ -391,6 +391,138 @@ async function installCatalogComponent(c) {
391
391
  console.log(` ✓ Installed ${c.name}`);
392
392
  }
393
393
 
394
+ // ── Bridge deploy (#245) ──
395
+ // The bridge (src/bridge/) builds to dist/bridge/ and ships in the npm package.
396
+ // After `npm install -g`, the updated files live at the npm package location but
397
+ // never get copied to ~/.ldm/extensions/lesa-bridge/dist/. This function fixes that.
398
+
399
+ function deployBridge() {
400
+ const ldmBridgeDir = join(LDM_EXTENSIONS, 'lesa-bridge');
401
+ const ocBridgeDir = join(HOME, '.openclaw', 'extensions', 'lesa-bridge');
402
+
403
+ // Deploy targets: LDM path (canonical) and OpenClaw path (where the plugin loads)
404
+ const targets = [
405
+ { dir: ldmBridgeDir, label: '~/.ldm/extensions/lesa-bridge/dist/' },
406
+ { dir: ocBridgeDir, label: '~/.openclaw/extensions/lesa-bridge/dist/' },
407
+ ].filter(t => existsSync(t.dir)); // Only deploy if the extension dir exists
408
+
409
+ if (targets.length === 0) return 0;
410
+
411
+ // Find the npm package bridge files. Try require.resolve first, fall back to known path.
412
+ let bridgeSrc = '';
413
+ try {
414
+ const pkgJson = join(__dirname, '..', 'dist', 'bridge');
415
+ if (existsSync(pkgJson)) {
416
+ bridgeSrc = pkgJson;
417
+ }
418
+ } catch {}
419
+
420
+ if (!bridgeSrc) {
421
+ // Fallback: check common global npm locations
422
+ const candidates = [
423
+ '/opt/homebrew/lib/node_modules/@wipcomputer/wip-ldm-os/dist/bridge',
424
+ join(HOME, '.npm-global/lib/node_modules/@wipcomputer/wip-ldm-os/dist/bridge'),
425
+ ];
426
+ for (const c of candidates) {
427
+ if (existsSync(c)) {
428
+ bridgeSrc = c;
429
+ break;
430
+ }
431
+ }
432
+ }
433
+
434
+ if (!bridgeSrc || !existsSync(bridgeSrc)) return 0;
435
+
436
+ // Check if files differ (compare against the LDM target, or first available)
437
+ const checkDest = join(targets[0].dir, 'dist');
438
+ let changed = false;
439
+ try {
440
+ const srcFiles = readdirSync(bridgeSrc).filter(f => f.endsWith('.js') || f.endsWith('.d.ts'));
441
+ for (const file of srcFiles) {
442
+ const srcPath = join(bridgeSrc, file);
443
+ const destPath = join(checkDest, file);
444
+ if (!existsSync(destPath)) {
445
+ changed = true;
446
+ break;
447
+ }
448
+ const srcContent = readFileSync(srcPath);
449
+ const destContent = readFileSync(destPath);
450
+ if (!srcContent.equals(destContent)) {
451
+ changed = true;
452
+ break;
453
+ }
454
+ }
455
+ // Also check if there are stale files in the target that aren't in the source
456
+ if (!changed) {
457
+ const destFiles = readdirSync(checkDest).filter(f => f.endsWith('.js'));
458
+ const srcFileSet = new Set(srcFiles);
459
+ for (const file of destFiles) {
460
+ if (!srcFileSet.has(file)) {
461
+ changed = true; // stale chunk file found
462
+ break;
463
+ }
464
+ }
465
+ }
466
+ } catch {
467
+ changed = true; // if comparison fails, copy anyway
468
+ }
469
+
470
+ if (!changed) return 0;
471
+
472
+ if (DRY_RUN) {
473
+ console.log(` + would deploy bridge files to ${targets.map(t => t.label).join(' + ')}`);
474
+ return 0;
475
+ }
476
+
477
+ const srcFiles = readdirSync(bridgeSrc).filter(f => f.endsWith('.js') || f.endsWith('.d.ts'));
478
+ let totalDeployed = 0;
479
+
480
+ for (const target of targets) {
481
+ const dest = join(target.dir, 'dist');
482
+ try {
483
+ mkdirSync(dest, { recursive: true });
484
+
485
+ // Clean stale .js files before copying (chunk hashes change between builds)
486
+ try {
487
+ const existing = readdirSync(dest).filter(f => f.endsWith('.js'));
488
+ const srcFileSet = new Set(srcFiles.filter(f => f.endsWith('.js')));
489
+ for (const file of existing) {
490
+ if (!srcFileSet.has(file)) {
491
+ unlinkSync(join(dest, file));
492
+ }
493
+ }
494
+ } catch {}
495
+
496
+ for (const file of srcFiles) {
497
+ cpSync(join(bridgeSrc, file), join(dest, file));
498
+ }
499
+ console.log(` + bridge deployed to ${target.label} (${srcFiles.length} files)`);
500
+ installLog(`Bridge deployed: ${srcFiles.length} files to ${target.label}`);
501
+ totalDeployed += srcFiles.length;
502
+ } catch (e) {
503
+ console.log(` ! bridge deploy to ${target.label} failed: ${e.message}`);
504
+ }
505
+ }
506
+
507
+ // Re-register MCP server to point to the canonical LDM path
508
+ if (totalDeployed > 0 && existsSync(join(ldmBridgeDir, 'dist', 'mcp-server.js'))) {
509
+ try {
510
+ const mcpPath = join(ldmBridgeDir, 'dist', 'mcp-server.js');
511
+ execSync(`claude mcp add lesa-bridge --scope user -- node ${mcpPath}`, {
512
+ stdio: 'pipe',
513
+ timeout: 10000,
514
+ });
515
+ console.log(` + MCP registration updated: lesa-bridge -> ~/.ldm/extensions/lesa-bridge/dist/mcp-server.js`);
516
+ installLog('MCP registration updated: lesa-bridge -> ~/.ldm/extensions/lesa-bridge/dist/mcp-server.js');
517
+ } catch (e) {
518
+ // Non-fatal: MCP registration is a convenience, not a requirement
519
+ console.log(` ! MCP registration update failed: ${e.message}`);
520
+ }
521
+ }
522
+
523
+ return totalDeployed;
524
+ }
525
+
394
526
  // ── ldm init ──
395
527
 
396
528
  async function cmdInit() {
@@ -812,6 +944,9 @@ async function cmdInit() {
812
944
  }
813
945
  }
814
946
 
947
+ // Deploy bridge files to all targets and re-register MCP (#245, #251)
948
+ deployBridge();
949
+
815
950
  // Clean up dead backup triggers (#207)
816
951
  // Bug: three backup systems were competing. Only ai.openclaw.ldm-backup (3am) works.
817
952
  // The old cron entry (LDMDevTools.app) and com.wipcomputer.daily-backup are dead.
@@ -1183,6 +1318,11 @@ async function cmdInstallCatalog() {
1183
1318
 
1184
1319
  autoDetectExtensions();
1185
1320
 
1321
+ // Deploy bridge files after self-update or on every catalog install (#245, #251)
1322
+ // After npm install -g, the new bridge files are in the npm package but not
1323
+ // in the extension directories. This copies them to both LDM and OpenClaw targets.
1324
+ deployBridge();
1325
+
1186
1326
  const { detectSystemState, reconcileState, formatReconciliation } = await import('../lib/state.mjs');
1187
1327
  const state = detectSystemState();
1188
1328
  const reconciled = reconcileState(state);
@@ -103,7 +103,8 @@ async function sendMessage(openclawDir, message, options) {
103
103
  method: "POST",
104
104
  headers: {
105
105
  Authorization: `Bearer ${token}`,
106
- "Content-Type": "application/json"
106
+ "Content-Type": "application/json",
107
+ "x-openclaw-scopes": "operator.read,operator.write"
107
108
  },
108
109
  body: JSON.stringify({
109
110
  model: `openclaw/${agentId}`,
@@ -8,7 +8,7 @@ import {
8
8
  searchConversations,
9
9
  searchWorkspace,
10
10
  sendMessage
11
- } from "./chunk-QZ4DNVJM.js";
11
+ } from "./chunk-XVIE3HLZ.js";
12
12
 
13
13
  // cli.ts
14
14
  import { existsSync, statSync } from "fs";
@@ -17,7 +17,7 @@ import {
17
17
  searchConversations,
18
18
  searchWorkspace,
19
19
  sendMessage
20
- } from "./chunk-QZ4DNVJM.js";
20
+ } from "./chunk-XVIE3HLZ.js";
21
21
  export {
22
22
  LDM_ROOT,
23
23
  blobToEmbedding,
@@ -9,7 +9,7 @@ import {
9
9
  searchConversations,
10
10
  searchWorkspace,
11
11
  sendMessage
12
- } from "./chunk-QZ4DNVJM.js";
12
+ } from "./chunk-XVIE3HLZ.js";
13
13
 
14
14
  // mcp-server.ts
15
15
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.4.63",
3
+ "version": "0.4.65",
4
4
  "type": "module",
5
5
  "description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
6
6
  "engines": {
@@ -196,6 +196,7 @@ export async function sendMessage(
196
196
  headers: {
197
197
  Authorization: `Bearer ${token}`,
198
198
  "Content-Type": "application/json",
199
+ "x-openclaw-scopes": "operator.read,operator.write",
199
200
  },
200
201
  body: JSON.stringify({
201
202
  model: `openclaw/${agentId}`,