UNPKG

@gguf/claw

Version:

Multi-channel AI gateway with extensible messaging integrations

324 lines (320 loc) 10.4 kB
import { c as logoutWeb, f as readWebSelfId, g as webAuthExists, r as resolveWhatsAppAccount } from "./accounts-Dy3mxoHH.js"; import { B as danger, V as info, q as success } from "./registry-C8pj8ctW.js"; import "./paths-DJmOcr7Q.js"; import "./model-selection-DQIwoYb8.js"; import { n as loadConfig } from "./config-DA0pxYcO.js"; import { i as defaultRuntime } from "./subsystem-BfLMZ8kW.js"; import { j as logInfo } from "./agent-scope-OWMdRegz.js"; import "./command-format-sreXrOOr.js"; import "./github-copilot-token-etmIcdko.js"; import "./manifest-registry-Lzj-fZNH.js"; import { i as waitForWaConnection, n as formatError, r as getStatusCode, t as createWaSocket } from "./session-ODHSPCMl.js"; import { randomUUID } from "node:crypto"; import { DisconnectReason } from "@whiskeysockets/baileys"; import QRCodeModule from "qrcode-terminal/vendor/QRCode/index.js"; import QRErrorCorrectLevelModule from "qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js"; import { deflateSync } from "node:zlib"; //#region src/media/png-encode.ts /** * Minimal PNG encoder for generating simple RGBA images without native dependencies. * Used for QR codes, live probes, and other programmatic image generation. */ const CRC_TABLE = (() => { const table = new Uint32Array(256); for (let i = 0; i < 256; i += 1) { let c = i; for (let k = 0; k < 8; k += 1) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1; table[i] = c >>> 0; } return table; })(); /** Compute CRC32 checksum for a buffer (used in PNG chunk encoding). */ function crc32(buf) { let crc = 4294967295; for (let i = 0; i < buf.length; i += 1) crc = CRC_TABLE[(crc ^ buf[i]) & 255] ^ crc >>> 8; return (crc ^ 4294967295) >>> 0; } /** Create a PNG chunk with type, data, and CRC. */ function pngChunk(type, data) { const typeBuf = Buffer.from(type, "ascii"); const len = Buffer.alloc(4); len.writeUInt32BE(data.length, 0); const crc = crc32(Buffer.concat([typeBuf, data])); const crcBuf = Buffer.alloc(4); crcBuf.writeUInt32BE(crc, 0); return Buffer.concat([ len, typeBuf, data, crcBuf ]); } /** Write a pixel to an RGBA buffer. Ignores out-of-bounds writes. */ function fillPixel(buf, x, y, width, r, g, b, a = 255) { if (x < 0 || y < 0 || x >= width) return; const idx = (y * width + x) * 4; if (idx < 0 || idx + 3 >= buf.length) return; buf[idx] = r; buf[idx + 1] = g; buf[idx + 2] = b; buf[idx + 3] = a; } /** Encode an RGBA buffer as a PNG image. */ function encodePngRgba(buffer, width, height) { const stride = width * 4; const raw = Buffer.alloc((stride + 1) * height); for (let row = 0; row < height; row += 1) { const rawOffset = row * (stride + 1); raw[rawOffset] = 0; buffer.copy(raw, rawOffset + 1, row * stride, row * stride + stride); } const compressed = deflateSync(raw); const signature = Buffer.from([ 137, 80, 78, 71, 13, 10, 26, 10 ]); const ihdr = Buffer.alloc(13); ihdr.writeUInt32BE(width, 0); ihdr.writeUInt32BE(height, 4); ihdr[8] = 8; ihdr[9] = 6; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0; return Buffer.concat([ signature, pngChunk("IHDR", ihdr), pngChunk("IDAT", compressed), pngChunk("IEND", Buffer.alloc(0)) ]); } //#endregion //#region src/web/qr-image.ts const QRCode = QRCodeModule; const QRErrorCorrectLevel = QRErrorCorrectLevelModule; function createQrMatrix(input) { const qr = new QRCode(-1, QRErrorCorrectLevel.L); qr.addData(input); qr.make(); return qr; } async function renderQrPngBase64(input, opts = {}) { const { scale = 6, marginModules = 4 } = opts; const qr = createQrMatrix(input); const modules = qr.getModuleCount(); const size = (modules + marginModules * 2) * scale; const buf = Buffer.alloc(size * size * 4, 255); for (let row = 0; row < modules; row += 1) for (let col = 0; col < modules; col += 1) { if (!qr.isDark(row, col)) continue; const startX = (col + marginModules) * scale; const startY = (row + marginModules) * scale; for (let y = 0; y < scale; y += 1) { const pixelY = startY + y; for (let x = 0; x < scale; x += 1) fillPixel(buf, startX + x, pixelY, size, 0, 0, 0, 255); } } return encodePngRgba(buf, size, size).toString("base64"); } //#endregion //#region src/web/login-qr.ts const ACTIVE_LOGIN_TTL_MS = 3 * 6e4; const activeLogins = /* @__PURE__ */ new Map(); function closeSocket(sock) { try { sock.ws?.close(); } catch {} } async function resetActiveLogin(accountId, reason) { const login = activeLogins.get(accountId); if (login) { closeSocket(login.sock); activeLogins.delete(accountId); } if (reason) logInfo(reason); } function isLoginFresh(login) { return Date.now() - login.startedAt < ACTIVE_LOGIN_TTL_MS; } function attachLoginWaiter(accountId, login) { login.waitPromise = waitForWaConnection(login.sock).then(() => { const current = activeLogins.get(accountId); if (current?.id === login.id) current.connected = true; }).catch((err) => { const current = activeLogins.get(accountId); if (current?.id !== login.id) return; current.error = formatError(err); current.errorStatus = getStatusCode(err); }); } async function restartLoginSocket(login, runtime) { if (login.restartAttempted) return false; login.restartAttempted = true; runtime.log(info("WhatsApp asked for a restart after pairing (code 515); retrying connection once…")); closeSocket(login.sock); try { login.sock = await createWaSocket(false, login.verbose, { authDir: login.authDir }); login.connected = false; login.error = void 0; login.errorStatus = void 0; attachLoginWaiter(login.accountId, login); return true; } catch (err) { login.error = formatError(err); login.errorStatus = getStatusCode(err); return false; } } async function startWebLoginWithQr(opts = {}) { const runtime = opts.runtime ?? defaultRuntime; const account = resolveWhatsAppAccount({ cfg: loadConfig(), accountId: opts.accountId }); const hasWeb = await webAuthExists(account.authDir); const selfId = readWebSelfId(account.authDir); if (hasWeb && !opts.force) return { message: `WhatsApp is already linked (${selfId.e164 ?? selfId.jid ?? "unknown"}). Say “relink” if you want a fresh QR.` }; const existing = activeLogins.get(account.accountId); if (existing && isLoginFresh(existing) && existing.qrDataUrl) return { qrDataUrl: existing.qrDataUrl, message: "QR already active. Scan it in WhatsApp → Linked Devices." }; await resetActiveLogin(account.accountId); let resolveQr = null; let rejectQr = null; const qrPromise = new Promise((resolve, reject) => { resolveQr = resolve; rejectQr = reject; }); const qrTimer = setTimeout(() => { rejectQr?.(/* @__PURE__ */ new Error("Timed out waiting for WhatsApp QR")); }, Math.max(opts.timeoutMs ?? 3e4, 5e3)); let sock; let pendingQr = null; try { sock = await createWaSocket(false, Boolean(opts.verbose), { authDir: account.authDir, onQr: (qr) => { if (pendingQr) return; pendingQr = qr; const current = activeLogins.get(account.accountId); if (current && !current.qr) current.qr = qr; clearTimeout(qrTimer); runtime.log(info("WhatsApp QR received.")); resolveQr?.(qr); } }); } catch (err) { clearTimeout(qrTimer); await resetActiveLogin(account.accountId); return { message: `Failed to start WhatsApp login: ${String(err)}` }; } const login = { accountId: account.accountId, authDir: account.authDir, isLegacyAuthDir: account.isLegacyAuthDir, id: randomUUID(), sock, startedAt: Date.now(), connected: false, waitPromise: Promise.resolve(), restartAttempted: false, verbose: Boolean(opts.verbose) }; activeLogins.set(account.accountId, login); if (pendingQr && !login.qr) login.qr = pendingQr; attachLoginWaiter(account.accountId, login); let qr; try { qr = await qrPromise; } catch (err) { clearTimeout(qrTimer); await resetActiveLogin(account.accountId); return { message: `Failed to get QR: ${String(err)}` }; } login.qrDataUrl = `data:image/png;base64,${await renderQrPngBase64(qr)}`; return { qrDataUrl: login.qrDataUrl, message: "Scan this QR in WhatsApp → Linked Devices." }; } async function waitForWebLogin(opts = {}) { const runtime = opts.runtime ?? defaultRuntime; const account = resolveWhatsAppAccount({ cfg: loadConfig(), accountId: opts.accountId }); const activeLogin = activeLogins.get(account.accountId); if (!activeLogin) return { connected: false, message: "No active WhatsApp login in progress." }; const login = activeLogin; if (!isLoginFresh(login)) { await resetActiveLogin(account.accountId); return { connected: false, message: "The login QR expired. Ask me to generate a new one." }; } const timeoutMs = Math.max(opts.timeoutMs ?? 12e4, 1e3); const deadline = Date.now() + timeoutMs; while (true) { const remaining = deadline - Date.now(); if (remaining <= 0) return { connected: false, message: "Still waiting for the QR scan. Let me know when you’ve scanned it." }; const timeout = new Promise((resolve) => setTimeout(() => resolve("timeout"), remaining)); if (await Promise.race([login.waitPromise.then(() => "done"), timeout]) === "timeout") return { connected: false, message: "Still waiting for the QR scan. Let me know when you’ve scanned it." }; if (login.error) { if (login.errorStatus === DisconnectReason.loggedOut) { await logoutWeb({ authDir: login.authDir, isLegacyAuthDir: login.isLegacyAuthDir, runtime }); const message = "WhatsApp reported the session is logged out. Cleared cached web session; please scan a new QR."; await resetActiveLogin(account.accountId, message); runtime.log(danger(message)); return { connected: false, message }; } if (login.errorStatus === 515) { if (await restartLoginSocket(login, runtime) && isLoginFresh(login)) continue; } const message = `WhatsApp login failed: ${login.error}`; await resetActiveLogin(account.accountId, message); runtime.log(danger(message)); return { connected: false, message }; } if (login.connected) { const message = "✅ Linked! WhatsApp is ready."; runtime.log(success(message)); await resetActiveLogin(account.accountId); return { connected: true, message }; } return { connected: false, message: "Login ended without a connection." }; } } //#endregion export { startWebLoginWithQr, waitForWebLogin };