@sequence0/blockclaw 2.0.0

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.
Files changed (190) hide show
  1. package/README.md +97 -0
  2. package/dist/agent/binary.d.ts +15 -0
  3. package/dist/agent/binary.d.ts.map +1 -0
  4. package/dist/agent/binary.js +172 -0
  5. package/dist/agent/binary.js.map +1 -0
  6. package/dist/agent/chains.d.ts +26 -0
  7. package/dist/agent/chains.d.ts.map +1 -0
  8. package/dist/agent/chains.js +123 -0
  9. package/dist/agent/chains.js.map +1 -0
  10. package/dist/agent/client.d.ts +484 -0
  11. package/dist/agent/client.d.ts.map +1 -0
  12. package/dist/agent/client.js +448 -0
  13. package/dist/agent/client.js.map +1 -0
  14. package/dist/agent/config.d.ts +75 -0
  15. package/dist/agent/config.d.ts.map +1 -0
  16. package/dist/agent/config.js +133 -0
  17. package/dist/agent/config.js.map +1 -0
  18. package/dist/agent/process.d.ts +36 -0
  19. package/dist/agent/process.d.ts.map +1 -0
  20. package/dist/agent/process.js +208 -0
  21. package/dist/agent/process.js.map +1 -0
  22. package/dist/agent/wallet.d.ts +43 -0
  23. package/dist/agent/wallet.d.ts.map +1 -0
  24. package/dist/agent/wallet.js +146 -0
  25. package/dist/agent/wallet.js.map +1 -0
  26. package/dist/ai/brain.d.ts +21 -0
  27. package/dist/ai/brain.d.ts.map +1 -0
  28. package/dist/ai/brain.js +117 -0
  29. package/dist/ai/brain.js.map +1 -0
  30. package/dist/ai/prompts.d.ts +15 -0
  31. package/dist/ai/prompts.d.ts.map +1 -0
  32. package/dist/ai/prompts.js +67 -0
  33. package/dist/ai/prompts.js.map +1 -0
  34. package/dist/ai/rules.d.ts +19 -0
  35. package/dist/ai/rules.d.ts.map +1 -0
  36. package/dist/ai/rules.js +143 -0
  37. package/dist/ai/rules.js.map +1 -0
  38. package/dist/core/detector.d.ts +16 -0
  39. package/dist/core/detector.d.ts.map +1 -0
  40. package/dist/core/detector.js +90 -0
  41. package/dist/core/detector.js.map +1 -0
  42. package/dist/core/healer.d.ts +62 -0
  43. package/dist/core/healer.d.ts.map +1 -0
  44. package/dist/core/healer.js +355 -0
  45. package/dist/core/healer.js.map +1 -0
  46. package/dist/core/heartbeat.d.ts +67 -0
  47. package/dist/core/heartbeat.d.ts.map +1 -0
  48. package/dist/core/heartbeat.js +426 -0
  49. package/dist/core/heartbeat.js.map +1 -0
  50. package/dist/core/memory.d.ts +57 -0
  51. package/dist/core/memory.d.ts.map +1 -0
  52. package/dist/core/memory.js +149 -0
  53. package/dist/core/memory.js.map +1 -0
  54. package/dist/core/updater.d.ts +16 -0
  55. package/dist/core/updater.d.ts.map +1 -0
  56. package/dist/core/updater.js +107 -0
  57. package/dist/core/updater.js.map +1 -0
  58. package/dist/index.d.ts +3 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +97 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/interface/commands/ask.d.ts +3 -0
  63. package/dist/interface/commands/ask.d.ts.map +1 -0
  64. package/dist/interface/commands/ask.js +39 -0
  65. package/dist/interface/commands/ask.js.map +1 -0
  66. package/dist/interface/commands/backup.d.ts +3 -0
  67. package/dist/interface/commands/backup.d.ts.map +1 -0
  68. package/dist/interface/commands/backup.js +576 -0
  69. package/dist/interface/commands/backup.js.map +1 -0
  70. package/dist/interface/commands/config.d.ts +3 -0
  71. package/dist/interface/commands/config.d.ts.map +1 -0
  72. package/dist/interface/commands/config.js +107 -0
  73. package/dist/interface/commands/config.js.map +1 -0
  74. package/dist/interface/commands/daemon.d.ts +3 -0
  75. package/dist/interface/commands/daemon.d.ts.map +1 -0
  76. package/dist/interface/commands/daemon.js +162 -0
  77. package/dist/interface/commands/daemon.js.map +1 -0
  78. package/dist/interface/commands/doctor.d.ts +3 -0
  79. package/dist/interface/commands/doctor.d.ts.map +1 -0
  80. package/dist/interface/commands/doctor.js +143 -0
  81. package/dist/interface/commands/doctor.js.map +1 -0
  82. package/dist/interface/commands/earnings.d.ts +3 -0
  83. package/dist/interface/commands/earnings.d.ts.map +1 -0
  84. package/dist/interface/commands/earnings.js +165 -0
  85. package/dist/interface/commands/earnings.js.map +1 -0
  86. package/dist/interface/commands/health.d.ts +3 -0
  87. package/dist/interface/commands/health.d.ts.map +1 -0
  88. package/dist/interface/commands/health.js +48 -0
  89. package/dist/interface/commands/health.js.map +1 -0
  90. package/dist/interface/commands/history.d.ts +3 -0
  91. package/dist/interface/commands/history.d.ts.map +1 -0
  92. package/dist/interface/commands/history.js +113 -0
  93. package/dist/interface/commands/history.js.map +1 -0
  94. package/dist/interface/commands/info.d.ts +3 -0
  95. package/dist/interface/commands/info.d.ts.map +1 -0
  96. package/dist/interface/commands/info.js +82 -0
  97. package/dist/interface/commands/info.js.map +1 -0
  98. package/dist/interface/commands/logs.d.ts +3 -0
  99. package/dist/interface/commands/logs.d.ts.map +1 -0
  100. package/dist/interface/commands/logs.js +98 -0
  101. package/dist/interface/commands/logs.js.map +1 -0
  102. package/dist/interface/commands/menu.d.ts +4 -0
  103. package/dist/interface/commands/menu.d.ts.map +1 -0
  104. package/dist/interface/commands/menu.js +156 -0
  105. package/dist/interface/commands/menu.js.map +1 -0
  106. package/dist/interface/commands/receive.d.ts +3 -0
  107. package/dist/interface/commands/receive.d.ts.map +1 -0
  108. package/dist/interface/commands/receive.js +81 -0
  109. package/dist/interface/commands/receive.js.map +1 -0
  110. package/dist/interface/commands/register.d.ts +3 -0
  111. package/dist/interface/commands/register.d.ts.map +1 -0
  112. package/dist/interface/commands/register.js +231 -0
  113. package/dist/interface/commands/register.js.map +1 -0
  114. package/dist/interface/commands/restart.d.ts +3 -0
  115. package/dist/interface/commands/restart.d.ts.map +1 -0
  116. package/dist/interface/commands/restart.js +154 -0
  117. package/dist/interface/commands/restart.js.map +1 -0
  118. package/dist/interface/commands/send.d.ts +3 -0
  119. package/dist/interface/commands/send.d.ts.map +1 -0
  120. package/dist/interface/commands/send.js +108 -0
  121. package/dist/interface/commands/send.js.map +1 -0
  122. package/dist/interface/commands/service.d.ts +3 -0
  123. package/dist/interface/commands/service.d.ts.map +1 -0
  124. package/dist/interface/commands/service.js +474 -0
  125. package/dist/interface/commands/service.js.map +1 -0
  126. package/dist/interface/commands/setup.d.ts +5 -0
  127. package/dist/interface/commands/setup.d.ts.map +1 -0
  128. package/dist/interface/commands/setup.js +410 -0
  129. package/dist/interface/commands/setup.js.map +1 -0
  130. package/dist/interface/commands/start.d.ts +3 -0
  131. package/dist/interface/commands/start.d.ts.map +1 -0
  132. package/dist/interface/commands/start.js +171 -0
  133. package/dist/interface/commands/start.js.map +1 -0
  134. package/dist/interface/commands/status.d.ts +3 -0
  135. package/dist/interface/commands/status.d.ts.map +1 -0
  136. package/dist/interface/commands/status.js +77 -0
  137. package/dist/interface/commands/status.js.map +1 -0
  138. package/dist/interface/commands/stop.d.ts +3 -0
  139. package/dist/interface/commands/stop.d.ts.map +1 -0
  140. package/dist/interface/commands/stop.js +62 -0
  141. package/dist/interface/commands/stop.js.map +1 -0
  142. package/dist/interface/commands/update.d.ts +3 -0
  143. package/dist/interface/commands/update.d.ts.map +1 -0
  144. package/dist/interface/commands/update.js +53 -0
  145. package/dist/interface/commands/update.js.map +1 -0
  146. package/dist/interface/commands/wallet.d.ts +3 -0
  147. package/dist/interface/commands/wallet.d.ts.map +1 -0
  148. package/dist/interface/commands/wallet.js +446 -0
  149. package/dist/interface/commands/wallet.js.map +1 -0
  150. package/dist/interface/commands/webhook.d.ts +3 -0
  151. package/dist/interface/commands/webhook.d.ts.map +1 -0
  152. package/dist/interface/commands/webhook.js +99 -0
  153. package/dist/interface/commands/webhook.js.map +1 -0
  154. package/dist/interface/commands/withdraw.d.ts +3 -0
  155. package/dist/interface/commands/withdraw.d.ts.map +1 -0
  156. package/dist/interface/commands/withdraw.js +261 -0
  157. package/dist/interface/commands/withdraw.js.map +1 -0
  158. package/dist/interface/interactive.d.ts +43 -0
  159. package/dist/interface/interactive.d.ts.map +1 -0
  160. package/dist/interface/interactive.js +276 -0
  161. package/dist/interface/interactive.js.map +1 -0
  162. package/dist/interface/logo.d.ts +2 -0
  163. package/dist/interface/logo.d.ts.map +1 -0
  164. package/dist/interface/logo.js +21 -0
  165. package/dist/interface/logo.js.map +1 -0
  166. package/dist/interface/webhooks.d.ts +19 -0
  167. package/dist/interface/webhooks.d.ts.map +1 -0
  168. package/dist/interface/webhooks.js +172 -0
  169. package/dist/interface/webhooks.js.map +1 -0
  170. package/dist/utils/format.d.ts +19 -0
  171. package/dist/utils/format.d.ts.map +1 -0
  172. package/dist/utils/format.js +107 -0
  173. package/dist/utils/format.js.map +1 -0
  174. package/dist/utils/logger.d.ts +20 -0
  175. package/dist/utils/logger.d.ts.map +1 -0
  176. package/dist/utils/logger.js +47 -0
  177. package/dist/utils/logger.js.map +1 -0
  178. package/dist/utils/paths.d.ts +35 -0
  179. package/dist/utils/paths.d.ts.map +1 -0
  180. package/dist/utils/paths.js +45 -0
  181. package/dist/utils/paths.js.map +1 -0
  182. package/dist/utils/platform.d.ts +26 -0
  183. package/dist/utils/platform.d.ts.map +1 -0
  184. package/dist/utils/platform.js +72 -0
  185. package/dist/utils/platform.js.map +1 -0
  186. package/dist/version.d.ts +3 -0
  187. package/dist/version.d.ts.map +1 -0
  188. package/dist/version.js +3 -0
  189. package/dist/version.js.map +1 -0
  190. package/package.json +67 -0
