clawmatrix 0.2.4 → 0.2.5

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": "clawmatrix",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Decentralized mesh cluster plugin for OpenClaw — inter-gateway communication, model proxy, task handoff, and tool proxy.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/compat.ts CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  import { spawn as cpSpawn } from "node:child_process";
8
8
  import { readFile, writeFile } from "node:fs/promises";
9
+ import { createRequire } from "node:module";
9
10
 
10
11
  export interface SpawnResult {
11
12
  exitCode: number;
@@ -97,8 +98,8 @@ let ptyModule: {
97
98
  function loadPty() {
98
99
  if (ptyModule !== undefined) return ptyModule;
99
100
  try {
100
- // eslint-disable-next-line @typescript-eslint/no-require-imports
101
- ptyModule = require("node-pty");
101
+ const req = createRequire(import.meta.url);
102
+ ptyModule = req("node-pty");
102
103
  } catch {
103
104
  ptyModule = null;
104
105
  }
package/src/identity.ts CHANGED
@@ -26,6 +26,7 @@
26
26
  * ECDH exchange with the peer's key.
27
27
  */
28
28
 
29
+ import { createPrivateKey } from "node:crypto";
29
30
  import fs from "node:fs";
30
31
  import path from "node:path";
31
32
  import {
@@ -82,7 +83,6 @@ export function loadOrCreateIdentity(stateDir: string): KeyPair {
82
83
 
83
84
  /** Reconstruct a KeyPair from serialized base64 strings. */
84
85
  function keyPairFromSerialized(publicKeyB64: string, privateKeyB64: string): KeyPair {
85
- const { createPrivateKey } = require("node:crypto");
86
86
  const publicKey = Buffer.from(publicKeyB64, "base64");
87
87
  const privateKey = Buffer.from(privateKeyB64, "base64");
88
88
 
@@ -28,6 +28,23 @@ import type { KeyPair } from "./crypto.ts";
28
28
  const RECONNECT_BASE = 1_000;
29
29
  const RECONNECT_MAX = 60_000;
30
30
 
31
+ /** Classify WebSocket close code into a human-readable reason. */
32
+ function classifyCloseReason(code: number, reason: string): string {
33
+ if (reason) return reason;
34
+ switch (code) {
35
+ case 1006: return "unreachable (node may be down)";
36
+ case 1000: return "normal close";
37
+ case 1001: return "peer going away";
38
+ case 1002: return "protocol error";
39
+ case 1003: return "unsupported data";
40
+ case 1008: return "policy violation";
41
+ case 1011: return "server error";
42
+ case 4001: return "auth failed";
43
+ case 4003: return "auth timeout";
44
+ default: return `close code ${code}`;
45
+ }
46
+ }
47
+
31
48
  /** Check if an IP is a loopback address (IPv4 127.x or IPv6 ::1). */
32
49
  function isLoopback(ip?: string): boolean {
33
50
  if (!ip) return false;
@@ -324,24 +341,27 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
324
341
  });
325
342
 
326
343
  let reconnectScheduled = false;
344
+ let lastError: string | undefined;
327
345
  const tryReconnect = () => {
328
346
  if (!reconnectScheduled) {
329
347
  reconnectScheduled = true;
330
- this.scheduleReconnect(peer);
348
+ this.scheduleReconnect(peer, lastError);
331
349
  }
332
350
  };
333
351
 
334
352
  ws.addEventListener("error", (ev) => {
335
- debug("peer", `connectToPeer(${peer.nodeId}): ws error: ${(ev as ErrorEvent).message ?? "unknown"}`);
353
+ lastError = (ev as ErrorEvent).message || undefined;
336
354
  tryReconnect();
337
355
  });
338
356
  ws.addEventListener("close", (ev) => {
339
- debug("peer", `connectToPeer(${peer.nodeId}): ws close code=${ev.code} reason=${ev.reason}`);
357
+ if (!lastError) {
358
+ lastError = classifyCloseReason(ev.code, ev.reason);
359
+ }
340
360
  tryReconnect();
341
361
  });
342
362
  }
343
363
 
344
- private scheduleReconnect(peer: PeerConfig) {
364
+ private scheduleReconnect(peer: PeerConfig, reason?: string) {
345
365
  if (this.stopped) {
346
366
  debug("peer", `scheduleReconnect(${peer.nodeId}): skipped (stopped)`);
347
367
  return;
@@ -351,7 +371,8 @@ export class PeerManager extends EventEmitter<PeerManagerEvents> {
351
371
  const attempt = this.reconnectAttempts.get(peer.nodeId) ?? 0;
352
372
  const delay = Math.min(RECONNECT_BASE * 2 ** attempt, RECONNECT_MAX);
353
373
  this.reconnectAttempts.set(peer.nodeId, attempt + 1);
354
- debug("peer", `scheduleReconnect(${peer.nodeId}): attempt=${attempt} delay=${delay}ms`);
374
+ const tag = reason ? ` reason="${reason}"` : "";
375
+ debug("peer", `scheduleReconnect(${peer.nodeId}): attempt=${attempt} delay=${delay}ms${tag}`);
355
376
 
356
377
  const timer = setTimeout(() => {
357
378
  this.reconnectTimers.delete(peer.nodeId);