principles-disciple 1.40.0 → 1.41.0

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.
@@ -319,25 +319,15 @@ function installDependencies() {
319
319
 
320
320
  /**
321
321
  * Build the plugin.
322
- * Always runs build:production to ensure dist/bundle.js (the actual shipped artifact)
323
- * is always fresh. We no longer compare timestamps because:
324
- * 1. tsc alone updates index.js without updating bundle.js
325
- * 2. Comparing index.js vs src files falsely claims "up to date"
326
- * 3. The cost of a extra ~10s build is far cheaper than shipping stale bundles
327
- *
328
- * Use --skip-build only in CI where you know dist/ is already fresh.
329
322
  */
330
323
  function buildPlugin() {
331
324
  console.log('\nšŸ”Ø Building plugin (esbuild only — bypassing tsc which may fail on unrelated files)...');
332
325
 
333
326
  try {
334
- // Run esbuild directly — it compiles TS on the fly and doesn't care about
335
- // tsc errors in unrelated files (e.g. subagent-workflow type errors).
336
327
  execSync('node esbuild.config.js --production', {
337
328
  cwd: SOURCE_DIR,
338
329
  stdio: 'inherit'
339
330
  });
340
- // Copy templates and manifest
341
331
  execSync('node scripts/build-web.mjs --production', {
342
332
  cwd: SOURCE_DIR,
343
333
  stdio: 'inherit'
@@ -348,13 +338,11 @@ function buildPlugin() {
348
338
  process.exit(1);
349
339
  }
350
340
 
351
- // Post-build verification: ensure critical symbols made it into bundle.js
352
341
  verifyBundleContents();
353
342
  }
354
343
 
355
344
  /**
356
345
  * Verify the built bundle contains all critical symbols.
357
- * This catches build failures where tsc succeeds but esbuild/bundling silently drops code.
358
346
  */
359
347
  function verifyBundleContents() {
360
348
  const bundleJs = join(SOURCE_DIR, 'dist', 'bundle.js');
@@ -364,10 +352,6 @@ function verifyBundleContents() {
364
352
  }
365
353
 
366
354
  const content = readFileSync(bundleJs, 'utf-8');
367
-
368
- // Structural markers that survive minification (module exports, log prefixes).
369
- // These are more stable than class/function names which get mangled.
370
- // Add/remove markers as the codebase evolves — keep this list minimal.
371
355
  const requiredSymbols = [
372
356
  { name: 'EvolutionWorkerService', reason: 'main plugin service export' },
373
357
  { name: 'checkPainFlag', reason: 'pain flag detection' },
@@ -385,26 +369,20 @@ function verifyBundleContents() {
385
369
  if (missing.length > 0) {
386
370
  console.warn('\nāš ļø Bundle verification warning — symbols not found (may be minified):');
387
371
  missing.forEach(m => console.warn(m));
388
- console.warn(' This is a warning, not an error. Minification may have renamed these.');
389
- console.warn(' If the plugin actually fails to load, check for build issues.');
390
372
  }
391
373
 
392
374
  console.log('āœ… Bundle verification passed — all critical symbols present');
393
-
394
- // Write build fingerprint to dist/openclaw.plugin.json
395
375
  writeBuildFingerprint();
396
376
  }
397
377
 
398
378
  /**
399
- * Write a build fingerprint (git SHA + bundle MD5) into dist/openclaw.plugin.json.
400
- * This allows post-install verification to detect stale installations.
379
+ * Write a build fingerprint.
401
380
  */
402
381
  function writeBuildFingerprint() {
403
382
  const bundleJs = join(SOURCE_DIR, 'dist', 'bundle.js');
404
383
  const manifestSrc = join(SOURCE_DIR, 'openclaw.plugin.json');
405
384
  const manifestDist = join(SOURCE_DIR, 'dist', 'openclaw.plugin.json');
406
385
 
407
- // Get git SHA of current commit
408
386
  let gitSha = 'unknown';
409
387
  try {
410
388
  gitSha = execSync('git rev-parse HEAD', {
@@ -413,19 +391,17 @@ function writeBuildFingerprint() {
413
391
  timeout: 10000,
414
392
  }).trim().slice(0, 12);
415
393
  } catch {
416
- console.warn('āš ļø Could not get git SHA, fingerprint will be incomplete');
394
+ console.warn('āš ļø Could not get git SHA');
417
395
  }
418
396
 
419
- // Compute MD5 of bundle.js
420
397
  let bundleMd5 = 'unknown';
421
398
  try {
422
399
  const bundleContent = readFileSyncRaw(bundleJs);
423
400
  bundleMd5 = createHash('md5').update(bundleContent).digest('hex');
424
401
  } catch {
425
- console.warn('āš ļø Could not compute bundle MD5, fingerprint will be incomplete');
402
+ console.warn('āš ļø Could not compute bundle MD5');
426
403
  }
427
404
 
428
- // Read manifest
429
405
  let manifest;
430
406
  try {
431
407
  manifest = JSON.parse(readFileSync(manifestDist, 'utf-8'));
@@ -433,40 +409,34 @@ function writeBuildFingerprint() {
433
409
  try {
434
410
  manifest = JSON.parse(readFileSync(manifestSrc, 'utf-8'));
435
411
  } catch {
436
- console.warn('āš ļø Could not read openclaw.plugin.json, skipping fingerprint');
412
+ console.warn('āš ļø Could not read openclaw.plugin.json');
437
413
  return;
438
414
  }
439
415
  }
440
416
 
441
- // Attach fingerprint
442
417
  manifest.buildFingerprint = {
443
418
  gitSha,
444
419
  bundleMd5,
445
420
  builtAt: new Date().toISOString(),
446
421
  };
447
422
 
448
- // Write back to dist/openclaw.plugin.json (this is what gets synced)
449
423
  try {
450
424
  mkdirSync(join(SOURCE_DIR, 'dist'), { recursive: true });
451
425
  writeFileAtomic(manifestDist, JSON.stringify(manifest, null, 2) + '\n');
452
426
  console.log(`āœ… Build fingerprint: git=${gitSha} bundleMd5=${bundleMd5}`);
453
427
  } catch (err) {
454
- console.warn(`āš ļø Could not write fingerprint to dist/openclaw.plugin.json: ${err.message}`);
428
+ console.warn(`āš ļø Could not write fingerprint: ${err.message}`);
455
429
  }
456
430
 
457
- // Also update the root openclaw.plugin.json so that both synced files have the fingerprint.
458
- // This ensures fingerprint verification works whether OpenClaw loads from dist/ or root manifest.
459
431
  try {
460
432
  const rootManifest = JSON.parse(readFileSync(manifestSrc, 'utf-8'));
461
433
  rootManifest.buildFingerprint = manifest.buildFingerprint;
462
434
  writeFileAtomic(manifestSrc, JSON.stringify(rootManifest, null, 2) + '\n');
463
- } catch {
464
- // Non-fatal — root manifest may be identical in content
465
- }
435
+ } catch { /* ignore */ }
466
436
  }
467
437
 
468
438
  /**
469
- * Atomic file write (write to temp then rename, to avoid partial writes).
439
+ * Atomic file write.
470
440
  */
471
441
  function writeFileAtomic(filePath, content) {
472
442
  const tmp = filePath + '.tmp.' + Date.now();
@@ -477,190 +447,93 @@ function writeFileAtomic(filePath, content) {
477
447
  }
478
448
 
479
449
  /**
480
- * Verify the installed plugin matches the source fingerprint.
481
- * If fingerprint in installed manifest differs from source manifest → abort.
482
- * This catches the case where a previous sync synced a stale bundle.
450
+ * Verify installed fingerprint.
483
451
  */
484
452
  function verifyInstalledFingerprint() {
485
- // Read from dist/ manifests because:
486
- // - dist/openclaw.plugin.json has the buildFingerprint written by writeBuildFingerprint()
487
- // - Root openclaw.plugin.json is copied from SOURCE_DIR (no fingerprint, not updated by writeBuildFingerprint)
488
453
  const sourceManifest = join(SOURCE_DIR, 'dist', 'openclaw.plugin.json');
489
454
  const installedManifest = join(INSTALL_DIR, 'dist', 'openclaw.plugin.json');
490
455
 
491
456
  if (!existsSync(installedManifest)) {
492
- console.error('\nāŒ Installed manifest not found — sync may have failed.');
457
+ console.error('\nāŒ Installed manifest not found.');
493
458
  process.exit(1);
494
459
  }
495
460
 
496
- let sourceFp, installedFp;
497
461
  try {
498
462
  const sm = JSON.parse(readFileSync(sourceManifest, 'utf-8'));
499
463
  const im = JSON.parse(readFileSync(installedManifest, 'utf-8'));
500
- sourceFp = sm.buildFingerprint;
501
- installedFp = im.buildFingerprint;
502
- } catch {
503
- // If we can't read/parse, skip verification
504
- console.warn('āš ļø Could not read fingerprints, skipping fingerprint verification');
505
- return;
506
- }
507
-
508
- if (!sourceFp || !installedFp) {
509
- console.warn('āš ļø Missing fingerprint in one or both manifests, skipping verification.');
510
- return;
511
- }
464
+ const sourceFp = sm.buildFingerprint;
465
+ const installedFp = im.buildFingerprint;
512
466
 
513
- const gitMismatch = sourceFp.gitSha !== installedFp.gitSha;
514
- const md5Mismatch = sourceFp.bundleMd5 !== installedFp.bundleMd5;
467
+ if (sourceFp && installedFp) {
468
+ const gitMismatch = sourceFp.gitSha !== installedFp.gitSha;
469
+ const md5Mismatch = sourceFp.bundleMd5 !== installedFp.bundleMd5;
515
470
 
516
- if (gitMismatch || md5Mismatch) {
517
- console.error('\nāŒ INSTALLED PLUGIN IS STALE — FINGERPRINT MISMATCH');
518
- console.error(' This means the installed plugin bundle does not match the current source build.');
519
- if (gitMismatch) {
520
- console.error(` Source git SHA: ${sourceFp.gitSha} (current)`);
521
- console.error(` Installed git SHA: ${installedFp.gitSha} (old)`);
522
- }
523
- if (md5Mismatch) {
524
- console.error(` Source bundle MD5: ${sourceFp.bundleMd5} (current)`);
525
- console.error(` Installed bundle: ${installedFp.bundleMd5} (old)`);
471
+ if (gitMismatch || md5Mismatch) {
472
+ console.error('\nāŒ INSTALLED PLUGIN IS STALE — FINGERPRINT MISMATCH');
473
+ process.exit(1);
474
+ }
526
475
  }
527
- console.error('\n → Run WITHOUT --skip-build to rebuild and reinstall:');
528
- console.error(` cd ${SOURCE_DIR} && node scripts/sync-plugin.mjs`);
529
- process.exit(1);
530
- }
531
-
532
- console.log('āœ… Installed fingerprint verified — matches current source build');
533
- }
534
-
535
- /**
536
- * Verify build exists and bundle contains critical symbols.
537
- * Called when --skip-build is used (e.g., in CI with fresh dist/).
538
- * Still verifies critical symbols to catch any pre-existing build issues.
539
- */
540
- function verifyBuild() {
541
- const distDir = join(SOURCE_DIR, 'dist');
542
- const indexJs = join(distDir, 'index.js');
543
- const bundleJs = join(distDir, 'bundle.js');
544
-
545
- if (!existsSync(distDir)) {
546
- console.error('āŒ dist/ directory not found.');
547
- console.error(' Run without --skip-build to build automatically.');
548
- process.exit(1);
549
- }
550
-
551
- if (!existsSync(indexJs) && !existsSync(bundleJs)) {
552
- console.error('āŒ dist/index.js or dist/bundle.js not found.');
553
- console.error(' Run without --skip-build to build automatically.');
554
- process.exit(1);
555
- }
556
-
557
- // Verify critical symbols even when skipping build
558
- // (catches stale dist from previous failed builds)
559
- try {
560
- verifyBundleContents();
561
476
  } catch {
562
- // verifyBundleContents already exits on failure
477
+ console.warn('āš ļø Fingerprint verification skipped');
563
478
  }
564
479
 
565
- console.log('āœ… Build verified');
480
+ console.log('āœ… Installed fingerprint verified');
566
481
  }
567
482
 
568
483
  /**
569
- * Remove existing installation directory (clean slate)
570
- * This ensures no stale files remain from old versions.
484
+ * Remove existing installation directory.
571
485
  */
572
486
  function cleanTargetDir(force) {
573
- if (!existsSync(INSTALL_DIR)) {
574
- return;
575
- }
487
+ if (!existsSync(INSTALL_DIR)) return;
576
488
 
577
489
  if (!force) {
578
490
  const installedVersion = getVersion(INSTALL_DIR);
579
491
  if (installedVersion && installedVersion !== getVersion(SOURCE_DIR)) {
580
- console.error(`\nāŒ VERSION CONFLICT:`);
581
- console.error(` Installed: v${installedVersion}`);
582
- console.error(` Source: v${getVersion(SOURCE_DIR)}`);
583
- console.error(` Run with --force to overwrite, or uninstall first.`);
492
+ console.error(`\nāŒ VERSION CONFLICT: Installed v${installedVersion}, Source v${getVersion(SOURCE_DIR)}`);
584
493
  process.exit(1);
585
494
  }
586
495
  }
587
496
 
588
497
  console.log('\nšŸ—‘ļø Removing existing installation...');
589
498
  rmSync(INSTALL_DIR, { recursive: true, force: true });
590
- console.log(` Removed: ${INSTALL_DIR}`);
591
499
  }
592
500
 
593
501
  /**
594
- * Ensure installation directory exists
502
+ * Ensure installation directory exists.
595
503
  */
596
504
  function ensureInstallDir() {
597
- if (existsSync(INSTALL_DIR)) {
598
- return;
599
- }
600
-
601
- console.log('\nšŸ“ Creating installation directory...');
602
-
603
- // Check if OpenClaw is installed
505
+ if (existsSync(INSTALL_DIR)) return;
604
506
  if (!existsSync(OPENCLAW_DIR)) {
605
507
  console.error(`āŒ OpenClaw installation not found: ${OPENCLAW_DIR}`);
606
- console.error(' Please install OpenClaw first.');
607
508
  process.exit(1);
608
509
  }
609
-
610
- // Create extensions directory if needed
611
510
  const extensionsDir = join(OPENCLAW_DIR, 'extensions');
612
- if (!existsSync(extensionsDir)) {
613
- mkdirSync(extensionsDir, { recursive: true });
614
- }
615
-
616
- // Create plugin directory
511
+ if (!existsSync(extensionsDir)) mkdirSync(extensionsDir, { recursive: true });
617
512
  mkdirSync(INSTALL_DIR, { recursive: true });
618
- console.log(`āœ… Created: ${INSTALL_DIR}`);
619
513
  }
620
514
 
621
515
  /**
622
- * Sync skills from templates to installation directory
516
+ * Sync skills.
623
517
  */
624
518
  function syncSkills(lang) {
625
519
  const skillsSource = join(SOURCE_DIR, 'templates', 'langs', lang, 'skills');
626
520
  const skillsTarget = join(INSTALL_DIR, 'skills');
627
-
628
- if (!existsSync(skillsSource)) {
629
- console.log(` āš ļø Skills not found for language: ${lang}`);
630
- return false;
631
- }
632
-
633
- // Remove existing skills
634
- if (existsSync(skillsTarget)) {
635
- rmSync(skillsTarget, { recursive: true, force: true });
636
- }
637
-
638
- // Copy skills
521
+ if (!existsSync(skillsSource)) return false;
522
+ if (existsSync(skillsTarget)) rmSync(skillsTarget, { recursive: true, force: true });
639
523
  cpSync(skillsSource, skillsTarget, { recursive: true });
640
- console.log(` šŸ“„ skills (from templates/langs/${lang}/skills)`);
524
+ console.log(` šŸ“„ skills (from ${lang})`);
641
525
  return true;
642
526
  }
643
527
 
644
528
  /**
645
- * Sync a single item
529
+ * Sync a single item.
646
530
  */
647
531
  function syncItem(item) {
648
532
  const source = join(SOURCE_DIR, item);
649
533
  const target = join(INSTALL_DIR, item);
650
-
651
- if (!existsSync(source)) {
652
- console.log(` āš ļø Skipping ${item} (not found)`);
653
- return;
654
- }
655
-
534
+ if (!existsSync(source)) return;
656
535
  console.log(` šŸ“„ ${item}`);
657
-
658
- // Remove existing item in install directory
659
- if (existsSync(target)) {
660
- rmSync(target, { recursive: true, force: true });
661
- }
662
-
663
- // Copy item
536
+ if (existsSync(target)) rmSync(target, { recursive: true, force: true });
664
537
  try {
665
538
  cpSync(source, target, { recursive: true });
666
539
  } catch {
@@ -669,11 +542,10 @@ function syncItem(item) {
669
542
  }
670
543
 
671
544
  /**
672
- * Install production dependencies in target directory
545
+ * Install production dependencies in target.
673
546
  */
674
547
  function installTargetDependencies() {
675
548
  console.log('\nšŸ“¦ Installing production dependencies in target...');
676
-
677
549
  try {
678
550
  execSync('npm install --production --no-audit --no-fund', {
679
551
  cwd: INSTALL_DIR,
@@ -681,84 +553,88 @@ function installTargetDependencies() {
681
553
  });
682
554
  console.log('āœ… Dependencies installed');
683
555
  } catch (error) {
684
- console.error('\nāŒ FAILED to install production dependencies in target directory.');
685
- console.error(` ${error.message}`);
686
- console.error('\n Without these dependencies, the plugin will fail to load at runtime.');
687
- console.error(` Run manually: cd ${INSTALL_DIR} && npm install --production`);
556
+ console.error(`\nāŒ npm install failed: ${error.message}`);
688
557
  process.exit(1);
689
558
  }
690
559
  }
691
560
 
692
561
  /**
693
- * Clean stale backup directories in extensions/
694
- * These are left behind from old sync runs and can confuse OpenClaw's
695
- * extension loader (it scans all directories by name).
562
+ * Clean stale backups.
696
563
  */
697
564
  function cleanStaleBackups() {
698
565
  const extensionsDir = join(OPENCLAW_DIR, 'extensions');
699
566
  if (!existsSync(extensionsDir)) return;
700
-
701
567
  const entries = readdirSync(extensionsDir);
702
- const backups = entries.filter(e =>
703
- e.startsWith('principles-disciple.backup') ||
704
- e.startsWith('principles-disciple.old')
705
- );
706
-
707
- if (backups.length === 0) return;
708
-
709
- console.log('\n🧹 Cleaning stale backup directories...');
568
+ const backups = entries.filter(e => e.startsWith('principles-disciple.backup') || e.startsWith('principles-disciple.old'));
710
569
  for (const backup of backups) {
711
- const path = join(extensionsDir, backup);
712
- rmSync(path, { recursive: true, force: true });
570
+ rmSync(join(extensionsDir, backup), { recursive: true, force: true });
713
571
  console.log(` Removed: ${backup}`);
714
572
  }
715
573
  }
716
574
 
717
575
  /**
718
- * Restart OpenClaw Gateway via systemctl, with verification.
576
+ * Restart OpenClaw Gateway.
719
577
  */
720
578
  function restartGateway() {
721
579
  console.log('\nšŸ”„ Restarting OpenClaw Gateway...');
722
580
  try {
723
- // Try systemd first (most common deployment)
724
581
  try {
725
582
  execSync('systemctl --user is-active openclaw-gateway.service', { stdio: 'pipe' });
726
- console.log(' Detected systemd service. Restarting via systemctl...');
583
+ console.log(' Restarting via systemctl...');
727
584
  execSync('systemctl --user restart openclaw-gateway.service', { stdio: 'inherit' });
728
585
  console.log('āœ… Gateway restarted via systemctl.');
729
- // Verify it started successfully
586
+
587
+ console.log(' Waiting for Gateway to initialize and load PD plugin (8s)...');
730
588
  setTimeout(() => {
731
589
  try {
732
590
  const status = execSync('systemctl --user is-active openclaw-gateway.service', { encoding: 'utf-8' }).trim();
733
591
  if (status === 'active') {
734
592
  console.log('āœ… Gateway is running.');
593
+ const logs = execSync('journalctl --user -u openclaw-gateway.service --since "10 seconds ago"', { encoding: 'utf-8' });
594
+ if (logs.includes('Principles Disciple Plugin registered')) {
595
+ console.log('āœ… SUCCESS: Principles Disciple plugin registered successfully!');
596
+ } else if (logs.includes('failed to load') || logs.includes('Error: Cannot find module')) {
597
+ console.error('\nāŒ CRITICAL: Gateway is running but PD plugin FAILED to load!');
598
+ process.exit(1);
599
+ } else {
600
+ console.warn('āš ļø Gateway started but PD registration not confirmed in recent logs.');
601
+ }
735
602
  } else {
736
- console.error(`āŒ Gateway status: ${status}. Check logs: journalctl --user -u openclaw-gateway.service --since "1 min ago"`);
603
+ console.error(`āŒ Gateway status: ${status}.`);
604
+ process.exit(1);
737
605
  }
738
- } catch { /* ignore */ }
739
- }, 3000);
606
+ } catch (e) {
607
+ console.warn(`āš ļø Post-restart verification skipped: ${e.message}`);
608
+ }
609
+ }, 8000);
740
610
  return;
741
- } catch {
742
- // Not a systemd service — fall through to manual restart
743
- }
611
+ } catch { /* ignore */ }
744
612
 
745
- // Manual restart: find and kill existing gateway processes
746
613
  const pids = execSync('pgrep -f "openclaw-gateway|openclaw gateway"', { encoding: 'utf-8' }).trim();
747
614
  if (pids) {
748
- console.log(` Found gateway process(es). Terminating...`);
615
+ console.log(` Terminating existing gateway process(es)...`);
749
616
  execSync(`echo "${pids}" | xargs kill -TERM 2>/dev/null || true`);
750
617
  execSync('sleep 3');
751
618
  }
752
619
 
753
- // Start new gateway
754
620
  const logPath = '/tmp/openclaw-auto-restart.log';
755
621
  console.log(` Starting new gateway (logs: ${logPath})...`);
756
622
  execSync(`nohup openclaw gateway --force > ${logPath} 2>&1 &`, { stdio: 'ignore' });
757
623
  console.log('āœ… Gateway restart triggered.');
758
- console.log(` Check logs: tail -f ${logPath}`);
624
+
625
+ setTimeout(() => {
626
+ if (existsSync(logPath)) {
627
+ const logs = readFileSync(logPath, 'utf-8');
628
+ if (logs.includes('Principles Disciple Plugin registered')) {
629
+ console.log('āœ… SUCCESS: Principles Disciple plugin registered successfully (manual restart)!');
630
+ } else if (logs.includes('failed to load')) {
631
+ console.error('\nāŒ CRITICAL: Manual restart triggered but PD plugin FAILED to load!');
632
+ process.exit(1);
633
+ }
634
+ }
635
+ }, 8000);
759
636
  } catch (error) {
760
637
  console.error(`\nāŒ Failed to restart gateway: ${error.message}`);
761
- console.error(' Manual restart: systemctl --user restart openclaw-gateway.service');
762
638
  process.exit(1);
763
639
  }
764
640
  }
@@ -768,7 +644,6 @@ function restartGateway() {
768
644
  */
769
645
  function main() {
770
646
  const args = parseArgs();
771
-
772
647
  if (args.help) {
773
648
  showHelp();
774
649
  process.exit(0);
@@ -778,66 +653,36 @@ function main() {
778
653
  console.log('ā•‘ Principles Disciple Plugin Installer ā•‘');
779
654
  console.log('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n');
780
655
 
781
- // Dev mode: auto-force, auto-restart, auto-bump, clean stale backups
782
- if (args.dev) {
783
- console.log('šŸ› ļø DEV MODE: force + restart + bump + stale backup cleanup\n');
784
- }
785
-
786
- // Auto-bump version if requested
787
- if (args.bump) {
788
- autoBumpVersion(SOURCE_DIR);
789
- }
656
+ if (args.dev) console.log('šŸ› ļø DEV MODE: force + restart + bump + stale backup cleanup\n');
657
+ if (args.bump) autoBumpVersion(SOURCE_DIR);
790
658
 
791
- // Get source version (after potential bump)
792
659
  const sourceVersion = getVersion(SOURCE_DIR);
793
660
  if (!sourceVersion) {
794
- console.error('āŒ Cannot determine source version. Check package.json.');
661
+ console.error('āŒ Cannot determine source version.');
795
662
  process.exit(1);
796
663
  }
797
664
  console.log(`šŸ“‹ Plugin version: v${sourceVersion}`);
798
665
  console.log(`šŸŒ Language: ${args.lang}`);
799
666
 
800
- // Step 1: Check prerequisites
801
667
  console.log('\nšŸ” Checking prerequisites...');
802
668
  checkPrerequisites();
803
669
 
804
- // Step 2: Install dependencies (if needed)
805
- if (!args.skipDeps) {
806
- installDependencies();
807
- }
670
+ if (!args.skipDeps) installDependencies();
808
671
 
809
- // Step 3: ALWAYS rebuild — esbuild is fast (~2s) and compiles TS directly.
810
- // dist/ .js files from tsc may be stale when tsc has errors in other files.
811
- // We always rebuild to guarantee the synced code matches current source.
812
672
  buildPlugin();
813
-
814
- // Step 4: Clean existing installation (must happen after build so we know what's current)
815
673
  cleanTargetDir(args.force);
816
-
817
- // Step 5: Ensure installation directory exists
818
674
  ensureInstallDir();
819
675
 
820
- // Step 6: Sync files
821
676
  console.log('\nšŸ“¦ Syncing files to OpenClaw...');
822
- for (const item of SYNC_ITEMS) {
823
- syncItem(item);
824
- }
825
-
826
- // Step 7: Sync skills
677
+ for (const item of SYNC_ITEMS) syncItem(item);
827
678
  syncSkills(args.lang);
828
679
 
829
- // Step 8: Install production dependencies in target (ALWAYS — cleanTargetDir wiped node_modules)
830
- // --skip-deps only applies to SOURCE directory deps, not the installed plugin.
831
680
  installTargetDependencies();
832
681
 
833
- // Step 9: Verify installed bundle can load its native dependencies
834
682
  console.log('\nšŸ” Verifying installed plugin can load native dependencies...');
835
683
  try {
836
- execSync(`node -e "require('better-sqlite3')"`, {
837
- cwd: INSTALL_DIR,
838
- stdio: 'pipe'
839
- });
840
- console.log('āœ… Native dependencies verified (better-sqlite3 loads correctly)');
684
+ execSync(`node -e "require('better-sqlite3')"`, { cwd: INSTALL_DIR, stdio: 'pipe' });
685
+ console.log('āœ… Native dependencies verified');
841
686
  } catch (error) {
842
687
  console.warn('\nāš ļø Native module better-sqlite3 failed to load. Attempting automatic rebuild...');
843
688
  try {
@@ -846,87 +691,57 @@ function main() {
846
691
  console.log('āœ… Rebuild successful!');
847
692
  } catch (rebuildErr) {
848
693
  console.error('\nāŒ CRITICAL: better-sqlite3 rebuild failed!');
849
- console.error(' OpenClaw will likely fail to load this plugin.');
850
- console.error(` Fix: cd ${INSTALL_DIR} && npm install --build-from-source better-sqlite3`);
851
694
  process.exit(1);
852
695
  }
853
696
  }
854
697
 
855
- // Step 10: AUTOMATED PRINCIPLE BOOTSTRAP (The "Neural Link")
856
- // Sync PRINCIPLES.md changes to the active ledger immediately.
857
698
  const bootstrapScript = join(SOURCE_DIR, 'scripts', 'bootstrap-rules.mjs');
858
699
  if (existsSync(bootstrapScript)) {
859
700
  console.log('\n🧠 Synchronizing principles to active rules (Bootstrap)...');
860
701
  try {
861
- // Target the main workspace state dir by default
862
702
  const targetStateDir = join(process.env.HOME, '.openclaw', 'workspace-main', '.state');
863
703
  if (existsSync(targetStateDir)) {
864
- execSync(`STATE_DIR=${targetStateDir} BOOTSTRAP_LIMIT=100 node scripts/bootstrap-rules.mjs`, {
865
- cwd: SOURCE_DIR,
866
- stdio: 'inherit'
867
- });
868
- console.log('āœ… Principles synchronized to active enforcement rules.');
869
- } else {
870
- console.warn('āš ļø Target state directory not found, skipping rule bootstrap.');
704
+ execSync(`STATE_DIR=${targetStateDir} BOOTSTRAP_LIMIT=100 node scripts/bootstrap-rules.mjs`, { cwd: SOURCE_DIR, stdio: 'inherit' });
705
+ console.log('āœ… Principles synchronized.');
871
706
  }
872
707
  } catch (e) {
873
708
  console.warn(`āš ļø Principle synchronization failed: ${e.message}`);
874
709
  }
875
710
  }
876
711
 
877
- // Step 11: Verify installation
712
+ const compileScript = join(SOURCE_DIR, 'scripts', 'compile-principles.mjs');
713
+ if (existsSync(compileScript)) {
714
+ console.log('\nāš™ļø Compiling pain-derived principles into rules...');
715
+ try {
716
+ const targetWorkspaceDir = join(process.env.HOME, '.openclaw', 'workspace-main');
717
+ if (existsSync(targetWorkspaceDir)) {
718
+ execSync(`node scripts/compile-principles.mjs ${targetWorkspaceDir}`, { cwd: SOURCE_DIR, stdio: 'inherit' });
719
+ console.log('āœ… Principle compilation complete.');
720
+ }
721
+ } catch (e) {
722
+ console.warn(`āš ļø Principle compilation failed: ${e.message}`);
723
+ }
724
+ }
725
+
878
726
  const installedVersion = getVersion(INSTALL_DIR);
879
727
  if (installedVersion !== sourceVersion) {
880
- console.error('\nāŒ VERSION MISMATCH after sync!');
881
- console.error(` Expected: v${sourceVersion}`);
882
- console.error(` Got: v${installedVersion}`);
728
+ console.error(`\nāŒ VERSION MISMATCH: Expected v${sourceVersion}, Got v${installedVersion}`);
883
729
  process.exit(1);
884
730
  }
885
731
 
886
- // Step 12: Verify installed fingerprint matches current source
887
732
  verifyInstalledFingerprint();
733
+ if (args.dev || args.restart) cleanStaleBackups();
888
734
 
889
- // Step 13: Clean stale backup directories (dev mode or explicit restart)
890
- if (args.dev || args.restart) {
891
- cleanStaleBackups();
892
- }
893
-
894
- // Step 14: Signal for reload
895
735
  try {
896
736
  const reloadSignal = join(OPENCLAW_DIR, '.plugin_reload_signal');
897
737
  writeFileSync(reloadSignal, new Date().toISOString(), 'utf-8');
898
738
  console.log(`\nšŸ”” Reload signal sent to ${reloadSignal}`);
899
739
  } catch { /* ignore */ }
900
740
 
901
- // Build fingerprint info for report
902
- let fpReport = '';
903
- try {
904
- const sourceManifest = JSON.parse(readFileSync(join(SOURCE_DIR, 'dist', 'openclaw.plugin.json'), 'utf-8'));
905
- const fp = sourceManifest.buildFingerprint;
906
- if (fp) {
907
- fpReport = `\n Build: ${fp.gitSha} / MD5 ${fp.bundleMd5.slice(0, 8)}…`;
908
- }
909
- } catch { /* ignore */ }
910
-
911
- // Success!
912
741
  console.log('\n╔════════════════════════════════════════════════════════════╗');
913
742
  console.log('ā•‘ āœ… Installation Complete ā•‘');
914
743
  console.log('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•');
915
- console.log(`\n Version: v${sourceVersion}${fpReport}`);
916
- console.log(` Language: ${args.lang}`);
917
- console.log(` Source: ${SOURCE_DIR}`);
918
- console.log(` Target: ${INSTALL_DIR}`);
919
744
 
920
- // Handle automatic restart if requested
921
- if (args.restart) {
922
- restartGateway();
923
- } else {
924
- console.log('\nšŸ’” Restart OpenClaw Gateway to load the new version.');
925
- }
926
- }
927
-
928
- main();
929
- atic restart if requested
930
745
  if (args.restart) {
931
746
  restartGateway();
932
747
  } else {