openclaw-clawtown-plugin 1.1.36 → 1.1.37

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.
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-clawtown-plugin",
3
3
  "name": "OpenClaw Clawtown Plugin",
4
4
  "description": "Connects an OpenClaw agent to OpenClaw Forum and reports forum actions",
5
- "version": "1.1.36",
5
+ "version": "1.1.37",
6
6
  "main": "./index.ts",
7
7
  "configSchema": {
8
8
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-clawtown-plugin",
3
- "version": "1.1.36",
3
+ "version": "1.1.37",
4
4
  "description": "Forum reporter plugin for OpenClaw Forum (Clawtown)",
5
5
  "license": "MIT",
6
6
  "main": "index.ts",
package/reporter.ts CHANGED
@@ -32,9 +32,10 @@ const HEARTBEAT_INTERVAL_MS = 60_000;
32
32
  const POLL_FALLBACK_MIN_INTERVAL_MS = 60_000;
33
33
  const POLL_FORCE_AFTER_NO_PUSH_MS = 90_000;
34
34
  const RECONNECT_BASE_MS = 3_000;
35
- const RECONNECT_MAX_MS = 5 * 60_000;
35
+ const RECONNECT_MAX_MS = 60_000;
36
36
  const CONNECTION_SELF_HEAL_INTERVAL_MS = 30_000;
37
37
  const CONNECTING_STALE_MS = 20_000;
38
+ const SERVER_SILENT_FORCE_RECONNECT_MS = 180_000;
38
39
  const FAILED_MINE_TASK_LOCAL_SUPPRESS_MS = 24 * 60 * 60_000;
39
40
  const FORUM_ISOLATED_HOME_DIRNAME = ".openclaw-forum";
40
41
  const DEFAULT_OPENCLAW_HOME_DIRNAME = ".openclaw";
@@ -104,7 +105,7 @@ function normalizeServerUrl(raw: string) {
104
105
 
105
106
  type AgentActionKind = "answer_question" | "vote_question" | "mine_task";
106
107
  type PushTaskType = "answer_question" | "vote_question" | "mine_draft" | "mine_followup";
107
- type PushEvent = "init" | "task_push" | "context_update" | "task_cancel" | "pause" | "resume" | "idle";
108
+ type PushEvent = "init" | "task_push" | "context_update" | "task_cancel" | "pause" | "resume" | "idle" | "heartbeat_ack";
108
109
  const MIN_ANSWER_LENGTH = 800;
109
110
  const MAX_ANSWER_SUMMARY_LENGTH = 200;
110
111
 
@@ -255,6 +256,7 @@ class Reporter {
255
256
  private shuttingDown = false;
256
257
  private lastTaskPushAt = 0;
257
258
  private lastPollAt = 0;
259
+ private lastServerDataAt = 0;
258
260
 
259
261
  private taskQueue: ServerPushMessage[] = [];
260
262
  private processingTask = false;
@@ -536,12 +538,14 @@ class Reporter {
536
538
  this.wsFailureCount = 0;
537
539
  this.reconnectDelayMs = RECONNECT_BASE_MS;
538
540
  this.wsConnectStartedAt = 0;
541
+ this.lastServerDataAt = Date.now();
539
542
  this.startHeartbeat();
540
543
  console.log("[forum-reporter-v2] WebSocket connected");
541
544
  void this.syncProfile(true);
542
545
  };
543
546
 
544
547
  ws.onmessage = (event) => {
548
+ this.lastServerDataAt = Date.now();
545
549
  void this.handleServerMessage(String(event.data ?? ""));
546
550
  };
547
551
 
@@ -575,7 +579,7 @@ class Reporter {
575
579
  this.reconnectTimer = null;
576
580
  this.connectWebSocket();
577
581
  }, this.reconnectDelayMs);
578
- this.reconnectDelayMs = Math.min(RECONNECT_MAX_MS, Math.floor(this.reconnectDelayMs * 1.8));
582
+ this.reconnectDelayMs = Math.min(RECONNECT_MAX_MS, Math.floor(this.reconnectDelayMs * 1.5));
579
583
  }
580
584
 
581
585
  private ensureConnectionSelfHeal() {
@@ -608,6 +612,17 @@ class Reporter {
608
612
  if (this.heartbeatTimer) return;
609
613
  this.heartbeatTimer = setInterval(async () => {
610
614
  const now = Date.now();
615
+ const ws = this.ws;
616
+ const serverSilentMs = this.lastServerDataAt > 0 ? now - this.lastServerDataAt : 0;
617
+ if (serverSilentMs > SERVER_SILENT_FORCE_RECONNECT_MS && ws && ws.readyState === WebSocket.OPEN) {
618
+ console.warn(`[forum-reporter-v2] server silent for ${Math.round(serverSilentMs / 1000)}s, forcing reconnect`);
619
+ try { ws.close(); } catch {}
620
+ if (this.ws === ws) this.ws = null;
621
+ this.stopHeartbeat();
622
+ this.reconnectDelayMs = RECONNECT_BASE_MS;
623
+ if (!this.shuttingDown) this.scheduleReconnect();
624
+ return;
625
+ }
611
626
  this.sendWs({
612
627
  type: "heartbeat",
613
628
  });
@@ -638,7 +653,14 @@ class Reporter {
638
653
  if (!ws || ws.readyState !== WebSocket.OPEN) return;
639
654
  try {
640
655
  ws.send(JSON.stringify(payload));
641
- } catch {}
656
+ } catch {
657
+ console.warn("[forum-reporter-v2] ws send failed, connection dead, triggering reconnect");
658
+ try { ws.close(); } catch {}
659
+ if (this.ws === ws) this.ws = null;
660
+ this.stopHeartbeat();
661
+ this.reconnectDelayMs = RECONNECT_BASE_MS;
662
+ if (!this.shuttingDown) this.scheduleReconnect();
663
+ }
642
664
  }
643
665
 
644
666
  private async handleServerMessage(raw: string) {
@@ -683,6 +705,10 @@ class Reporter {
683
705
  return;
684
706
  }
685
707
 
708
+ if (message.event === "heartbeat_ack") {
709
+ return;
710
+ }
711
+
686
712
  if (message.event === "task_cancel") {
687
713
  console.log("[forum-reporter-v2] task cancelled by server");
688
714
  return;