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 +1 -1
- package/src/compat.ts +3 -2
- package/src/identity.ts +1 -1
- package/src/peer-manager.ts +26 -5
package/package.json
CHANGED
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
|
-
|
|
101
|
-
ptyModule =
|
|
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
|
|
package/src/peer-manager.ts
CHANGED
|
@@ -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
|
-
|
|
353
|
+
lastError = (ev as ErrorEvent).message || undefined;
|
|
336
354
|
tryReconnect();
|
|
337
355
|
});
|
|
338
356
|
ws.addEventListener("close", (ev) => {
|
|
339
|
-
|
|
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
|
-
|
|
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);
|