@@ -0,0 +1,355 @@
1
+ import fs from "node:fs";
2
+ import { isAgentRunning, stopAgent, spawnAgent, findAvailablePort, isPortAvailable, findAgentProcess } from "../agent/process.js";
3
+ import { resolveBinary, downloadBinary } from "../agent/binary.js";
4
+ import { mergeConfigs, readBlockclawConfig, setConfigValue } from "../agent/config.js";
5
+ import { logger } from "../utils/logger.js";
6
+ import { AGENT_LOG } from "../utils/paths.js";
7
+ // ---------------------------------------------------------------------------
8
+ // Constants
9
+ // ---------------------------------------------------------------------------
10
+ const DEFAULT_MAX_RESTARTS = 5;
11
+ const DEFAULT_WINDOW_MS = 300_000; // 5 minutes
12
+ const BACKOFF_BASE_MS = 1_000;
13
+ const BACKOFF_MAX_EXPONENT = 4; // cap at 2^4 = 16s
14
+ // ---------------------------------------------------------------------------
15
+ // SelfHealer
16
+ // ---------------------------------------------------------------------------
17
+ export class SelfHealer {
18
+ client;
19
+ memory;
20
+ maxRestarts;
21
+ windowMs;
22
+ /** Timestamps of recent restarts within the rolling window */
23
+ restartTimestamps = [];
24
+ /** Consecutive restart count for exponential backoff */
25
+ consecutiveRestarts = 0;
26
+ constructor(client, memory, options) {
27
+ this.client = client;
28
+ this.memory = memory;
29
+ this.maxRestarts = options?.maxRestarts ?? DEFAULT_MAX_RESTARTS;
30
+ this.windowMs = options?.windowMs ?? DEFAULT_WINDOW_MS;
31
+ }
32
+ // -------------------------------------------------------------------------
33
+ // Public API
34
+ // -------------------------------------------------------------------------
35
+ /**
36
+ * Evaluate a heartbeat event and decide on a healing action.
37
+ * Executes the action and returns which action was taken.
38
+ */
39
+ async handleEvent(event) {
40
+ // Check restart budget first
41
+ this._pruneRestartWindow();
42
+ if (this.restartTimestamps.length >= this.maxRestarts) {
43
+ // Too many restarts in window — stop healing, alert operator
44
+ return this._alertOperator(`Too many restarts (${this.restartTimestamps.length}/${this.maxRestarts}) within ${this.windowMs / 1000}s window. Self-healing paused.`);
45
+ }
46
+ switch (event.type) {
47
+ case "health_ok":
48
+ // Agent is healthy — reset backoff counter so future restarts start fresh
49
+ if (this.consecutiveRestarts > 0) {
50
+ logger.debug(`Agent healthy — resetting restart counter (was ${this.consecutiveRestarts})`);
51
+ this.consecutiveRestarts = 0;
52
+ }
53
+ return "none";
54
+ case "agent_down":
55
+ return this._handleAgentDown();
56
+ case "peers_lost":
57
+ return this._handlePeersLost();
58
+ case "disk_warning":
59
+ return this._handleDiskWarning();
60
+ case "memory_warning":
61
+ return this._handleMemoryWarning();
62
+ case "update_available":
63
+ return this._handleUpdateAvailable(event);
64
+ case "health_degraded":
65
+ return this._handleHealthDegraded(event);
66
+ case "health_critical":
67
+ return this._handleHealthCritical(event);
68
+ case "network_unreachable":
69
+ logger.warn("Internet connectivity lost — waiting for network to recover (no restart)");
70
+ return "none";
71
+ case "rpc_unreachable":
72
+ logger.warn("Sequence0 RPC is unreachable — waiting for RPC to recover (no restart)");
73
+ return "none";
74
+ case "rpc_stale":
75
+ return this._alertOperator(`Chain RPC is stale — block number has not changed for ${event.data?.staleChecks ?? "multiple"} consecutive checks. ` +
76
+ `RPC: ${event.data?.rpcUrl ?? "unknown"}, block: ${event.data?.blockNumber ?? "unknown"}`);
77
+ default:
78
+ return "none";
79
+ }
80
+ }
81
+ /** Reset restart counter and backoff state. */
82
+ reset() {
83
+ this.restartTimestamps = [];
84
+ this.consecutiveRestarts = 0;
85
+ logger.debug("SelfHealer counters reset");
86
+ }
87
+ // -------------------------------------------------------------------------
88
+ // Event handlers
89
+ // -------------------------------------------------------------------------
90
+ /** Agent is unreachable — decide between restart and port conflict fix. */
91
+ async _handleAgentDown() {
92
+ const { running } = isAgentRunning();
93
+ if (!running) {
94
+ // Process is dead — straightforward restart
95
+ logger.warn("Node process is not running, attempting restart...");
96
+ return this._restartAgent();
97
+ }
98
+ // Process is alive but not responding — could be a port conflict
99
+ const config = mergeConfigs();
100
+ const portFree = await isPortAvailable(config.apiPort);
101
+ if (!portFree) {
102
+ // Something else is on the port, or agent is stuck
103
+ const agentPid = await findAgentProcess();
104
+ if (agentPid) {
105
+ // Our agent is running but not responding on expected port — port conflict
106
+ logger.warn(`Port ${config.apiPort} occupied, fixing port conflict...`);
107
+ return this._fixPortConflict();
108
+ }
109
+ }
110
+ // Agent process exists but unresponsive — restart
111
+ logger.warn("Node unresponsive, restarting...");
112
+ return this._restartAgent();
113
+ }
114
+ /** Zero peers — restart agent to re-bootstrap. */
115
+ async _handlePeersLost() {
116
+ logger.warn("No connected peers, restarting node to reconnect...");
117
+ return this._reconnectPeers();
118
+ }
119
+ /** Disk usage above 95% — truncate agent logs. */
120
+ async _handleDiskWarning() {
121
+ logger.warn("Disk usage critical, rotating logs...");
122
+ return this._rotateLogs();
123
+ }
124
+ /** Memory usage above 90% — restart agent to reclaim memory. */
125
+ async _handleMemoryWarning() {
126
+ logger.warn("Memory usage critical, restarting node...");
127
+ return this._restartAgent();
128
+ }
129
+ /** Update available — download and restart if auto-update is enabled. */
130
+ async _handleUpdateAvailable(event) {
131
+ const bcConfig = readBlockclawConfig();
132
+ if (!bcConfig.auto_update) {
133
+ logger.info(`Update available (${event.data?.version}), but auto_update is disabled`);
134
+ return "none";
135
+ }
136
+ const version = event.data?.version;
137
+ if (!version)
138
+ return "none";
139
+ logger.info(`Auto-updating node to ${version}...`);
140
+ try {
141
+ await stopAgent(mergeConfigs().apiPort);
142
+ await downloadBinary(version);
143
+ await this._spawnWithBackoff();
144
+ this._recordRestart();
145
+ logger.success(`Node updated to ${version} and restarted`);
146
+ return "update_binary";
147
+ }
148
+ catch (err) {
149
+ logger.error(`Auto-update failed: ${err instanceof Error ? err.message : String(err)}`);
150
+ return this._alertOperator(`Auto-update to ${version} failed: ${err instanceof Error ? err.message : String(err)}`);
151
+ }
152
+ }
153
+ // -------------------------------------------------------------------------
154
+ // Healing actions
155
+ // -------------------------------------------------------------------------
156
+ /** Stop the agent (if running), resolve binary, and spawn with merged config. */
157
+ async _restartAgent() {
158
+ try {
159
+ // Apply exponential backoff
160
+ await this._backoff();
161
+ const config = mergeConfigs();
162
+ // Stop existing agent if running
163
+ const { running } = isAgentRunning();
164
+ if (running) {
165
+ await stopAgent(config.apiPort);
166
+ }
167
+ await this._spawnWithBackoff();
168
+ this._recordRestart();
169
+ this.consecutiveRestarts++;
170
+ logger.success("Node restarted successfully");
171
+ return "restart_agent";
172
+ }
173
+ catch (err) {
174
+ logger.error(`Restart failed: ${err instanceof Error ? err.message : String(err)}`);
175
+ return this._alertOperator(`Restart failed: ${err instanceof Error ? err.message : String(err)}`);
176
+ }
177
+ }
178
+ /** Find an available port, update config, and restart. */
179
+ async _fixPortConflict() {
180
+ try {
181
+ const config = mergeConfigs();
182
+ // Stop agent first
183
+ const { running } = isAgentRunning();
184
+ if (running) {
185
+ await stopAgent(config.apiPort);
186
+ }
187
+ // Find a new available port
188
+ const newPort = await findAvailablePort(config.apiPort + 1);
189
+ logger.info(`Switching API port from ${config.apiPort} to ${newPort}`);
190
+ // Resolve binary and spawn with new port
191
+ const binPath = await resolveBinary();
192
+ spawnAgent({
193
+ binPath,
194
+ network: config.network,
195
+ port: config.port,
196
+ apiPort: newPort,
197
+ dataDir: config.dataDir,
198
+ rpcUrl: config.rpcUrl,
199
+ registry: config.registry,
200
+ walletFactory: config.walletFactory,
201
+ feeCollector: config.feeCollector,
202
+ programRegistry: config.programRegistry,
203
+ delegationRegistry: config.delegationRegistry,
204
+ daemon: config.daemon,
205
+ bootstrap: config.bootstrap,
206
+ logLevel: config.logLevel,
207
+ externalIp: config.externalIp,
208
+ noRegistry: config.noRegistry,
209
+ });
210
+ // Persist the new port to config so future restarts use it
211
+ setConfigValue("api_port", String(newPort));
212
+ this._recordRestart();
213
+ logger.success(`Node restarted on port ${newPort}`);
214
+ return "fix_port_conflict";
215
+ }
216
+ catch (err) {
217
+ logger.error(`Port conflict fix failed: ${err instanceof Error ? err.message : String(err)}`);
218
+ return this._alertOperator(`Port conflict fix failed: ${err instanceof Error ? err.message : String(err)}`);
219
+ }
220
+ }
221
+ /** Restart the agent to re-bootstrap peer connections. */
222
+ async _reconnectPeers() {
223
+ try {
224
+ await this._backoff();
225
+ const config = mergeConfigs();
226
+ const { running } = isAgentRunning();
227
+ if (running) {
228
+ await stopAgent(config.apiPort);
229
+ }
230
+ await this._spawnWithBackoff();
231
+ this._recordRestart();
232
+ this.consecutiveRestarts++;
233
+ logger.success("Node restarted to reconnect peers");
234
+ return "reconnect_peers";
235
+ }
236
+ catch (err) {
237
+ logger.error(`Peer reconnect failed: ${err instanceof Error ? err.message : String(err)}`);
238
+ return this._alertOperator(`Peer reconnect failed: ${err instanceof Error ? err.message : String(err)}`);
239
+ }
240
+ }
241
+ /** Truncate the agent log file to reclaim disk space. */
242
+ async _rotateLogs() {
243
+ try {
244
+ if (fs.existsSync(AGENT_LOG)) {
245
+ // Truncate the log file to zero bytes
246
+ fs.truncateSync(AGENT_LOG, 0);
247
+ logger.success(`Rotated (truncated) agent log: ${AGENT_LOG}`);
248
+ }
249
+ else {
250
+ logger.debug("No agent log file found to rotate");
251
+ }
252
+ return "rotate_logs";
253
+ }
254
+ catch (err) {
255
+ logger.error(`Log rotation failed: ${err instanceof Error ? err.message : String(err)}`);
256
+ return "rotate_logs"; // still report the action
257
+ }
258
+ }
259
+ /** Committee is degraded — alert operator about at-risk wallets. */
260
+ async _handleHealthDegraded(event) {
261
+ const walletId = event.data?.walletId ?? "unknown";
262
+ const alive = event.data?.alive ?? 0;
263
+ const total = event.data?.total ?? 0;
264
+ const threshold = event.data?.threshold ?? 0;
265
+ const deadPeers = event.data?.deadPeers ?? [];
266
+ logger.warn(`Wallet "${walletId}" security group is degraded: ${alive}/${total} members online ` +
267
+ `(need ${threshold} minimum signers). ${deadPeers.length} member(s) offline.`);
268
+ if (alive <= threshold) {
269
+ logger.warn(`Wallet "${walletId}" is at risk — approaching minimum signers needed. ` +
270
+ `Consider running: blockclaw wallet reshare ${walletId}`);
271
+ }
272
+ // Record the issue for tracking
273
+ try {
274
+ this.memory.recordIssue(`Committee degraded: ${walletId} (${alive}/${total} alive, threshold=${threshold})`, { walletId, alive, total, threshold, deadPeers });
275
+ }
276
+ catch {
277
+ // Non-fatal
278
+ }
279
+ return "alert_operator";
280
+ }
281
+ /** Committee is critical/dead — urgent alert, suggest reshare. */
282
+ async _handleHealthCritical(event) {
283
+ const walletId = event.data?.walletId ?? "unknown";
284
+ const alive = event.data?.alive ?? 0;
285
+ const total = event.data?.total ?? 0;
286
+ const threshold = event.data?.threshold ?? 0;
287
+ return this._alertOperator(`CRITICAL: Wallet "${walletId}" cannot sign — only ${alive}/${total} members online ` +
288
+ `(need ${threshold} minimum). Run: blockclaw wallet reshare ${walletId}`);
289
+ }
290
+ /** Emit a critical alert and stop self-healing. */
291
+ _alertOperator(message) {
292
+ logger.error("===================================================");
293
+ logger.error(" OPERATOR ALERT — Self-healing has been paused");
294
+ logger.error(` ${message}`);
295
+ logger.error("===================================================");
296
+ logger.error("Manual intervention required. Run: blockclaw doctor");
297
+ // Record the alert in memory for persistence
298
+ try {
299
+ this.memory.recordIssue(message, {
300
+ type: "operator_alert",
301
+ timestamp: Date.now(),
302
+ });
303
+ }
304
+ catch {
305
+ // Memory may not be initialized — non-fatal
306
+ }
307
+ return "alert_operator";
308
+ }
309
+ // -------------------------------------------------------------------------
310
+ // Helpers
311
+ // -------------------------------------------------------------------------
312
+ /** Resolve binary, merge config, and spawn the agent process. */
313
+ async _spawnWithBackoff() {
314
+ const binPath = await resolveBinary();
315
+ const config = mergeConfigs();
316
+ spawnAgent({
317
+ binPath,
318
+ network: config.network,
319
+ port: config.port,
320
+ apiPort: config.apiPort,
321
+ dataDir: config.dataDir,
322
+ rpcUrl: config.rpcUrl,
323
+ registry: config.registry,
324
+ walletFactory: config.walletFactory,
325
+ feeCollector: config.feeCollector,
326
+ programRegistry: config.programRegistry,
327
+ delegationRegistry: config.delegationRegistry,
328
+ daemon: config.daemon,
329
+ bootstrap: config.bootstrap,
330
+ logLevel: config.logLevel,
331
+ externalIp: config.externalIp,
332
+ noRegistry: config.noRegistry,
333
+ });
334
+ }
335
+ /** Apply exponential backoff before a restart: 1s, 2s, 4s, 8s, 16s. */
336
+ async _backoff() {
337
+ const exponent = Math.min(this.consecutiveRestarts, BACKOFF_MAX_EXPONENT);
338
+ const delayMs = BACKOFF_BASE_MS * Math.pow(2, exponent);
339
+ if (delayMs > BACKOFF_BASE_MS) {
340
+ logger.info(`Backoff: waiting ${delayMs}ms before restart (attempt ${this.consecutiveRestarts + 1})`);
341
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
342
+ }
343
+ }
344
+ /** Record a restart timestamp in the rolling window. */
345
+ _recordRestart() {
346
+ this.restartTimestamps.push(Date.now());
347
+ }
348
+ /** Remove restart timestamps older than the rolling window. */
349
+ _pruneRestartWindow() {
350
+ const cutoff = Date.now() - this.windowMs;
351
+ this.restartTimestamps = this.restartTimestamps.filter((ts) => ts > cutoff);
352
+ }
353
+ }
354
+ export default SelfHealer;
355
+ //# sourceMappingURL=healer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healer.js","sourceRoot":"","sources":["../../src/core/healer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { AgentClient } from "../agent/client.js";
2
+ import { SelfHealer } from "./healer.js";
3
+ export interface HeartbeatEvent {
4
+ timestamp: number;
5
+ type: "health_ok" | "health_degraded" | "health_critical" | "agent_down" | "peers_lost" | "disk_warning" | "memory_warning" | "update_available" | "network_unreachable" | "rpc_unreachable" | "rpc_stale";
6
+ message: string;
7
+ data?: Record<string, unknown>;
8
+ }
9
+ export interface HeartbeatOptions {
10
+ /** Interval between checks in milliseconds (default: 30000 = 30s) */
11
+ intervalMs?: number;
12
+ /** Callback invoked for every heartbeat event */
13
+ onEvent?: (event: HeartbeatEvent) => void;
14
+ }
15
+ export declare class Heartbeat {
16
+ private readonly client;
17
+ private readonly intervalMs;
18
+ private readonly onEvent?;
19
+ private timer;
20
+ private consecutiveFailures;
21
+ /** RPC health tracking */
22
+ private lastBlockNumber;
23
+ private staleBlockCount;
24
+ /** Cached RPC URL resolved from config */
25
+ private rpcUrl;
26
+ /** Optional healer — set externally to enable self-healing */
27
+ healer: SelfHealer | null;
28
+ constructor(client: AgentClient, options?: HeartbeatOptions);
29
+ /** Start the periodic heartbeat loop. */
30
+ start(): void;
31
+ /** Stop the heartbeat loop. */
32
+ stop(): void;
33
+ /** Whether the heartbeat loop is currently running. */
34
+ isRunning(): boolean;
35
+ /** Run a single heartbeat check cycle. Useful for testing. */
36
+ runOnce(): Promise<HeartbeatEvent[]>;
37
+ /** Resolve the RPC URL from config, caching the result. */
38
+ private _getRpcUrl;
39
+ /**
40
+ * Check internet connectivity. Tries RPC eth_blockNumber first, falls back
41
+ * to DNS resolution for sequence0.network. Returns true if internet is up.
42
+ */
43
+ private _checkInternet;
44
+ /**
45
+ * Check RPC health by calling eth_blockNumber. Tracks block progression
46
+ * and emits rpc_unreachable or rpc_stale events.
47
+ */
48
+ private _checkRpc;
49
+ /** GET /health — track latency and consecutive failures. */
50
+ private _checkHealth;
51
+ /** GET /status — check peers, sessions, wallets. */
52
+ private _checkStatus;
53
+ /** GET /health/committees — detect degraded/critical committees. */
54
+ private _checkCommittees;
55
+ /** Check system memory usage via os module. */
56
+ private _checkMemory;
57
+ /** Check disk usage via platform utility. */
58
+ private _checkDisk;
59
+ /** Maximum events.log size in bytes before rotation (10 MB). */
60
+ private static readonly MAX_LOG_BYTES;
61
+ /** Rotate events.log if it exceeds the size limit. */
62
+ private _rotateLogIfNeeded;
63
+ /** Append a heartbeat event as a JSON line to the events log file. */
64
+ private _persistEvent;
65
+ }
66
+ export default Heartbeat;
67
+ //# sourceMappingURL=heartbeat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../../src/core/heartbeat.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAmD,MAAM,oBAAoB,CAAC;AAKlG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAOzC,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EACA,WAAW,GACX,iBAAiB,GACjB,iBAAiB,GACjB,YAAY,GACZ,YAAY,GACZ,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,qBAAqB,GACrB,iBAAiB,GACjB,WAAW,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CAC3C;AAqBD,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAkC;IAE3D,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,mBAAmB,CAAK;IAEhC,0BAA0B;IAC1B,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,eAAe,CAAK;IAE5B,0CAA0C;IAC1C,OAAO,CAAC,MAAM,CAAuB;IAErC,8DAA8D;IACvD,MAAM,EAAE,UAAU,GAAG,IAAI,CAAQ;gBAE5B,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,gBAAgB;IAU3D,yCAAyC;IACzC,KAAK,IAAI,IAAI;IAQb,+BAA+B;IAC/B,IAAI,IAAI,IAAI;IAQZ,uDAAuD;IACvD,SAAS,IAAI,OAAO;IAQpB,8DAA8D;IACxD,OAAO,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAgD1C,2DAA2D;IAC3D,OAAO,CAAC,UAAU;IAelB;;;OAGG;YACW,cAAc;IAoC5B;;;OAGG;YACW,SAAS;IAqEvB,4DAA4D;YAC9C,YAAY;IAoD1B,oDAAoD;YACtC,YAAY;IAkC1B,oEAAoE;YACtD,gBAAgB;IAyC9B,+CAA+C;IAC/C,OAAO,CAAC,YAAY;IAmBpB,6CAA6C;YAC/B,UAAU;IAyBxB,gEAAgE;IAChE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAEzD,sDAAsD;IACtD,OAAO,CAAC,kBAAkB;IAW1B,sEAAsE;IACtE,OAAO,CAAC,aAAa;CAStB;AAED,eAAe,SAAS,CAAC"}