@inetafrica/open-claudia 2.6.43 → 2.6.44

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.
@@ -19,11 +19,16 @@ class TelegramAdapter {
19
19
  this.token = token;
20
20
  this.ownerChatId = ownerChatId;
21
21
  this.chatIds = chatIds || [];
22
+ // Own the HTTP(S) agent so we can drop dead keep-alive sockets on reconnect.
23
+ this._agent = new https.Agent({ keepAlive: true });
22
24
  this.bot = new TelegramBot(token, {
23
25
  polling: { autoStart: false, params: { timeout: 30 } },
26
+ request: { agent: this._agent },
24
27
  });
25
28
  this._listeners = { message: new Set(), action: new Set() };
26
29
  this._reconnectTimer = null;
30
+ this._recoveryTimer = null;
31
+ this._reconnectAttempts = 0;
27
32
  this._wireInbound();
28
33
  }
29
34
 
@@ -45,9 +50,49 @@ class TelegramAdapter {
45
50
  }
46
51
 
47
52
  async stop() {
53
+ if (this._reconnectTimer) { clearTimeout(this._reconnectTimer); this._reconnectTimer = null; }
54
+ if (this._recoveryTimer) { clearTimeout(this._recoveryTimer); this._recoveryTimer = null; }
48
55
  try { await this.bot.stopPolling(); } catch (e) {}
49
56
  }
50
57
 
58
+ // Recover from a wedged poll loop. The library reuses one keep-alive socket;
59
+ // after a sleep/wake or network blip that socket dies and every poll then
60
+ // ETIMEDOUTs forever — restarting polling alone reuses the same dead socket.
61
+ // So we destroy the agent (forcing a fresh connection), back off, and if we
62
+ // still can't recover after several tries we exit so launchd (KeepAlive)
63
+ // relaunches a fully clean process.
64
+ _scheduleReconnect() {
65
+ if (this._reconnectTimer) return; // one cycle in flight; swallow the flood
66
+ if (this._recoveryTimer) { clearTimeout(this._recoveryTimer); this._recoveryTimer = null; }
67
+ this._reconnectAttempts += 1;
68
+ const MAX = 6;
69
+ if (this._reconnectAttempts > MAX) {
70
+ console.error(`Polling unrecoverable after ${MAX} attempts — exiting for supervisor restart.`);
71
+ process.exit(1);
72
+ }
73
+ const delay = Math.min(30000, 1000 * 2 ** this._reconnectAttempts); // 2s,4s,8s,16s,30s,30s
74
+ console.log(`Network lost. Reconnecting in ${Math.round(delay / 1000)}s (attempt ${this._reconnectAttempts}/${MAX})...`);
75
+ this._reconnectTimer = setTimeout(async () => {
76
+ this._reconnectTimer = null;
77
+ try {
78
+ try { await this.bot.stopPolling(); } catch (e) {}
79
+ try { this._agent.destroy(); } catch (e) {} // drop the dead pooled socket
80
+ await new Promise((r) => setTimeout(r, 1000));
81
+ await this.bot.startPolling();
82
+ console.log("Polling restarted.");
83
+ // No fresh error within 30s ⇒ genuinely recovered; reset the counter.
84
+ this._recoveryTimer = setTimeout(() => {
85
+ this._recoveryTimer = null;
86
+ if (this._reconnectAttempts) console.log("Connection healthy again.");
87
+ this._reconnectAttempts = 0;
88
+ }, 30000);
89
+ } catch (e) {
90
+ console.error("Reconnect attempt failed:", e.message);
91
+ this._scheduleReconnect(); // back off and retry; don't die on first miss
92
+ }
93
+ }, delay);
94
+ }
95
+
51
96
  _wireInbound() {
52
97
  this.bot.on("polling_error", (err) => {
53
98
  const msg = err.message || "";
@@ -56,21 +101,8 @@ class TelegramAdapter {
56
101
  console.error("Another instance is polling. Exiting.");
57
102
  process.exit(1);
58
103
  }
59
- if (msg.includes("ETIMEDOUT") || msg.includes("ECONNRESET") || msg.includes("ENOTFOUND") || msg.includes("EFATAL")) {
60
- if (this._reconnectTimer) return;
61
- console.log("Network lost. Reconnecting in 10s...");
62
- this._reconnectTimer = setTimeout(async () => {
63
- this._reconnectTimer = null;
64
- try {
65
- await this.bot.stopPolling();
66
- await new Promise((r) => setTimeout(r, 2000));
67
- await this.bot.startPolling();
68
- console.log("Reconnected.");
69
- } catch (e) {
70
- console.error("Reconnect failed:", e.message);
71
- process.exit(1);
72
- }
73
- }, 10000);
104
+ if (/ETIMEDOUT|ECONNRESET|ENOTFOUND|ENETUNREACH|EAI_AGAIN|EFATAL|socket hang up/i.test(msg)) {
105
+ this._scheduleReconnect();
74
106
  }
75
107
  });
76
108
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "2.6.43",
3
+ "version": "2.6.44",
4
4
  "description": "Your always-on AI coding assistant — Claude Code, Cursor Agent, and OpenAI Codex via Telegram or Kazee Chat",
5
5
  "main": "bot.js",
6
6
  "bin": {