Fix WebSocket crash + heartbeat default 10min + docs refresh

- Wrap Baileys connection.update listeners in try-catch to prevent
  unhandled exceptions from crashing the relay process
- Add WebSocket-level error handlers in session.ts
- Add global unhandledRejection/uncaughtException handlers in index.ts
- Make listener.onClose error-safe with .catch() in auto-reply.ts
- Change default heartbeat from 30min to 10min
- Rewrite claude-config.md with personality, better explain personal
  assistant features, add recommended MCPs section
This commit is contained in:
Peter Steinberger
2025-11-27 18:21:14 +01:00
parent 549ad272fc
commit 85f53a4174
7 changed files with 297 additions and 87 deletions

View File

@@ -117,5 +117,23 @@ const isMain =
process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
if (isMain) {
// Global error handlers to prevent silent crashes from unhandled rejections/exceptions.
// These log the error and exit gracefully instead of crashing without trace.
process.on("unhandledRejection", (reason, promise) => {
console.error(
"[warelay] Unhandled promise rejection:",
reason instanceof Error ? reason.stack ?? reason.message : reason,
);
process.exit(1);
});
process.on("uncaughtException", (error) => {
console.error(
"[warelay] Uncaught exception:",
error.stack ?? error.message,
);
process.exit(1);
});
program.parseAsync(process.argv);
}

View File

@@ -45,7 +45,7 @@ export type WebMonitorTuning = {
const formatDuration = (ms: number) =>
ms >= 1000 ? `${(ms / 1000).toFixed(2)}s` : `${ms}ms`;
const DEFAULT_REPLY_HEARTBEAT_MINUTES = 30;
const DEFAULT_REPLY_HEARTBEAT_MINUTES = 10;
export const HEARTBEAT_TOKEN = "HEARTBEAT_OK";
export const HEARTBEAT_PROMPT = "HEARTBEAT ultrathink";
@@ -758,7 +758,13 @@ export async function monitorWebProvider(
}
const reason = await Promise.race([
listener.onClose ?? waitForever(),
listener.onClose?.catch((err) => {
reconnectLogger.error(
{ error: String(err) },
"listener.onClose rejected",
);
return { status: 500, isLoggedOut: false, error: err };
}) ?? waitForever(),
abortPromise ?? waitForever(),
]);

View File

@@ -165,12 +165,24 @@ export async function monitorWebInbox(options: {
sock.ev.on(
"connection.update",
(update: Partial<import("@whiskeysockets/baileys").ConnectionState>) => {
if (update.connection === "close") {
const status = getStatusCode(update.lastDisconnect?.error);
try {
if (update.connection === "close") {
const status = getStatusCode(update.lastDisconnect?.error);
onCloseResolve?.({
status,
isLoggedOut: status === DisconnectReason.loggedOut,
error: update.lastDisconnect?.error,
});
}
} catch (err) {
inboundLogger.error(
{ error: String(err) },
"connection.update handler error",
);
onCloseResolve?.({
status,
isLoggedOut: status === DisconnectReason.loggedOut,
error: update.lastDisconnect?.error,
status: undefined,
isLoggedOut: false,
error: err,
});
}
},

View File

@@ -57,29 +57,45 @@ export async function createWaSocket(printQr: boolean, verbose: boolean) {
markOnlineOnConnect: false,
});
const sessionLogger = getChildLogger({ module: "web-session" });
sock.ev.on("creds.update", saveCreds);
sock.ev.on(
"connection.update",
(update: Partial<import("@whiskeysockets/baileys").ConnectionState>) => {
const { connection, lastDisconnect, qr } = update;
if (qr && printQr) {
console.log("Scan this QR in WhatsApp (Linked Devices):");
qrcode.generate(qr, { small: true });
}
if (connection === "close") {
const status = getStatusCode(lastDisconnect?.error);
if (status === DisconnectReason.loggedOut) {
console.error(
danger("WhatsApp session logged out. Run: warelay login"),
);
try {
const { connection, lastDisconnect, qr } = update;
if (qr && printQr) {
console.log("Scan this QR in WhatsApp (Linked Devices):");
qrcode.generate(qr, { small: true });
}
}
if (connection === "open" && verbose) {
console.log(success("WhatsApp Web connected."));
if (connection === "close") {
const status = getStatusCode(lastDisconnect?.error);
if (status === DisconnectReason.loggedOut) {
console.error(
danger("WhatsApp session logged out. Run: warelay login"),
);
}
}
if (connection === "open" && verbose) {
console.log(success("WhatsApp Web connected."));
}
} catch (err) {
sessionLogger.error(
{ error: String(err) },
"connection.update handler error",
);
}
},
);
// Handle WebSocket-level errors to prevent unhandled exceptions from crashing the process
if (sock.ws) {
sock.ws.on("error", (err: Error) => {
sessionLogger.error({ error: String(err) }, "WebSocket error");
});
}
return sock;
}