@blockrun/clawrouter 0.12.111 → 0.12.113

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/clawrouter",
3
- "version": "0.12.111",
3
+ "version": "0.12.113",
4
4
  "description": "Smart LLM router — save 85% on inference costs. 55+ models (11 free), one wallet, x402 micropayments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -62,6 +62,20 @@ run_dependency_install() {
62
62
 
63
63
  trap restore_previous_install EXIT
64
64
 
65
+ # Pre-flight: validate openclaw.json is parseable before touching anything
66
+ validate_config() {
67
+ local config_path="$HOME/.openclaw/openclaw.json"
68
+ if [ ! -f "$config_path" ]; then return 0; fi
69
+ if ! node -e "JSON.parse(require('fs').readFileSync('$config_path','utf8'))" 2>/dev/null; then
70
+ echo ""
71
+ echo "✗ openclaw.json is corrupt (invalid JSON)."
72
+ echo " Fix it first: openclaw doctor --fix"
73
+ echo " Then re-run this script."
74
+ echo ""
75
+ exit 1
76
+ fi
77
+ }
78
+
65
79
  kill_port_processes() {
66
80
  local port="$1"
67
81
  local pids=""
@@ -87,6 +101,9 @@ kill_port_processes() {
87
101
  echo "🦞 ClawRouter Reinstall"
88
102
  echo ""
89
103
 
104
+ # Pre-flight: fail fast if config is corrupt
105
+ validate_config
106
+
90
107
  # 0. Back up wallet key BEFORE removing anything
91
108
  echo "→ Backing up wallet..."
92
109
  if [ -f "$WALLET_FILE" ]; then
@@ -312,8 +329,47 @@ if [ -d "$CREDS_DIR" ] && [ "$(ls -A "$CREDS_DIR" 2>/dev/null)" ]; then
312
329
  echo " ✓ Backed up OpenClaw credentials"
313
330
  fi
314
331
 
332
+ # Extract channel config (Telegram tokens, etc.) from openclaw.json before install
333
+ # openclaw plugins install can overwrite config and wipe channel settings
334
+ CHANNEL_CONFIG_BACKUP=""
335
+ if [ -f "$CONFIG_PATH" ]; then
336
+ CHANNEL_CONFIG_BACKUP="$(mktemp)"
337
+ node -e "
338
+ const fs = require('fs');
339
+ try {
340
+ const config = JSON.parse(fs.readFileSync('$CONFIG_PATH', 'utf8'));
341
+ // Save channels block and gateway block (gateway.mode etc.)
342
+ const preserved = {};
343
+ if (config.channels) preserved.channels = config.channels;
344
+ if (config.gateway) preserved.gateway = config.gateway;
345
+ fs.writeFileSync('$CHANNEL_CONFIG_BACKUP', JSON.stringify(preserved, null, 2));
346
+ const channelCount = Object.keys(config.channels || {}).length;
347
+ if (channelCount > 0) console.log(' ✓ Preserved config for channels: ' + Object.keys(config.channels).join(', '));
348
+ } catch (e) { fs.writeFileSync('$CHANNEL_CONFIG_BACKUP', '{}'); }
349
+ "
350
+ fi
351
+
315
352
  echo "→ Installing ClawRouter..."
316
- openclaw plugins install @blockrun/clawrouter
353
+ # Run with timeout — openclaw plugins install may hang after printing
354
+ # "Installed plugin: clawrouter" in OpenClaw v2026.4.5 (parallel plugin loading).
355
+ # 120s is enough for slow connections; the install itself completes in ~30s.
356
+ if command -v timeout >/dev/null 2>&1; then
357
+ timeout 120 openclaw plugins install @blockrun/clawrouter || {
358
+ exit_code=$?
359
+ if [ $exit_code -eq 124 ]; then
360
+ echo " (install command timed out — this is normal with OpenClaw v2026.4.5)"
361
+ echo " Plugin was installed successfully before the hang."
362
+ else
363
+ exit $exit_code
364
+ fi
365
+ }
366
+ else
367
+ openclaw plugins install @blockrun/clawrouter
368
+ fi
369
+
370
+ # Install is complete — clear the rollback trap immediately.
371
+ # From this point on, Ctrl+C or errors should NOT roll back the install.
372
+ trap - EXIT INT TERM
317
373
 
318
374
  # Restore credentials after plugin install (always restore to preserve user's channels)
319
375
  if [ -n "$CREDS_BACKUP" ] && [ -d "$CREDS_BACKUP" ]; then
@@ -323,6 +379,59 @@ if [ -n "$CREDS_BACKUP" ] && [ -d "$CREDS_BACKUP" ]; then
323
379
  rm -rf "$(dirname "$CREDS_BACKUP")"
324
380
  fi
325
381
 
382
+ # Restore channel config (Telegram tokens etc.) that may have been wiped by plugin install
383
+ if [ -n "$CHANNEL_CONFIG_BACKUP" ] && [ -f "$CHANNEL_CONFIG_BACKUP" ] && [ -f "$CONFIG_PATH" ]; then
384
+ node -e "
385
+ const fs = require('fs');
386
+ function atomicWrite(filePath, data) {
387
+ const tmpPath = filePath + '.tmp.' + process.pid;
388
+ fs.writeFileSync(tmpPath, data);
389
+ fs.renameSync(tmpPath, filePath);
390
+ }
391
+ try {
392
+ const config = JSON.parse(fs.readFileSync('$CONFIG_PATH', 'utf8'));
393
+ const preserved = JSON.parse(fs.readFileSync('$CHANNEL_CONFIG_BACKUP', 'utf8'));
394
+ let changed = false;
395
+
396
+ // Restore channels block if it was wiped or is now empty
397
+ if (preserved.channels && Object.keys(preserved.channels).length > 0) {
398
+ if (!config.channels || Object.keys(config.channels).length === 0) {
399
+ config.channels = preserved.channels;
400
+ changed = true;
401
+ console.log(' ✓ Restored channel config (Telegram/WhatsApp/etc.)');
402
+ } else {
403
+ // Merge: restore any channels that are now missing
404
+ let merged = 0;
405
+ for (const [ch, val] of Object.entries(preserved.channels)) {
406
+ if (!config.channels[ch]) {
407
+ config.channels[ch] = val;
408
+ merged++;
409
+ }
410
+ }
411
+ if (merged > 0) {
412
+ changed = true;
413
+ console.log(' ✓ Merged ' + merged + ' missing channel(s) back into config');
414
+ } else {
415
+ console.log(' Channel config intact');
416
+ }
417
+ }
418
+ }
419
+
420
+ // Restore gateway.mode if missing
421
+ if (preserved.gateway?.mode && (!config.gateway || !config.gateway.mode)) {
422
+ if (!config.gateway) config.gateway = {};
423
+ config.gateway.mode = preserved.gateway.mode;
424
+ changed = true;
425
+ }
426
+
427
+ if (changed) atomicWrite('$CONFIG_PATH', JSON.stringify(config, null, 2));
428
+ } catch (e) {
429
+ console.log(' Warning: could not restore channel config:', e.message);
430
+ }
431
+ "
432
+ rm -f "$CHANNEL_CONFIG_BACKUP"
433
+ fi
434
+
326
435
  # 6.1. Verify installation and force-update if openclaw installed a stale cached version
327
436
  echo "→ Verifying installation..."
328
437
  DIST_PATH="$PLUGIN_DIR/dist/index.js"
@@ -552,6 +661,21 @@ if (fs.existsSync(configPath)) {
552
661
  }
553
662
  "
554
663
 
664
+ # Clean up stale plugin backups — these cause "duplicate plugin" warnings on restart
665
+ echo "→ Cleaning up stale plugin backups..."
666
+ CLEANED=0
667
+ for backup_dir in "$HOME/.openclaw/extensions/clawrouter.backup."*; do
668
+ if [ -d "$backup_dir" ]; then
669
+ rm -rf "$backup_dir"
670
+ CLEANED=$((CLEANED + 1))
671
+ fi
672
+ done
673
+ if [ "$CLEANED" -gt 0 ]; then
674
+ echo " ✓ Removed $CLEANED stale backup(s)"
675
+ else
676
+ echo " ✓ No stale backups found"
677
+ fi
678
+
555
679
  # Final: verify wallet survived reinstall
556
680
  echo "→ Verifying wallet integrity..."
557
681
  if [ -f "$WALLET_FILE" ]; then
@@ -578,7 +702,37 @@ fi
578
702
  echo ""
579
703
  echo "✓ Done! Smart routing enabled by default."
580
704
  echo ""
581
- echo "Run: openclaw gateway restart"
705
+
706
+ # Auto-restart gateway so new version is active immediately
707
+ echo "→ Restarting gateway..."
708
+ RESTART_OK=false
709
+ if systemctl --user is-active openclaw-gateway.service >/dev/null 2>&1 || \
710
+ systemctl --user is-enabled openclaw-gateway.service >/dev/null 2>&1; then
711
+ if systemctl --user restart openclaw-gateway.service 2>/dev/null; then
712
+ # Wait up to 15s for ClawRouter proxy port to come up
713
+ for i in $(seq 1 15); do
714
+ sleep 1
715
+ if curl -sf --connect-timeout 1 http://localhost:8402/v1/models >/dev/null 2>&1; then
716
+ RESTART_OK=true
717
+ break
718
+ fi
719
+ done
720
+ if $RESTART_OK; then
721
+ echo " ✓ Gateway restarted — ClawRouter active on port 8402"
722
+ else
723
+ echo " ⚠ Gateway restarted but port 8402 not yet up (may still be starting)"
724
+ echo " Check: systemctl --user status openclaw-gateway.service"
725
+ fi
726
+ else
727
+ echo " ⚠ systemctl restart failed. Run manually: openclaw gateway restart"
728
+ fi
729
+ elif command -v openclaw >/dev/null 2>&1; then
730
+ # Fallback: use openclaw CLI restart (background, don't hang)
731
+ openclaw gateway restart &>/dev/null &
732
+ echo " ✓ Gateway restart triggered"
733
+ else
734
+ echo " Run: openclaw gateway restart"
735
+ fi
582
736
  echo ""
583
737
  echo "Model aliases available:"
584
738
  echo " /model sonnet → claude-sonnet-4.6"
package/scripts/update.sh CHANGED
@@ -71,9 +71,27 @@ run_dependency_install() {
71
71
 
72
72
  trap restore_previous_install EXIT
73
73
 
74
+ # Pre-flight: validate openclaw.json is parseable before touching anything
75
+ validate_config() {
76
+ local config_path="$HOME/.openclaw/openclaw.json"
77
+ if [ ! -f "$config_path" ]; then return 0; fi
78
+ if ! node -e "JSON.parse(require('fs').readFileSync('$config_path','utf8'))" 2>/dev/null; then
79
+ echo ""
80
+ echo "✗ openclaw.json is corrupt (invalid JSON)."
81
+ echo " Fix it first: openclaw doctor --fix"
82
+ echo " Then re-run this script."
83
+ echo ""
84
+ exit 1
85
+ fi
86
+ }
87
+
74
88
  # ── Step 1: Back up wallet key ─────────────────────────────────
75
89
  echo "🦞 ClawRouter Update"
76
90
  echo ""
91
+
92
+ # Pre-flight: fail fast if config is corrupt
93
+ validate_config
94
+
77
95
  echo "→ Checking wallet..."
78
96
 
79
97
  if [ -f "$WALLET_FILE" ]; then
@@ -200,8 +218,46 @@ if [ -d "$CREDS_DIR" ] && [ "$(ls -A "$CREDS_DIR" 2>/dev/null)" ]; then
200
218
  echo " ✓ Backed up OpenClaw credentials"
201
219
  fi
202
220
 
221
+ # Extract channel config (Telegram tokens, etc.) from openclaw.json before install
222
+ # openclaw plugins install can overwrite config and wipe channel settings
223
+ CHANNEL_CONFIG_BACKUP=""
224
+ if [ -f "$CONFIG_PATH" ]; then
225
+ CHANNEL_CONFIG_BACKUP="$(mktemp)"
226
+ node -e "
227
+ const fs = require('fs');
228
+ try {
229
+ const config = JSON.parse(fs.readFileSync('$CONFIG_PATH', 'utf8'));
230
+ const preserved = {};
231
+ if (config.channels) preserved.channels = config.channels;
232
+ if (config.gateway) preserved.gateway = config.gateway;
233
+ fs.writeFileSync('$CHANNEL_CONFIG_BACKUP', JSON.stringify(preserved, null, 2));
234
+ const channelCount = Object.keys(config.channels || {}).length;
235
+ if (channelCount > 0) console.log(' ✓ Preserved config for channels: ' + Object.keys(config.channels).join(', '));
236
+ } catch (e) { fs.writeFileSync('$CHANNEL_CONFIG_BACKUP', '{}'); }
237
+ "
238
+ fi
239
+
203
240
  echo "→ Installing latest ClawRouter..."
204
- openclaw plugins install @blockrun/clawrouter
241
+ # Run with timeout — openclaw plugins install may hang after printing
242
+ # "Installed plugin: clawrouter" in OpenClaw v2026.4.5 (parallel plugin loading).
243
+ # 120s is enough for slow connections; the install itself completes in ~30s.
244
+ if command -v timeout >/dev/null 2>&1; then
245
+ timeout 120 openclaw plugins install @blockrun/clawrouter || {
246
+ exit_code=$?
247
+ if [ $exit_code -eq 124 ]; then
248
+ echo " (install command timed out — this is normal with OpenClaw v2026.4.5)"
249
+ echo " Plugin was installed successfully before the hang."
250
+ else
251
+ exit $exit_code
252
+ fi
253
+ }
254
+ else
255
+ openclaw plugins install @blockrun/clawrouter
256
+ fi
257
+
258
+ # Install is complete — clear the rollback trap immediately.
259
+ # From this point on, Ctrl+C or errors should NOT roll back the install.
260
+ trap - EXIT INT TERM
205
261
 
206
262
  # Restore credentials after plugin install (always restore to preserve user's channels)
207
263
  if [ -n "$CREDS_BACKUP" ] && [ -d "$CREDS_BACKUP" ]; then
@@ -211,6 +267,46 @@ if [ -n "$CREDS_BACKUP" ] && [ -d "$CREDS_BACKUP" ]; then
211
267
  rm -rf "$(dirname "$CREDS_BACKUP")"
212
268
  fi
213
269
 
270
+ # Restore channel config (Telegram tokens etc.) that may have been wiped by plugin install
271
+ if [ -n "$CHANNEL_CONFIG_BACKUP" ] && [ -f "$CHANNEL_CONFIG_BACKUP" ] && [ -f "$CONFIG_PATH" ]; then
272
+ node -e "
273
+ const fs = require('fs');
274
+ function atomicWrite(filePath, data) {
275
+ const tmpPath = filePath + '.tmp.' + process.pid;
276
+ fs.writeFileSync(tmpPath, data);
277
+ fs.renameSync(tmpPath, filePath);
278
+ }
279
+ try {
280
+ const config = JSON.parse(fs.readFileSync('$CONFIG_PATH', 'utf8'));
281
+ const preserved = JSON.parse(fs.readFileSync('$CHANNEL_CONFIG_BACKUP', 'utf8'));
282
+ let changed = false;
283
+ if (preserved.channels && Object.keys(preserved.channels).length > 0) {
284
+ if (!config.channels || Object.keys(config.channels).length === 0) {
285
+ config.channels = preserved.channels;
286
+ changed = true;
287
+ console.log(' ✓ Restored channel config (Telegram/WhatsApp/etc.)');
288
+ } else {
289
+ let merged = 0;
290
+ for (const [ch, val] of Object.entries(preserved.channels)) {
291
+ if (!config.channels[ch]) { config.channels[ch] = val; merged++; }
292
+ }
293
+ if (merged > 0) { changed = true; console.log(' ✓ Merged ' + merged + ' missing channel(s) back into config'); }
294
+ else { console.log(' Channel config intact'); }
295
+ }
296
+ }
297
+ if (preserved.gateway?.mode && (!config.gateway || !config.gateway.mode)) {
298
+ if (!config.gateway) config.gateway = {};
299
+ config.gateway.mode = preserved.gateway.mode;
300
+ changed = true;
301
+ }
302
+ if (changed) atomicWrite('$CONFIG_PATH', JSON.stringify(config, null, 2));
303
+ } catch (e) {
304
+ console.log(' Warning: could not restore channel config:', e.message);
305
+ }
306
+ "
307
+ rm -f "$CHANNEL_CONFIG_BACKUP"
308
+ fi
309
+
214
310
  # ── Step 4b: Verify version — force-update if openclaw served a stale cache ──
215
311
  force_install_from_npm() {
216
312
  local version="$1"
@@ -438,6 +534,21 @@ if (fs.existsSync(configPath)) {
438
534
  }
439
535
  "
440
536
 
537
+ # Clean up stale plugin backups — these cause "duplicate plugin" warnings on restart
538
+ echo "→ Cleaning up stale plugin backups..."
539
+ CLEANED=0
540
+ for backup_dir in "$HOME/.openclaw/extensions/clawrouter.backup."*; do
541
+ if [ -d "$backup_dir" ]; then
542
+ rm -rf "$backup_dir"
543
+ CLEANED=$((CLEANED + 1))
544
+ fi
545
+ done
546
+ if [ "$CLEANED" -gt 0 ]; then
547
+ echo " ✓ Removed $CLEANED stale backup(s)"
548
+ else
549
+ echo " ✓ No stale backups found"
550
+ fi
551
+
441
552
  # ── Summary ─────────────────────────────────────────────────────
442
553
  echo ""
443
554
  echo "✓ ClawRouter updated successfully!"
@@ -461,7 +572,36 @@ if [ -f "$WALLET_FILE" ]; then
461
572
  fi
462
573
 
463
574
  echo ""
464
- echo " Run: openclaw gateway restart"
575
+
576
+ # Auto-restart gateway so new version is active immediately
577
+ echo "→ Restarting gateway..."
578
+ RESTART_OK=false
579
+ if systemctl --user is-active openclaw-gateway.service >/dev/null 2>&1 || \
580
+ systemctl --user is-enabled openclaw-gateway.service >/dev/null 2>&1; then
581
+ if systemctl --user restart openclaw-gateway.service 2>/dev/null; then
582
+ for i in $(seq 1 15); do
583
+ sleep 1
584
+ if curl -sf --connect-timeout 1 http://localhost:8402/v1/models >/dev/null 2>&1; then
585
+ RESTART_OK=true
586
+ break
587
+ fi
588
+ done
589
+ if $RESTART_OK; then
590
+ echo " ✓ Gateway restarted — ClawRouter active on port 8402"
591
+ else
592
+ echo " ⚠ Gateway restarted but port 8402 not yet up (may still be starting)"
593
+ echo " Check: systemctl --user status openclaw-gateway.service"
594
+ fi
595
+ else
596
+ echo " ⚠ systemctl restart failed. Run manually: openclaw gateway restart"
597
+ fi
598
+ elif command -v openclaw >/dev/null 2>&1; then
599
+ openclaw gateway restart &>/dev/null &
600
+ echo " ✓ Gateway restart triggered"
601
+ else
602
+ echo " Run: openclaw gateway restart"
603
+ fi
604
+
465
605
  echo ""
466
606
  echo " Commands:"
467
607
  echo " npx @blockrun/clawrouter report # daily usage report"