diff --git a/apps/x/apps/main/src/ipc.ts b/apps/x/apps/main/src/ipc.ts index 2dc4aadf..d06f837e 100644 --- a/apps/x/apps/main/src/ipc.ts +++ b/apps/x/apps/main/src/ipc.ts @@ -51,6 +51,8 @@ import type { CodeSession } from '@x/shared/dist/code-sessions.js'; import { invalidateCopilotInstructionsCache } from '@x/core/dist/application/assistant/instructions.js'; import { triggerSync as triggerGranolaSync } from '@x/core/dist/knowledge/granola/sync.js'; import { ISlackConfigRepo } from '@x/core/dist/slack/repo.js'; +import { IChannelsConfigRepo } from '@x/core/dist/channels/repo.js'; +import { applyChannelsConfig, getChannelsStatus, logoutWhatsApp, subscribeChannelsStatus } from '@x/core/dist/channels/service.js'; import { runAgentSlack, getAgentSlackCliStatus, AgentSlackRunError } from '@x/core/dist/slack/agent-slack-exec.js'; import { knowledgeSourcesRepo } from '@x/core/dist/knowledge/sources/repo.js'; import { rankSlackHomeMessages } from '@x/core/dist/knowledge/sources/rank_slack_home.js'; @@ -646,6 +648,20 @@ function emitSessionEvent(event: SessionBusEvent): void { } } +// Mobile channels: status changes (QR pairing, connect/disconnect) → renderer. +let channelsWatcher: (() => void) | null = null; +export function startChannelsWatcher(): void { + if (channelsWatcher) return; + channelsWatcher = subscribeChannelsStatus((status) => { + const windows = BrowserWindow.getAllWindows(); + for (const win of windows) { + if (!win.isDestroyed() && win.webContents) { + win.webContents.send('channels:status', status); + } + } + }); +} + let sessionsWatcher: (() => void) | null = null; export function startSessionsWatcher(): void { if (sessionsWatcher) { @@ -1208,6 +1224,22 @@ export function setupIpcHandlers() { return { success: true }; }, + // ── Mobile channels (WhatsApp / Telegram bridge) ───────────── + 'channels:getConfig': async () => { + return container.resolve('channelsConfigRepo').getConfig(); + }, + 'channels:setConfig': async (_event, args) => { + await container.resolve('channelsConfigRepo').setConfig(args); + await applyChannelsConfig(args); + return { success: true }; + }, + 'channels:getStatus': async () => { + return getChannelsStatus(); + }, + 'channels:whatsappLogout': async () => { + await logoutWhatsApp(); + return { success: true }; + }, 'slack:getConfig': async () => { const repo = container.resolve('slackConfigRepo'); const config = await repo.getConfig(); diff --git a/apps/x/apps/main/src/main.ts b/apps/x/apps/main/src/main.ts index 9f17fdc8..7af32d35 100644 --- a/apps/x/apps/main/src/main.ts +++ b/apps/x/apps/main/src/main.ts @@ -3,6 +3,7 @@ import path from "node:path"; import { setupIpcHandlers, startRunsWatcher, startSessionsWatcher, + startChannelsWatcher, startCodeSessionStatusWatcher, startServicesWatcher, startLiveNoteAgentWatcher, @@ -25,6 +26,7 @@ import { init as initEmailLabeling } from "@x/core/dist/knowledge/label_emails.j import { init as initNoteTagging } from "@x/core/dist/knowledge/tag_notes.js"; import { init as initInlineTasks } from "@x/core/dist/knowledge/inline_tasks.js"; import { init as initAgentRunner } from "@x/core/dist/agent-schedule/runner.js"; +import { init as initChannels } from "@x/core/dist/channels/service.js"; import { init as initAgentNotes } from "@x/core/dist/knowledge/agent_notes.js"; import { init as initCalendarNotifications } from "@x/core/dist/knowledge/notify_calendar_meetings.js"; import { init as initMeetingPrep } from "@x/core/dist/knowledge/meeting_prep_scheduler.js"; @@ -414,6 +416,13 @@ app.whenReady().then(async () => { await container.resolve('sessions').initialize(); startSessionsWatcher(); + // Mobile channels (WhatsApp/Telegram bridge): needs the session index, so + // start after initialize(). Failures must never block boot. + startChannelsWatcher(); + initChannels().catch((error) => { + console.error('[Channels] Failed to start mobile channels:', error); + }); + // start code-session status tracker (derives working/needs-you/idle + notifications) startCodeSessionStatusWatcher(); diff --git a/apps/x/apps/renderer/src/components/settings-dialog.tsx b/apps/x/apps/renderer/src/components/settings-dialog.tsx index 6200cef9..e89e69f7 100644 --- a/apps/x/apps/renderer/src/components/settings-dialog.tsx +++ b/apps/x/apps/renderer/src/components/settings-dialog.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { useState, useEffect, useCallback, useMemo } from "react" -import { Server, Key, Shield, Palette, Monitor, Sun, Moon, Loader2, CheckCircle2, Plus, X, Wrench, Search, ChevronRight, Link2, Tags, Mail, BookOpen, User, Plug, HelpCircle, MessageCircle, Bug, Terminal, AlertTriangle, RefreshCw, PanelRight, Bell } from "lucide-react" +import { Server, Key, Shield, Palette, Monitor, Sun, Moon, Loader2, CheckCircle2, Plus, X, Wrench, Search, ChevronRight, Link2, Tags, Mail, BookOpen, User, Plug, HelpCircle, MessageCircle, Bug, Terminal, AlertTriangle, RefreshCw, PanelRight, Bell, Smartphone } from "lucide-react" import { Dialog, @@ -25,10 +25,11 @@ import { useTheme } from "@/contexts/theme-context" import { toast } from "sonner" import { AccountSettings } from "@/components/settings/account-settings" import { ConnectedAccountsSettings } from "@/components/settings/connected-accounts-settings" +import { MobileChannelsSettings } from "@/components/settings/mobile-channels-settings" import type { ApprovalPolicy } from "@x/shared/src/code-mode.js" import { startProvisioning, useProvisioning, enabledOptimistic, type AgentStatus, type CodeModeAgentStatus } from "@/lib/code-mode-provisioning" -type ConfigTab = "account" | "connections" | "models" | "mcp" | "security" | "code-mode" | "appearance" | "notifications" | "note-tagging" | "help" +type ConfigTab = "account" | "connections" | "mobile" | "models" | "mcp" | "security" | "code-mode" | "appearance" | "notifications" | "note-tagging" | "help" interface TabConfig { id: ConfigTab @@ -51,6 +52,12 @@ const tabs: TabConfig[] = [ icon: Plug, description: "Manage accounts and tools", }, + { + id: "mobile", + label: "Mobile", + icon: Smartphone, + description: "Chat with Rowboat from WhatsApp or Telegram", + }, { id: "models", label: "Models", @@ -2216,7 +2223,7 @@ export function SettingsDialog({ children, defaultTab = "account", open: control {/* Content */} -
+
{activeTab === "account" ? ( ) : activeTab === "connections" ? ( @@ -2231,6 +2238,8 @@ export function SettingsDialog({ children, defaultTab = "account", open: control
+ ) : activeTab === "mobile" ? ( + ) : activeTab === "models" ? ( rowboatConnected ? diff --git a/apps/x/apps/renderer/src/components/settings/mobile-channels-settings.tsx b/apps/x/apps/renderer/src/components/settings/mobile-channels-settings.tsx new file mode 100644 index 00000000..4f8fc2a4 --- /dev/null +++ b/apps/x/apps/renderer/src/components/settings/mobile-channels-settings.tsx @@ -0,0 +1,285 @@ +"use client" + +import { useCallback, useEffect, useState } from "react" +import type { z } from "zod" +import { Loader2, MessageCircle, Send, Smartphone } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Separator } from "@/components/ui/separator" +import { Switch } from "@/components/ui/switch" +import { toast } from "sonner" +import type { ChannelsConfig, ChannelsStatus } from "@x/shared/src/channels.js" + +type Config = z.infer +type Status = z.infer + +// Comma/space/newline separated ids ↔ list. +function parseIdList(draft: string): string[] { + return draft + .split(/[\s,]+/) + .map((s) => s.replace(/[^\d]/g, "")) + .filter(Boolean) +} + +export function MobileChannelsSettings({ dialogOpen }: { dialogOpen: boolean }) { + const [config, setConfig] = useState(null) + const [status, setStatus] = useState(null) + const [tokenDraft, setTokenDraft] = useState("") + const [waAllowDraft, setWaAllowDraft] = useState("") + const [tgAllowDraft, setTgAllowDraft] = useState("") + const [saving, setSaving] = useState(false) + + useEffect(() => { + if (!dialogOpen) return + let cancelled = false + void (async () => { + try { + const [cfg, st] = await Promise.all([ + window.ipc.invoke("channels:getConfig", null), + window.ipc.invoke("channels:getStatus", null), + ]) + if (cancelled) return + setConfig(cfg) + setStatus(st) + setTokenDraft(cfg.telegram.botToken) + setWaAllowDraft(cfg.whatsapp.allowFrom.join(", ")) + setTgAllowDraft(cfg.telegram.allowFrom.join(", ")) + } catch { + if (!cancelled) toast.error("Failed to load mobile channel settings") + } + })() + const unsubscribe = window.ipc.on("channels:status", (st) => { + setStatus(st) + }) + return () => { + cancelled = true + unsubscribe() + } + }, [dialogOpen]) + + const save = useCallback(async (next: Config) => { + setConfig(next) + setSaving(true) + try { + await window.ipc.invoke("channels:setConfig", next) + } catch (error) { + toast.error(error instanceof Error ? error.message : "Failed to save channel settings") + } finally { + setSaving(false) + } + }, []) + + if (!config) { + return ( +
+ +
+ ) + } + + const wa = status?.whatsapp + const tg = status?.telegram + + return ( +
+
+ +

+ Chat with Rowboat from your phone. Send help for + commands (list, resume 2,{" "} + new, stop) — anything + else is a message to the current chat. Your computer must be on with Rowboat running. +

+
+ + {/* WhatsApp */} +
+
+
+
+ +
+
+ WhatsApp + + {wa?.state === "connected" + ? `Linked as +${wa.self ?? "?"} — message yourself to use it` + : wa?.state === "qr" + ? "Scan the QR below with your phone" + : wa?.state === "starting" + ? "Connecting…" + : wa?.state === "error" + ? wa.error ?? "Error" + : "Links your own WhatsApp as a companion device"} + +
+
+
+ {wa?.state === "connected" && ( + + )} + + void save({ ...config, whatsapp: { ...config.whatsapp, enabled } }) + } + /> +
+
+ + {config.whatsapp.enabled && wa?.state === "qr" && wa.qrDataUrl && ( +
+ WhatsApp pairing QR +
+

Link Rowboat to WhatsApp

+

1. Open WhatsApp on your phone

+

2. Settings → Linked Devices → Link a Device

+

3. Scan this code

+

+ Then message yourself (your own contact) to talk + to Rowboat. +

+
+
+ )} + + {config.whatsapp.enabled && ( +
+ +
+ setWaAllowDraft(e.target.value)} + placeholder="e.g. 14155551234, 919876543210" + className="h-8 text-xs" + /> + +
+

+ Your own number (self-chat) is always allowed. Digits only, with country code. +

+
+ )} +
+ + + + {/* Telegram */} +
+
+
+
+ +
+
+ Telegram + + {tg?.state === "polling" + ? `Listening${tg.botUsername ? ` as @${tg.botUsername}` : ""}` + : tg?.state === "starting" + ? "Connecting…" + : tg?.state === "error" + ? tg.error ?? "Error" + : "Uses your own bot — create one with @BotFather"} + +
+
+ + void save({ ...config, telegram: { ...config.telegram, enabled } }) + } + /> +
+ + {config.telegram.enabled && ( + <> +
+ +
+ setTokenDraft(e.target.value)} + placeholder="123456789:AAF…" + className="h-8 text-xs font-mono" + /> + +
+

+ Message @BotFather on Telegram → /newbot → paste the token here. +

+
+
+ +
+ setTgAllowDraft(e.target.value)} + placeholder="e.g. 123456789" + className="h-8 text-xs" + /> + +
+

+ Message your bot once — it replies with your chat ID to add here. +

+
+ + )} +
+
+ ) +} diff --git a/apps/x/packages/core/package.json b/apps/x/packages/core/package.json index 18c32a8c..f17f0bc4 100644 --- a/apps/x/packages/core/package.json +++ b/apps/x/packages/core/package.json @@ -30,6 +30,7 @@ "@x/shared": "workspace:*", "ai": "^5.0.133", "awilix": "^12.0.5", + "baileys": "7.0.0-rc13", "chokidar": "^4.0.3", "cors": "^2.8.6", "cron-parser": "^5.5.0", @@ -45,6 +46,7 @@ "papaparse": "^5.5.3", "pdf-parse": "^2.4.5", "posthog-node": "^4.18.0", + "qrcode": "^1.5.4", "react": "^19.2.3", "xlsx": "^0.18.5", "yaml": "^2.8.2", @@ -56,6 +58,7 @@ "@types/node": "^25.0.3", "@types/papaparse": "^5.5.2", "@types/pdf-parse": "^1.1.5", + "@types/qrcode": "^1.5.6", "vitest": "catalog:" } } diff --git a/apps/x/packages/core/src/channels/bridge.ts b/apps/x/packages/core/src/channels/bridge.ts new file mode 100644 index 00000000..187331cc --- /dev/null +++ b/apps/x/packages/core/src/channels/bridge.ts @@ -0,0 +1,396 @@ +import type { SessionIndexEntry } from "@x/shared/dist/sessions.js"; +import { reduceTurn, type TurnStreamEvent } from "@x/shared/dist/turns.js"; +import { assistantText, lastAssistantText } from "../agents/headless.js"; +import { TurnNotSettledError, type ISessions } from "../sessions/api.js"; +import type { EmitterSessionBus } from "../sessions/bus.js"; + +// Transport-agnostic command layer: inbound texts from a messaging channel +// are parsed into commands (list / resume / new / stop / status) or forwarded +// into a regular chat session; the turn's final assistant text is sent back +// through the transport's reply callback. Turns run with autoPermission and +// show up live in the desktop UI like any other session. + +const AGENT_ID = "copilot"; +const TURN_TIMEOUT_MS = 30 * 60 * 1000; +const LIST_LIMIT = 10; +// Telegram caps messages at 4096 chars; WhatsApp is far higher. Long replies +// are chunked, then truncated — the desktop app has the full text. +const REPLY_CHUNK_SIZE = 3500; +const MAX_REPLY_CHUNKS = 3; + +const HELP_TEXT = [ + "🤖 Rowboat commands:", + "• list — recent chats", + "• resume N — continue chat N from the list", + "• new [message] — start a fresh chat", + "• status — current chat and what it's doing", + "• stop — cancel the running task", + "", + "Anything else is sent to the current chat.", +].join("\n"); + +export type ReplyFn = (text: string) => Promise; + +interface SenderState { + activeSessionId: string | null; + // sessionIds as last shown by `list` (1-based indexing for `resume N`). + lastList: string[]; + pendingAsk: { turnId: string; toolCallId: string } | null; + busy: boolean; +} + +type Settled = + | { kind: "completed"; text: string | null } + | { kind: "failed"; error: string } + | { kind: "cancelled" } + | { kind: "ask_human"; toolCallId: string; query: string; options?: string[] } + | { kind: "suspended" } + | { kind: "timeout" }; + +function settleOf(event: TurnStreamEvent): Settled | null { + switch (event.type) { + case "turn_completed": + return { kind: "completed", text: assistantText(event.output) }; + case "turn_failed": + return { kind: "failed", error: event.error }; + case "turn_cancelled": + return { kind: "cancelled" }; + case "turn_suspended": { + const ask = event.pendingAsyncTools.find( + (t) => t.toolId === "builtin:ask-human" || t.toolName === "ask-human", + ); + if (ask) { + const input = ask.input as { query?: unknown; options?: unknown } | null; + const query = + typeof input?.query === "string" && input.query + ? input.query + : "The agent needs your input."; + const options = Array.isArray(input?.options) + ? input.options.filter((o): o is string => typeof o === "string") + : undefined; + return { kind: "ask_human", toolCallId: ask.toolCallId, query, options }; + } + // Other async tools settle on their own and the turn resumes; + // keep waiting. Pending permissions need the desktop. + if (event.pendingAsyncTools.length === 0 && event.pendingPermissions.length > 0) { + return { kind: "suspended" }; + } + return null; + } + default: + return null; + } +} + +function relativeTime(iso: string): string { + const then = Date.parse(iso); + if (!Number.isFinite(then)) return ""; + const diffSec = Math.round((Date.now() - then) / 1000); + if (diffSec < 60) return "just now"; + const diffMin = Math.round(diffSec / 60); + if (diffMin < 60) return `${diffMin}m ago`; + const diffHr = Math.round(diffMin / 60); + if (diffHr < 24) return `${diffHr}h ago`; + return `${Math.round(diffHr / 24)}d ago`; +} + +function chunkReply(text: string): string[] { + if (text.length <= REPLY_CHUNK_SIZE) return [text]; + const parts: string[] = []; + let rest = text; + while (rest.length > 0 && parts.length < MAX_REPLY_CHUNKS) { + parts.push(rest.slice(0, REPLY_CHUNK_SIZE)); + rest = rest.slice(REPLY_CHUNK_SIZE); + } + if (rest.length > 0) { + parts[parts.length - 1] += "\n… (truncated — open Rowboat for the full reply)"; + } + return parts; +} + +export class ChannelBridge { + private senders = new Map(); + + constructor( + private readonly deps: { + sessions: ISessions; + sessionBus: EmitterSessionBus; + }, + ) {} + + async handleInbound(senderKey: string, text: string, reply: ReplyFn): Promise { + const trimmed = text.trim(); + if (!trimmed) return; + const state = this.senderState(senderKey); + const lower = trimmed.toLowerCase(); + + try { + if (lower === "help" || lower === "?") { + await reply(HELP_TEXT); + return; + } + if (lower === "list" || lower === "chats") { + await reply(this.renderList(state)); + return; + } + const resume = /^(?:resume|open)\s+(\d+)$/.exec(lower); + if (resume) { + await reply(this.resumeSession(state, Number(resume[1]))); + return; + } + if (lower === "status") { + await reply(this.renderStatus(state)); + return; + } + if (lower === "stop") { + await reply(await this.stopActive(state)); + return; + } + if (lower === "new") { + state.activeSessionId = null; + state.pendingAsk = null; + await reply("🆕 Fresh chat — send your first message."); + return; + } + const newWithText = /^new\s+([\s\S]+)$/i.exec(trimmed); + if (newWithText) { + state.activeSessionId = null; + state.pendingAsk = null; + await this.runMessage(state, newWithText[1].trim(), reply); + return; + } + await this.runMessage(state, trimmed, reply); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + await reply(`❌ ${message}`).catch(() => undefined); + } + } + + private senderState(senderKey: string): SenderState { + let state = this.senders.get(senderKey); + if (!state) { + state = { activeSessionId: null, lastList: [], pendingAsk: null, busy: false }; + this.senders.set(senderKey, state); + } + return state; + } + + private sortedSessions(): SessionIndexEntry[] { + return this.deps.sessions + .listSessions() + .filter((e) => !e.error) + .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)); + } + + private renderList(state: SenderState): string { + const entries = this.sortedSessions().slice(0, LIST_LIMIT); + if (entries.length === 0) { + return "No chats yet — just send a message to start one."; + } + state.lastList = entries.map((e) => e.sessionId); + const lines = entries.map((e, i) => { + const marker = + e.latestTurnStatus === "suspended" ? " ⚠️" : + e.latestTurnStatus === "idle" ? " ⏳" : ""; + const active = e.sessionId === state.activeSessionId ? " ← current" : ""; + return `${i + 1}. ${e.title ?? "Untitled"}${marker} (${relativeTime(e.updatedAt)})${active}`; + }); + return [ + "Recent chats:", + ...lines, + "", + `Reply "resume N" to continue one.`, + ].join("\n"); + } + + private resumeSession(state: SenderState, index: number): string { + if (state.lastList.length === 0) { + state.lastList = this.sortedSessions() + .slice(0, LIST_LIMIT) + .map((e) => e.sessionId); + } + const sessionId = state.lastList[index - 1]; + if (!sessionId) { + return `No chat #${index} — send "list" to see recent chats.`; + } + state.activeSessionId = sessionId; + state.pendingAsk = null; + const entry = this.sortedSessions().find((e) => e.sessionId === sessionId); + return `▶️ Resumed "${entry?.title ?? "Untitled"}" — send a message to continue.`; + } + + private renderStatus(state: SenderState): string { + if (!state.activeSessionId) { + return "No current chat — your next message starts a new one."; + } + const entry = this.sortedSessions().find( + (e) => e.sessionId === state.activeSessionId, + ); + if (!entry) return "Current chat no longer exists — send a message to start fresh."; + const status = state.busy + ? "working" + : entry.latestTurnStatus === "suspended" + ? "waiting on input" + : entry.latestTurnStatus; + return `Current chat: "${entry.title ?? "Untitled"}" — ${status}.`; + } + + private async stopActive(state: SenderState): Promise { + state.pendingAsk = null; + if (!state.activeSessionId) return "Nothing to stop."; + const entry = this.deps.sessions + .listSessions() + .find((e) => e.sessionId === state.activeSessionId); + if (!entry?.latestTurnId) return "Nothing to stop."; + if ( + entry.latestTurnStatus === "completed" || + entry.latestTurnStatus === "failed" || + entry.latestTurnStatus === "cancelled" + ) { + return "Nothing running in the current chat."; + } + await this.deps.sessions.stopTurn(entry.latestTurnId, "stopped from mobile channel"); + return "🛑 Stop requested."; + } + + private async runMessage(state: SenderState, text: string, reply: ReplyFn): Promise { + if (state.busy) { + await reply('⏳ Still working on the previous message — send "stop" to cancel it.'); + return; + } + state.busy = true; + // Subscribe before advancing so no settle event can slip past. + const watcher = this.watchBus(); + try { + let turnId: string; + if (state.pendingAsk) { + const ask = state.pendingAsk; + state.pendingAsk = null; + turnId = ask.turnId; + await reply("⏳ Working on it…"); + await this.deps.sessions.respondToAskHuman(ask.turnId, ask.toolCallId, text); + } else { + if (!state.activeSessionId) { + state.activeSessionId = await this.deps.sessions.createSession(); + } + await reply("⏳ Working on it…"); + const sent = await this.deps.sessions.sendMessage( + state.activeSessionId, + { role: "user", content: text }, + { agent: { agentId: AGENT_ID }, autoPermission: true }, + ); + turnId = sent.turnId; + } + const settled = await watcher.waitFor(turnId, TURN_TIMEOUT_MS); + await this.deliverSettled(state, turnId, settled, reply); + } catch (error) { + if (error instanceof TurnNotSettledError) { + await reply( + '⏳ That chat is still working on something — send "stop" to cancel it, or "new" to start a fresh chat.', + ); + return; + } + throw error; + } finally { + watcher.dispose(); + state.busy = false; + } + } + + private async deliverSettled( + state: SenderState, + turnId: string, + settled: Settled, + reply: ReplyFn, + ): Promise { + switch (settled.kind) { + case "completed": { + let text = settled.text; + if (!text) { + // Rare: final message had no text parts; recover the last + // assistant text from the persisted turn. + try { + const turn = await this.deps.sessions.getTurn(turnId); + text = lastAssistantText(reduceTurn(turn.events)); + } catch { + text = null; + } + } + for (const chunk of chunkReply(text ?? "✅ Done (no text reply).")) { + await reply(chunk); + } + return; + } + case "failed": + await reply(`❌ Task failed: ${settled.error}`); + return; + case "cancelled": + await reply("🛑 Stopped."); + return; + case "ask_human": { + state.pendingAsk = { turnId, toolCallId: settled.toolCallId }; + const lines = [`❓ ${settled.query}`]; + if (settled.options?.length) { + lines.push(...settled.options.map((o, i) => `${i + 1}. ${o}`)); + } + lines.push("", "Reply with your answer."); + await reply(lines.join("\n")); + return; + } + case "suspended": + await reply( + "⚠️ The agent is waiting for a permission approval — open Rowboat on your desktop to continue.", + ); + return; + case "timeout": + await reply( + "⏱️ Still running after 30 minutes — check the desktop app for progress.", + ); + return; + } + } + + // Buffers turn events from the moment of subscription so a settle that + // fires between sendMessage() returning and waitFor() attaching is never + // lost. One watcher per in-flight message; disposed in runMessage. + private watchBus() { + const buffered: Array<{ turnId: string; event: TurnStreamEvent }> = []; + let waiter: { turnId: string; resolve: (settled: Settled) => void } | null = null; + const unsubscribe = this.deps.sessionBus.subscribe((event) => { + if (event.kind !== "turn-event") return; + if (waiter) { + if (event.turnId !== waiter.turnId) return; + const settled = settleOf(event.event); + if (settled) waiter.resolve(settled); + return; + } + buffered.push({ turnId: event.turnId, event: event.event }); + }); + return { + waitFor: (turnId: string, timeoutMs: number): Promise => + new Promise((resolve) => { + for (const b of buffered) { + if (b.turnId !== turnId) continue; + const settled = settleOf(b.event); + if (settled) { + resolve(settled); + return; + } + } + buffered.length = 0; + const timer = setTimeout( + () => resolve({ kind: "timeout" }), + timeoutMs, + ); + waiter = { + turnId, + resolve: (settled) => { + clearTimeout(timer); + resolve(settled); + }, + }; + }), + dispose: unsubscribe, + }; + } +} diff --git a/apps/x/packages/core/src/channels/repo.ts b/apps/x/packages/core/src/channels/repo.ts new file mode 100644 index 00000000..d0ee2607 --- /dev/null +++ b/apps/x/packages/core/src/channels/repo.ts @@ -0,0 +1,40 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import type { z } from 'zod'; +import { WorkDir } from '../config/config.js'; +import { ChannelsConfig, DEFAULT_CHANNELS_CONFIG } from '@x/shared/dist/channels.js'; + +export interface IChannelsConfigRepo { + getConfig(): Promise>; + setConfig(config: z.infer): Promise; +} + +export class FSChannelsConfigRepo implements IChannelsConfigRepo { + private readonly configPath = path.join(WorkDir, 'config', 'channels.json'); + + constructor() { + this.ensureConfigFile(); + } + + private async ensureConfigFile(): Promise { + try { + await fs.access(this.configPath); + } catch { + await fs.writeFile(this.configPath, JSON.stringify(DEFAULT_CHANNELS_CONFIG, null, 2)); + } + } + + async getConfig(): Promise> { + try { + const content = await fs.readFile(this.configPath, 'utf8'); + return ChannelsConfig.parse(JSON.parse(content)); + } catch { + return DEFAULT_CHANNELS_CONFIG; + } + } + + async setConfig(config: z.infer): Promise { + const validated = ChannelsConfig.parse(config); + await fs.writeFile(this.configPath, JSON.stringify(validated, null, 2)); + } +} diff --git a/apps/x/packages/core/src/channels/service.ts b/apps/x/packages/core/src/channels/service.ts new file mode 100644 index 00000000..d7fb8754 --- /dev/null +++ b/apps/x/packages/core/src/channels/service.ts @@ -0,0 +1,183 @@ +import path from "node:path"; +import fs from "node:fs/promises"; +import QRCode from "qrcode"; +import type { z } from "zod"; +import type { ChannelsConfig, ChannelsStatus } from "@x/shared/dist/channels.js"; +import container from "../di/container.js"; +import { WorkDir } from "../config/config.js"; +import type { ISessions } from "../sessions/api.js"; +import type { EmitterSessionBus } from "../sessions/bus.js"; +import { ChannelBridge } from "./bridge.js"; +import type { IChannelsConfigRepo } from "./repo.js"; +import { TelegramTransport } from "./transports/telegram.js"; +import { WhatsAppTransport } from "./transports/whatsapp.js"; + +// Lifecycle owner for the mobile channels: reads config, runs the enabled +// transports against one shared ChannelBridge, and fans status out to the +// renderer (QR pairing, connection state). init() from main after +// sessions.initialize(); applyChannelsConfig() on every settings save. + +type Config = z.infer; +type Status = z.infer; + +const WHATSAPP_AUTH_DIR = path.join(WorkDir, "channels", "whatsapp-auth"); + +let bridge: ChannelBridge | null = null; +let whatsapp: WhatsAppTransport | null = null; +let telegram: TelegramTransport | null = null; + +const status: Status = { + whatsapp: { state: "disabled" }, + telegram: { state: "disabled" }, +}; + +const statusListeners = new Set<(status: Status) => void>(); + +// Serializes apply/logout so a fast settings double-save can't interleave +// transport teardown and startup. +let lifecycle: Promise = Promise.resolve(); + +function notifyStatus(): void { + for (const listener of statusListeners) { + try { + listener(structuredClone(status)); + } catch { + // observers must never affect the channels + } + } +} + +function setWhatsAppStatus(next: Status["whatsapp"]): void { + status.whatsapp = next; + notifyStatus(); +} + +function setTelegramStatus(next: Status["telegram"]): void { + status.telegram = next; + notifyStatus(); +} + +export function getChannelsStatus(): Status { + return structuredClone(status); +} + +export function subscribeChannelsStatus(listener: (status: Status) => void): () => void { + statusListeners.add(listener); + return () => statusListeners.delete(listener); +} + +function ensureBridge(): ChannelBridge { + if (!bridge) { + bridge = new ChannelBridge({ + sessions: container.resolve("sessions"), + sessionBus: container.resolve("sessionBus"), + }); + } + return bridge; +} + +async function stopWhatsApp(): Promise { + if (!whatsapp) return; + await whatsapp.stop().catch(() => undefined); + whatsapp = null; +} + +function stopTelegram(): void { + if (!telegram) return; + telegram.stop(); + telegram = null; +} + +function startWhatsApp(config: Config["whatsapp"]): void { + if (!config.enabled) { + setWhatsAppStatus({ state: "disabled" }); + return; + } + const channelBridge = ensureBridge(); + const transport = new WhatsAppTransport({ + authDir: WHATSAPP_AUTH_DIR, + allowFrom: config.allowFrom, + onInbound: (senderKey, text, reply) => { + void channelBridge.handleInbound(senderKey, text, reply); + }, + onStatus: (update) => { + if (update.state === "qr" && update.qr) { + // Render the pairing QR main-side so the renderer just shows + // an ; the raw pairing string never leaves core. + QRCode.toDataURL(update.qr, { margin: 1, width: 256 }) + .then((qrDataUrl) => setWhatsAppStatus({ state: "qr", qrDataUrl })) + .catch(() => setWhatsAppStatus({ state: "error", error: "Failed to render pairing QR" })); + return; + } + setWhatsAppStatus({ + state: update.state, + ...(update.self ? { self: update.self } : {}), + ...(update.error ? { error: update.error } : {}), + }); + }, + }); + whatsapp = transport; + transport.start().catch((error) => { + setWhatsAppStatus({ + state: "error", + error: error instanceof Error ? error.message : String(error), + }); + }); +} + +function startTelegram(config: Config["telegram"]): void { + if (!config.enabled) { + setTelegramStatus({ state: "disabled" }); + return; + } + if (!config.botToken) { + setTelegramStatus({ state: "error", error: "Bot token missing — create one with @BotFather" }); + return; + } + const channelBridge = ensureBridge(); + const transport = new TelegramTransport({ + botToken: config.botToken, + allowFrom: config.allowFrom, + onInbound: (senderKey, text, reply) => { + void channelBridge.handleInbound(senderKey, text, reply); + }, + onStatus: setTelegramStatus, + }); + telegram = transport; + void transport.start(); +} + +export function applyChannelsConfig(config: Config): Promise { + lifecycle = lifecycle.then(async () => { + await stopWhatsApp(); + stopTelegram(); + startWhatsApp(config.whatsapp); + startTelegram(config.telegram); + }); + return lifecycle; +} + +// Unlink the WhatsApp device and, if the channel is still enabled, restart it +// so a fresh pairing QR appears. Telegram is left untouched. +export function logoutWhatsApp(): Promise { + lifecycle = lifecycle.then(async () => { + if (whatsapp) { + await whatsapp.logout().catch(() => undefined); + whatsapp = null; + } else { + await fs.rm(WHATSAPP_AUTH_DIR, { recursive: true, force: true }); + } + const config = await container + .resolve("channelsConfigRepo") + .getConfig(); + startWhatsApp(config.whatsapp); + }); + return lifecycle; +} + +export async function init(): Promise { + const config = await container + .resolve("channelsConfigRepo") + .getConfig(); + await applyChannelsConfig(config); +} diff --git a/apps/x/packages/core/src/channels/transports/telegram.ts b/apps/x/packages/core/src/channels/transports/telegram.ts new file mode 100644 index 00000000..ccb0069f --- /dev/null +++ b/apps/x/packages/core/src/channels/transports/telegram.ts @@ -0,0 +1,133 @@ +import type { z } from "zod"; +import type { TelegramChannelStatus } from "@x/shared/dist/channels.js"; +import type { ReplyFn } from "../bridge.js"; + +// Telegram Bot API transport. Deliberately dependency-free: the Bot API is +// plain HTTPS — getUpdates long polling (outbound connection, works behind +// NAT) plus sendMessage. The user supplies their own bot token (@BotFather). + +const POLL_TIMEOUT_S = 50; +const RETRY_DELAY_MS = 5000; + +type Status = z.infer; + +interface TelegramUpdate { + update_id: number; + message?: { + message_id: number; + text?: string; + chat: { id: number; type: string }; + from?: { id: number; is_bot?: boolean }; + }; +} + +export interface TelegramTransportOptions { + botToken: string; + allowFrom: string[]; + onInbound: (senderKey: string, text: string, reply: ReplyFn) => void; + onStatus: (status: Status) => void; +} + +export class TelegramTransport { + private abort: AbortController | null = null; + private stopped = false; + private offset = 0; + + constructor(private readonly opts: TelegramTransportOptions) {} + + async start(): Promise { + this.stopped = false; + this.opts.onStatus({ state: "starting" }); + void this.run(); + } + + stop(): void { + this.stopped = true; + this.abort?.abort(); + this.opts.onStatus({ state: "disabled" }); + } + + private api(method: string): string { + return `https://api.telegram.org/bot${this.opts.botToken}/${method}`; + } + + private async call(method: string, body?: unknown, signal?: AbortSignal): Promise { + const res = await fetch(this.api(method), { + method: "POST", + headers: { "content-type": "application/json" }, + body: body === undefined ? undefined : JSON.stringify(body), + ...(signal ? { signal } : {}), + }); + const payload = (await res.json()) as { ok: boolean; result?: unknown; description?: string }; + if (!payload.ok) { + throw new Error(payload.description ?? `Telegram API error (${method})`); + } + return payload.result; + } + + private async run(): Promise { + // Validate the token up front so a typo surfaces in settings + // immediately instead of as a silent poll loop failure. + try { + const me = (await this.call("getMe")) as { username?: string }; + if (this.stopped) return; + this.opts.onStatus({ state: "polling", botUsername: me.username }); + } catch (error) { + if (this.stopped) return; + this.opts.onStatus({ + state: "error", + error: error instanceof Error ? error.message : String(error), + }); + return; + } + + while (!this.stopped) { + this.abort = new AbortController(); + try { + const updates = (await this.call( + "getUpdates", + { + timeout: POLL_TIMEOUT_S, + offset: this.offset, + allowed_updates: ["message"], + }, + this.abort.signal, + )) as TelegramUpdate[]; + for (const update of updates) { + this.offset = Math.max(this.offset, update.update_id + 1); + this.handleUpdate(update); + } + } catch (error) { + if (this.stopped) return; + this.opts.onStatus({ + state: "error", + error: error instanceof Error ? error.message : String(error), + }); + await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS)); + if (this.stopped) return; + this.opts.onStatus({ state: "polling" }); + } + } + } + + private handleUpdate(update: TelegramUpdate): void { + const message = update.message; + if (!message?.text || message.from?.is_bot) return; + // DMs only: group chats would let any member drive the bridge. + if (message.chat.type !== "private") return; + const chatId = String(message.chat.id); + const reply: ReplyFn = (text) => this.send(chatId, text); + if (!this.opts.allowFrom.includes(chatId)) { + void this.send( + chatId, + `⛔ Not authorized. Your chat ID is ${chatId} — add it under Rowboat → Settings → Mobile to pair this chat.`, + ).catch(() => undefined); + return; + } + this.opts.onInbound(`telegram:${chatId}`, message.text, reply); + } + + private async send(chatId: string, text: string): Promise { + await this.call("sendMessage", { chat_id: chatId, text }); + } +} diff --git a/apps/x/packages/core/src/channels/transports/whatsapp.ts b/apps/x/packages/core/src/channels/transports/whatsapp.ts new file mode 100644 index 00000000..4e70f412 --- /dev/null +++ b/apps/x/packages/core/src/channels/transports/whatsapp.ts @@ -0,0 +1,191 @@ +import fs from "node:fs/promises"; +import makeWASocket, { + DisconnectReason, + areJidsSameUser, + isJidGroup, + jidDecode, + useMultiFileAuthState, +} from "baileys"; +import type { ReplyFn } from "../bridge.js"; + +// WhatsApp transport via Baileys: the app links to the user's own WhatsApp +// account as a linked device (QR pairing, same as WhatsApp Web) over an +// outbound WebSocket — no server, no port forwarding. +// +// Access model: the linked account's own self-chat ("message yourself") is +// always allowed; other senders must be explicitly allowlisted by phone +// number. Group chats are ignored entirely. + +type WASocket = ReturnType; + +const RECONNECT_DELAY_MS = 3000; +// Marks bridge-sent messages. In the self-chat our own replies come back on +// messages.upsert like any other message; the marker (plus sent-id tracking) +// keeps the bridge from answering itself in a loop. +const REPLY_MARKER = "🤖 "; + +export interface WhatsAppTransportStatus { + state: "starting" | "qr" | "connected" | "error" | "disabled"; + qr?: string; + self?: string; + error?: string; +} + +export interface WhatsAppTransportOptions { + authDir: string; + allowFrom: string[]; + onInbound: (senderKey: string, text: string, reply: ReplyFn) => void; + onStatus: (status: WhatsAppTransportStatus) => void; +} + +interface TextishMessage { + conversation?: unknown; + extendedTextMessage?: { text?: unknown }; + ephemeralMessage?: { message?: TextishMessage }; +} + +interface InboundWAMessage { + key?: { remoteJid?: string | null; fromMe?: boolean | null; id?: string | null }; + message?: unknown; +} + +function messageText(message: unknown): string | null { + if (!message || typeof message !== "object") return null; + const m = message as TextishMessage; + const unwrapped = m.ephemeralMessage?.message ?? m; + const text: unknown = unwrapped.conversation ?? unwrapped.extendedTextMessage?.text; + return typeof text === "string" && text ? text : null; +} + +export class WhatsAppTransport { + private sock: WASocket | null = null; + private stopped = false; + private sentIds = new Set(); + + constructor(private readonly opts: WhatsAppTransportOptions) {} + + async start(): Promise { + this.stopped = false; + this.opts.onStatus({ state: "starting" }); + await this.connect(); + } + + async stop(): Promise { + this.stopped = true; + try { + this.sock?.end(undefined); + } catch { + // already closed + } + this.sock = null; + this.opts.onStatus({ state: "disabled" }); + } + + // Unlink this device: invalidates the pairing on the phone and clears + // local credentials so the next start shows a fresh QR. + async logout(): Promise { + this.stopped = true; + try { + await this.sock?.logout(); + } catch { + // best effort — clearing creds below is what actually unpairs us + } + this.sock = null; + await fs.rm(this.opts.authDir, { recursive: true, force: true }); + this.opts.onStatus({ state: "disabled" }); + } + + private async connect(): Promise { + if (this.stopped) return; + const { state, saveCreds } = await useMultiFileAuthState(this.opts.authDir); + const sock = makeWASocket({ + auth: state, + syncFullHistory: false, + markOnlineOnConnect: false, + }); + this.sock = sock; + + sock.ev.on("creds.update", saveCreds); + + sock.ev.on("connection.update", (update) => { + if (this.stopped) return; + if (update.qr) { + this.opts.onStatus({ state: "qr", qr: update.qr }); + } + if (update.connection === "open") { + const self = jidDecode(sock.user?.id ?? "")?.user; + this.opts.onStatus({ state: "connected", ...(self ? { self } : {}) }); + } + if (update.connection === "close") { + const statusCode = (update.lastDisconnect?.error as { output?: { statusCode?: number } } | undefined) + ?.output?.statusCode; + if (statusCode === DisconnectReason.loggedOut) { + // Unlinked from the phone; stale creds would loop forever. + void fs.rm(this.opts.authDir, { recursive: true, force: true }); + this.opts.onStatus({ + state: "error", + error: "Logged out from the phone — toggle WhatsApp off and on to pair again.", + }); + return; + } + setTimeout(() => { + this.connect().catch((error) => { + this.opts.onStatus({ + state: "error", + error: error instanceof Error ? error.message : String(error), + }); + }); + }, RECONNECT_DELAY_MS); + } + }); + + sock.ev.on("messages.upsert", ({ messages, type }) => { + if (type !== "notify") return; + for (const msg of messages) { + this.handleMessage(sock, msg); + } + }); + } + + private handleMessage(sock: WASocket, msg: InboundWAMessage): void { + const jid: string | undefined = msg.key?.remoteJid ?? undefined; + if (!jid || isJidGroup(jid) || jid === "status@broadcast") return; + const messageId: string | undefined = msg.key?.id ?? undefined; + if (messageId && this.sentIds.has(messageId)) return; + const text = messageText(msg.message); + if (!text || text.startsWith(REPLY_MARKER)) return; + + const selfIds = [sock.user?.id, (sock.user as { lid?: string } | undefined)?.lid].filter( + (v): v is string => Boolean(v), + ); + const isSelfChat = selfIds.some((selfId) => areJidsSameUser(jid, selfId)); + const senderNumber = jidDecode(jid)?.user ?? ""; + + // Self-chat is the owner by definition. Anyone else must be + // allowlisted — this bridge is remote control over the desktop agent. + if (!isSelfChat) { + if (msg.key?.fromMe) return; + if (!this.opts.allowFrom.includes(senderNumber)) return; + } + + const reply: ReplyFn = (replyText) => this.send(jid, replyText); + this.opts.onInbound(`whatsapp:${senderNumber || jid}`, text, reply); + } + + private async send(jid: string, text: string): Promise { + const sock = this.sock; + if (!sock) throw new Error("WhatsApp is not connected"); + const sent = await sock.sendMessage(jid, { text: `${REPLY_MARKER}${text}` }); + const id = sent?.key?.id; + if (id) { + this.sentIds.add(id); + if (this.sentIds.size > 500) { + // Trim oldest; Set iteration order is insertion order. + for (const old of this.sentIds) { + this.sentIds.delete(old); + if (this.sentIds.size <= 250) break; + } + } + } + } +} diff --git a/apps/x/packages/core/src/di/container.ts b/apps/x/packages/core/src/di/container.ts index 3455f1b7..eefdaa5f 100644 --- a/apps/x/packages/core/src/di/container.ts +++ b/apps/x/packages/core/src/di/container.ts @@ -18,6 +18,7 @@ import { IAbortRegistry, InMemoryAbortRegistry } from "../runs/abort-registry.js import { FSAgentScheduleRepo, IAgentScheduleRepo } from "../agent-schedule/repo.js"; import { FSAgentScheduleStateRepo, IAgentScheduleStateRepo } from "../agent-schedule/state-repo.js"; import { FSSlackConfigRepo, ISlackConfigRepo } from "../slack/repo.js"; +import { FSChannelsConfigRepo, IChannelsConfigRepo } from "../channels/repo.js"; import { CodeModeManager } from "../code-mode/acp/manager.js"; import { CodePermissionRegistry } from "../code-mode/acp/permission-registry.js"; import { FSCodeProjectsRepo, ICodeProjectsRepo } from "../code-mode/projects/repo.js"; @@ -90,6 +91,7 @@ container.register({ agentScheduleRepo: asClass(FSAgentScheduleRepo).singleton(), agentScheduleStateRepo: asClass(FSAgentScheduleStateRepo).singleton(), slackConfigRepo: asClass(FSSlackConfigRepo).singleton(), + channelsConfigRepo: asClass(FSChannelsConfigRepo).singleton(), // ACP code-mode engine: the manager holds a live agent connection per chat only // around an active turn (torn down after a short idle grace; resumed via diff --git a/apps/x/packages/shared/src/channels.ts b/apps/x/packages/shared/src/channels.ts new file mode 100644 index 00000000..d6e4fa33 --- /dev/null +++ b/apps/x/packages/shared/src/channels.ts @@ -0,0 +1,54 @@ +import { z } from "zod"; + +// --------------------------------------------------------------------------- +// Mobile messaging channels (WhatsApp / Telegram bridges). +// +// Each channel links the user's OWN account/bot to the local app: WhatsApp +// connects as a linked device (QR pairing), Telegram long-polls the user's +// own bot token. There is no central relay — the desktop app must be running. +// --------------------------------------------------------------------------- + +export const WhatsAppChannelConfig = z.object({ + enabled: z.boolean(), + // Extra sender phone numbers (digits only, no '+') allowed to drive the + // bridge. The linked account itself (self-chat) is always allowed. + allowFrom: z.array(z.string()), +}); + +export const TelegramChannelConfig = z.object({ + enabled: z.boolean(), + botToken: z.string(), + // Telegram chat ids (numeric strings) allowed to drive the bridge. + // Unknown senders are told their chat id so it can be added here. + allowFrom: z.array(z.string()), +}); + +export const ChannelsConfig = z.object({ + whatsapp: WhatsAppChannelConfig, + telegram: TelegramChannelConfig, +}); + +export const DEFAULT_CHANNELS_CONFIG: z.infer = { + whatsapp: { enabled: false, allowFrom: [] }, + telegram: { enabled: false, botToken: "", allowFrom: [] }, +}; + +export const WhatsAppChannelStatus = z.object({ + state: z.enum(["disabled", "starting", "qr", "connected", "error"]), + // PNG data URL of the pairing QR (present while state === "qr"). + qrDataUrl: z.string().optional(), + // Linked account phone number (digits) once connected. + self: z.string().optional(), + error: z.string().optional(), +}); + +export const TelegramChannelStatus = z.object({ + state: z.enum(["disabled", "starting", "polling", "error"]), + botUsername: z.string().optional(), + error: z.string().optional(), +}); + +export const ChannelsStatus = z.object({ + whatsapp: WhatsAppChannelStatus, + telegram: TelegramChannelStatus, +}); diff --git a/apps/x/packages/shared/src/index.ts b/apps/x/packages/shared/src/index.ts index 07bc23a9..6d5f5e59 100644 --- a/apps/x/packages/shared/src/index.ts +++ b/apps/x/packages/shared/src/index.ts @@ -19,4 +19,5 @@ export * as browserControl from './browser-control.js'; export * as billing from './billing.js'; export * as notificationSettings from './notification-settings.js'; export * as codeSessions from './code-sessions.js'; +export * as channels from './channels.js'; export { PrefixLogger }; diff --git a/apps/x/packages/shared/src/ipc.ts b/apps/x/packages/shared/src/ipc.ts index 6010b840..a91896a7 100644 --- a/apps/x/packages/shared/src/ipc.ts +++ b/apps/x/packages/shared/src/ipc.ts @@ -24,6 +24,7 @@ import { EmailBlockSchema, GmailThreadSchema } from './blocks.js'; import { PermissionDecision, ApprovalPolicy, CodingAgent } from './code-mode.js'; import { NotificationSettingsSchema } from './notification-settings.js'; import { CodeProject, CodeSession, CodeSessionMode, CodeSessionStatus, GitRepoInfo, GitStatusFile, CodeAgentModelOptions } from './code-sessions.js'; +import { ChannelsConfig, ChannelsStatus } from './channels.js'; // ============================================================================ // Runtime Validation Schemas (Single Source of Truth) @@ -950,6 +951,28 @@ const ipcSchemas = { success: z.literal(true), }), }, + // ── Mobile channels (WhatsApp / Telegram bridge) ───────────── + 'channels:getConfig': { + req: z.null(), + res: ChannelsConfig, + }, + 'channels:setConfig': { + req: ChannelsConfig, + res: z.object({ success: z.literal(true) }), + }, + 'channels:getStatus': { + req: z.null(), + res: ChannelsStatus, + }, + 'channels:whatsappLogout': { + req: z.null(), + res: z.object({ success: z.literal(true) }), + }, + // Push: main → renderer status updates (QR rotation, connect/disconnect). + 'channels:status': { + req: ChannelsStatus, + res: z.null(), + }, 'slack:getConfig': { req: z.null(), res: z.object({ diff --git a/apps/x/pnpm-lock.yaml b/apps/x/pnpm-lock.yaml index cc6d0b32..d3c69af4 100644 --- a/apps/x/pnpm-lock.yaml +++ b/apps/x/pnpm-lock.yaml @@ -453,7 +453,7 @@ importers: version: 2.0.1 '@composio/core': specifier: ^0.6.0 - version: 0.6.2(zod@4.2.1) + version: 0.6.2(ws@8.21.0)(zod@4.2.1) '@google-cloud/local-auth': specifier: ^3.0.1 version: 3.0.1(encoding@0.1.13) @@ -478,6 +478,9 @@ importers: awilix: specifier: ^12.0.5 version: 12.0.5 + baileys: + specifier: 7.0.0-rc13 + version: 7.0.0-rc13(sharp@0.35.3(@types/node@25.0.3)) chokidar: specifier: ^4.0.3 version: 4.0.3 @@ -523,6 +526,9 @@ importers: posthog-node: specifier: ^4.18.0 version: 4.18.0 + qrcode: + specifier: ^1.5.4 + version: 1.5.4 react: specifier: ^19.2.3 version: 19.2.3 @@ -551,6 +557,9 @@ importers: '@types/pdf-parse': specifier: ^1.1.5 version: 1.1.5 + '@types/qrcode': + specifier: ^1.5.6 + version: 1.5.6 vitest: specifier: 'catalog:' version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(jsdom@29.1.1)(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.8.2)) @@ -971,6 +980,9 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@borewit/text-codec@0.2.2': + resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} + '@braintree/sanitize-url@7.1.1': resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} @@ -978,6 +990,16 @@ packages: resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true + '@cacheable/memory@2.2.0': + resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} + + '@cacheable/node-cache@1.7.6': + resolution: {integrity: sha512-6Omk2SgNnjtxB5f/E6bTIWIt5xhdpx39fGNRQgU9lojvRxU68v+qY+SXXLsp3ZGukqoPjsK21wZ6XABFr/Ge3A==} + engines: {node: '>=18'} + + '@cacheable/utils@2.5.0': + resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==} + '@chevrotain/cst-dts-gen@12.0.0': resolution: {integrity: sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==} @@ -1171,6 +1193,7 @@ packages: '@eigenpal/docx-editor-react@1.0.3': resolution: {integrity: sha512-KupDVHo6KC4KUs48bM1pMYFFbDJqkW8XyIhgsnLx+BWk2yOPU4bx2HfWB6H+JEVROA1h1AmhTAyE39gk75wg5w==} + deprecated: deprecated peerDependencies: prosemirror-commands: ^1.5.2 prosemirror-dropcursor: ^1.8.2 @@ -1321,6 +1344,9 @@ packages: engines: {node: '>=14.14'} hasBin: true + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + '@esbuild/aix-ppc64@0.24.2': resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} engines: {node: '>=18'} @@ -1700,12 +1726,18 @@ packages: resolution: {integrity: sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==} engines: {node: '>=14.0.0'} + '@hapi/boom@9.1.4': + resolution: {integrity: sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==} + '@hapi/formula@3.0.2': resolution: {integrity: sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==} '@hapi/hoek@11.0.7': resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==} + '@hapi/hoek@9.3.0': + resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} + '@hapi/pinpoint@2.0.1': resolution: {integrity: sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==} @@ -1744,6 +1776,152 @@ packages: '@iconify/utils@3.1.0': resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.3': + resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.3': + resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.3': + resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.2': + resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.2': + resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.2': + resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.3.2': + resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.3.2': + resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.3.2': + resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.3.2': + resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.3.2': + resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.35.3': + resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.35.3': + resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.35.3': + resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.35.3': + resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.35.3': + resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.35.3': + resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.35.3': + resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.35.3': + resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.35.3': + resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.3': + resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.3': + resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.3': + resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.3': + resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + '@inquirer/checkbox@3.0.1': resolution: {integrity: sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==} engines: {node: '>=18'} @@ -1835,6 +2013,15 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + '@lezer/common@1.5.2': resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} @@ -2231,6 +2418,9 @@ packages: resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==} engines: {node: '>=14'} + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -2250,12 +2440,21 @@ packages: '@protobufjs/codegen@2.0.4': resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + '@protobufjs/eventemitter@1.1.0': resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + '@protobufjs/fetch@1.1.0': resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + '@protobufjs/float@1.0.2': resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} @@ -2271,6 +2470,9 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@protobufjs/utf8@1.1.1': + resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==} + '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -3768,6 +3970,13 @@ packages: '@tiptap/starter-kit@3.22.4': resolution: {integrity: sha512-qWjw+vfdin1rzMRpRU4cC5tLTwMJtUpXeQukv+6mOqqvhptuwuZBjUHImVEJaSPoHXS7+1ut+nTnrLyWyEuE5Q==} + '@tokenizer/inflate@0.4.1': + resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} + engines: {node: '>=18'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@tokenlens/core@1.3.0': resolution: {integrity: sha512-d8YNHNC+q10bVpi95fELJwJyPVf1HfvBEI18eFQxRSZTdByXrP+f/ZtlhSzkx0Jl0aEmYVeBA5tPeeYRioLViQ==} @@ -4003,6 +4212,9 @@ packages: '@types/pdf-parse@1.1.5': resolution: {integrity: sha512-kBfrSXsloMnUJOKi25s3+hRmkycHfLK6A09eRGqF/N8BkQoPUmaCr+q8Cli5FnfohEz/rsv82zAiPz/LXtOGhA==} + '@types/qrcode@1.5.6': + resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} + '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -4380,6 +4592,9 @@ packages: async-lock@1.4.1: resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + async@1.5.2: resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==} @@ -4390,6 +4605,10 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + author-regex@1.0.0: resolution: {integrity: sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g==} engines: {node: '>=0.8'} @@ -4411,6 +4630,22 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + baileys@7.0.0-rc13: + resolution: {integrity: sha512-v8k74K8B5R7WNYGa26MyJAYEu3Wc4BSuK01QaK8lr30lhE8Nga31nWNu8KN0NDDt+Fsvkq4SQFFI8Q13ghjKmA==} + engines: {node: '>=20.0.0'} + peerDependencies: + audio-decode: ^2.1.3 + jimp: ^1.6.1 + link-preview-js: ^3.0.0 + sharp: '*' + peerDependenciesMeta: + audio-decode: + optional: true + jimp: + optional: true + link-preview-js: + optional: true + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -4528,6 +4763,9 @@ packages: resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} engines: {node: '>=8'} + cacheable@2.5.0: + resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -4547,6 +4785,10 @@ packages: camel-case@4.1.2: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + camelize@1.0.1: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} @@ -4638,6 +4880,9 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -4746,6 +4991,10 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -4820,6 +5069,9 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + curve25519-js@0.0.4: + resolution: {integrity: sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==} + cytoscape-cose-bilkent@4.1.0: resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} peerDependencies: @@ -5004,6 +5256,10 @@ packages: supports-color: optional: true + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} @@ -5088,6 +5344,9 @@ packages: resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + dingbat-to-unicode@1.0.1: resolution: {integrity: sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==} @@ -5392,6 +5651,9 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -5490,6 +5752,10 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + file-type@21.3.4: + resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==} + engines: {node: '>=20'} + filename-reserved-regex@2.0.0: resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==} engines: {node: '>=4'} @@ -5510,6 +5776,10 @@ packages: resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} engines: {node: '>=4'} + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -5820,6 +6090,10 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + hashery@1.5.1: + resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} + engines: {node: '>=20'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -5881,6 +6155,12 @@ packages: resolution: {integrity: sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w==} engines: {node: '>=16.9.0'} + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} + + hookified@2.2.0: + resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} @@ -6298,6 +6578,9 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + khroma@2.1.0: resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} @@ -6315,6 +6598,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + libsignal@6.0.0: + resolution: {integrity: sha512-d/5V3YFtDljbFMufz4ncyUYGYhJl+vzAe+c2EFFBQ6bz1h8Q3IOMEGXYMzlibU60I+e8GagMMpji18iez3P1hA==} + lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} @@ -6413,6 +6699,10 @@ packages: resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} engines: {node: '>=4'} + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -6601,6 +6891,10 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + media-typer@2.0.0: + resolution: {integrity: sha512-kOy3OxT2HH39N70UnKgu4NWDZjLOz8W/mfyvniHjRH/DrL3f2pOfvWQ4p60offbbtDAnXWp0v9LfMIqMec269Q==} + engines: {node: '>=18'} + mem@4.3.0: resolution: {integrity: sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==} engines: {node: '>=6'} @@ -6872,6 +7166,10 @@ packages: murmur-32@0.2.0: resolution: {integrity: sha512-ZkcWZudylwF+ir3Ld1n7gL6bI2mQAzXvSobPwVtu8aYi2sbXeipeSkdcanRLzIofLcM5F53lGaKm2dk7orBi7Q==} + music-metadata@11.13.0: + resolution: {integrity: sha512-uXRaov9dfjSpQufXIU7sMxVZnh+FilCQv2mXn+K5EJ/decP3dTWrgvPYa5r6MtRbieNSCE708Da4J0u1UGfQIw==} + engines: {node: '>=18'} + mute-stream@1.0.0: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -7003,6 +7301,10 @@ packages: peerDependencies: zod: ^4.0.16 + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -7081,6 +7383,10 @@ packages: resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} engines: {node: '>=4'} + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -7089,6 +7395,10 @@ packages: resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} engines: {node: '>=4'} + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -7101,6 +7411,10 @@ packages: resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} engines: {node: '>=8'} + p-queue@9.3.0: + resolution: {integrity: sha512-7NED7xhQ74Ngp4JP/2e0VZHp7vSWfJfqeiR92jPgxsz6m0Se4P03YoTKa9dDXyZ3r6P616gUXttrB6nnHYKang==} + engines: {node: '>=20'} + p-retry@4.6.2: resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} engines: {node: '>=8'} @@ -7109,10 +7423,18 @@ packages: resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} engines: {node: '>=8'} + p-timeout@7.0.1: + resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} + engines: {node: '>=20'} + p-try@1.0.0: resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} engines: {node: '>=4'} + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -7244,6 +7566,16 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@9.14.0: + resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} + hasBin: true + pizzip@3.2.0: resolution: {integrity: sha512-X4NPNICxCfIK8VYhF6wbksn81vTiziyLbvKuORVAmolvnUzl1A1xmz9DAWKxPRq9lZg84pJOOAMq3OE61bD8IQ==} @@ -7258,6 +7590,10 @@ packages: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} engines: {node: '>=10.4.0'} + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + points-on-curve@0.2.0: resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} @@ -7314,6 +7650,9 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -7383,6 +7722,10 @@ packages: resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} engines: {node: '>=12.0.0'} + protobufjs@7.6.4: + resolution: {integrity: sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==} + engines: {node: '>=12.0.0'} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -7411,6 +7754,15 @@ packages: pusher-js@8.4.0: resolution: {integrity: sha512-wp3HqIIUc1GRyu1XrP6m2dgyE9MoCsXVsWNlohj0rjSkLf+a0jLvEyVubdg58oMk7bhjBWnFClgp8jfAa6Ak4Q==} + qified@0.10.1: + resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} + engines: {node: '>=20'} + + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + qs@6.14.1: resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} engines: {node: '>=0.6'} @@ -7424,6 +7776,9 @@ packages: queue@6.0.2: resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + quick-lru@5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} @@ -7549,6 +7904,10 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + recharts@3.8.1: resolution: {integrity: sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg==} engines: {node: '>=18'} @@ -7644,6 +8003,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + resedit@2.0.3: resolution: {integrity: sha512-oTeemxwoMuxxTYxXUwjkrOPfngTQehlv0/HoYFNkB4uzsP1Un1A9nI8JQKGOFkxpqkC7qkMs0lUsGrvUlbLNUA==} engines: {node: '>=14', npm: '>=7'} @@ -7751,6 +8113,10 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -7788,6 +8154,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -7806,6 +8177,9 @@ packages: server-destroy@1.0.1: resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -7821,6 +8195,15 @@ packages: engines: {node: '>= 0.10'} hasBin: true + sharp@0.35.3: + resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -7899,6 +8282,9 @@ packages: resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + sonner@2.0.7: resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} peerDependencies: @@ -7931,6 +8317,10 @@ packages: spdx-license-ids@3.0.22: resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -8021,6 +8411,10 @@ packages: strnum@2.1.2: resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} + strtok3@10.3.5: + resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==} + engines: {node: '>=18'} + style-mod@4.1.3: resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} @@ -8100,6 +8494,9 @@ packages: engines: {node: '>=10'} hasBin: true + thread-stream@3.2.0: + resolution: {integrity: sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==} + tiny-each-async@2.0.3: resolution: {integrity: sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA==} @@ -8166,6 +8563,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + token-types@6.1.2: + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} + engines: {node: '>=14.16'} + tokenlens@1.3.1: resolution: {integrity: sha512-7oxmsS5PNCX3z+b+z07hL5vCzlgHKkCGrEQjQmWl5l+v5cUrtL7S1cuST4XThaL1XyjbTX8J5hfP0cjDJRkaLA==} @@ -8270,6 +8671,10 @@ packages: ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + underscore@1.13.8: resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} @@ -8586,6 +8991,9 @@ packages: webpack-cli: optional: true + whatsapp-rust-bridge@0.5.4: + resolution: {integrity: sha512-yYO1qSs0Fe7tGtnxOFHomocUD6IZtoAgmA4oDFyGIRZ67D3QZk3w7swA6XXFXNQngiyrg2k7tul6IrM3eUFh7A==} + whatwg-mimetype@5.0.0: resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} engines: {node: '>=20'} @@ -8597,6 +9005,9 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + which-typed-array@1.1.20: resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} engines: {node: '>= 0.4'} @@ -8615,6 +9026,9 @@ packages: engines: {node: '>=8'} hasBin: true + win-guid@0.2.1: + resolution: {integrity: sha512-gEIQU4mkgl2OPeoNrWflcJFJ3Ae2BPd4eCsHHA/XikslkIVms/nHhvnvzIZV7VLmBvtFlDOzLt9rrZT+n6D67A==} + wmf@1.0.2: resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} engines: {node: '>=0.8'} @@ -8642,6 +9056,18 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + wsl-utils@0.3.1: resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} engines: {node: '>=20'} @@ -8684,6 +9110,9 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -8699,6 +9128,10 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -8707,6 +9140,10 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + yargs@16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} @@ -9517,12 +9954,32 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@borewit/text-codec@0.2.2': {} + '@braintree/sanitize-url@7.1.1': {} '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 + '@cacheable/memory@2.2.0': + dependencies: + '@cacheable/utils': 2.5.0 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/node-cache@1.7.6': + dependencies: + cacheable: 2.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/utils@2.5.0': + dependencies: + hashery: 1.5.1 + keyv: 5.6.0 + '@chevrotain/cst-dts-gen@12.0.0': dependencies: '@chevrotain/gast': 12.0.0 @@ -9799,13 +10256,13 @@ snapshots: '@composio/client@0.1.0-alpha.56': {} - '@composio/core@0.6.2(zod@4.2.1)': + '@composio/core@0.6.2(ws@8.21.0)(zod@4.2.1)': dependencies: '@composio/client': 0.1.0-alpha.56 '@composio/json-schema-to-zod': 0.1.20(zod@4.2.1) '@types/json-schema': 7.0.15 chalk: 4.1.2 - openai: 6.17.0(zod@4.2.1) + openai: 6.17.0(ws@8.21.0)(zod@4.2.1) pusher-js: 8.4.0 semver: 7.7.3 zod: 4.2.1 @@ -10208,7 +10665,7 @@ snapshots: make-fetch-happen: 10.2.1 nopt: 6.0.0 proc-log: 2.0.1 - semver: 7.7.3 + semver: 7.8.5 tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: @@ -10302,6 +10759,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@emnapi/runtime@1.11.2': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.24.2': optional: true @@ -10536,10 +10998,16 @@ snapshots: dependencies: '@hapi/hoek': 11.0.7 + '@hapi/boom@9.1.4': + dependencies: + '@hapi/hoek': 9.3.0 + '@hapi/formula@3.0.2': {} '@hapi/hoek@11.0.7': {} + '@hapi/hoek@9.3.0': {} + '@hapi/pinpoint@2.0.1': {} '@hapi/tlds@1.1.4': {} @@ -10571,6 +11039,112 @@ snapshots: '@iconify/types': 2.0.0 mlly: 1.8.0 + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.2 + optional: true + + '@img/sharp-darwin-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.2 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.2': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.2': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + optional: true + + '@img/sharp-linux-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.2 + optional: true + + '@img/sharp-linux-arm@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.2 + optional: true + + '@img/sharp-linux-ppc64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.2 + optional: true + + '@img/sharp-linux-riscv64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.2 + optional: true + + '@img/sharp-linux-s390x@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.2 + optional: true + + '@img/sharp-linux-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + optional: true + + '@img/sharp-wasm32@0.35.3': + dependencies: + '@emnapi/runtime': 1.11.2 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-win32-arm64@0.35.3': + optional: true + + '@img/sharp-win32-ia32@0.35.3': + optional: true + + '@img/sharp-win32-x64@0.35.3': + optional: true + '@inquirer/checkbox@3.0.1': dependencies: '@inquirer/core': 9.2.1 @@ -10710,6 +11284,14 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.5.1 + hookified: 1.15.1 + keyv: 5.6.0 + + '@keyv/serialize@1.1.1': {} + '@lezer/common@1.5.2': {} '@lezer/cpp@1.1.6': @@ -10905,7 +11487,7 @@ snapshots: '@npmcli/fs@2.1.2': dependencies: '@gar/promisify': 1.1.3 - semver: 7.7.3 + semver: 7.8.5 '@npmcli/move-file@2.0.1': dependencies: @@ -11136,6 +11718,8 @@ snapshots: '@opentelemetry/semantic-conventions@1.39.0': {} + '@pinojs/redact@0.4.0': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -11151,13 +11735,21 @@ snapshots: '@protobufjs/codegen@2.0.4': {} + '@protobufjs/codegen@2.0.5': {} + '@protobufjs/eventemitter@1.1.0': {} + '@protobufjs/eventemitter@1.1.1': {} + '@protobufjs/fetch@1.1.0': dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/inquire': 1.1.0 + '@protobufjs/fetch@1.1.1': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/float@1.0.2': {} '@protobufjs/inquire@1.1.0': {} @@ -11168,6 +11760,8 @@ snapshots: '@protobufjs/utf8@1.1.0': {} + '@protobufjs/utf8@1.1.1': {} + '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.1.3': {} @@ -12860,6 +13454,15 @@ snapshots: '@tiptap/extensions': 3.22.5(@tiptap/core@3.22.4(@tiptap/pm@3.22.4))(@tiptap/pm@3.22.4) '@tiptap/pm': 3.22.4 + '@tokenizer/inflate@0.4.1': + dependencies: + debug: 4.4.3 + token-types: 6.1.2 + transitivePeerDependencies: + - supports-color + + '@tokenizer/token@0.3.0': {} + '@tokenlens/core@1.3.0': {} '@tokenlens/fetch@1.3.0': @@ -13155,6 +13758,10 @@ snapshots: dependencies: '@types/node': 25.0.3 + '@types/qrcode@1.5.6': + dependencies: + '@types/node': 25.0.3 + '@types/qs@6.14.0': {} '@types/range-parser@1.2.7': {} @@ -13606,6 +14213,10 @@ snapshots: async-lock@1.4.1: {} + async-mutex@0.5.0: + dependencies: + tslib: 2.8.1 + async@1.5.2: optional: true @@ -13613,6 +14224,8 @@ snapshots: at-least-node@1.0.0: {} + atomic-sleep@1.0.0: {} + author-regex@1.0.0: {} available-typed-arrays@1.0.7: @@ -13644,6 +14257,25 @@ snapshots: bail@2.0.2: {} + baileys@7.0.0-rc13(sharp@0.35.3(@types/node@25.0.3)): + dependencies: + '@cacheable/node-cache': 1.7.6 + '@hapi/boom': 9.1.4 + async-mutex: 0.5.0 + libsignal: 6.0.0 + lru-cache: 11.5.1 + music-metadata: 11.13.0 + p-queue: 9.3.0 + pino: 9.14.0 + protobufjs: 7.6.4 + sharp: 0.35.3(@types/node@25.0.3) + whatsapp-rust-bridge: 0.5.4 + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + balanced-match@1.0.2: {} base32-encode@1.2.0: @@ -13796,6 +14428,14 @@ snapshots: normalize-url: 6.1.0 responselike: 2.0.1 + cacheable@2.5.0: + dependencies: + '@cacheable/memory': 2.2.0 + '@cacheable/utils': 2.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.10.1 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -13820,6 +14460,8 @@ snapshots: pascal-case: 3.1.2 tslib: 2.8.1 + camelcase@5.3.1: {} + camelize@1.0.1: {} caniuse-lite@1.0.30001761: {} @@ -13896,6 +14538,12 @@ snapshots: cli-width@4.1.0: {} + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + cliui@7.0.4: dependencies: string-width: 4.2.3 @@ -13998,6 +14646,8 @@ snapshots: content-type@1.0.5: {} + content-type@2.0.0: {} + convert-source-map@2.0.0: {} cookie-signature@1.2.2: {} @@ -14068,6 +14718,8 @@ snapshots: csstype@3.2.3: {} + curve25519-js@0.0.4: {} + cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.1): dependencies: cose-base: 1.0.3 @@ -14271,6 +14923,8 @@ snapshots: dependencies: ms: 2.1.3 + decamelize@1.2.0: {} + decimal.js-light@2.5.1: {} decimal.js@10.6.0: {} @@ -14342,6 +14996,8 @@ snapshots: diff@8.0.4: {} + dijkstrajs@1.0.3: {} + dingbat-to-unicode@1.0.1: {} dir-compare@4.2.0: @@ -14761,6 +15417,8 @@ snapshots: eventemitter3@5.0.1: {} + eventemitter3@5.0.4: {} + events@3.3.0: {} eventsource-parser@3.0.6: {} @@ -14885,6 +15543,15 @@ snapshots: dependencies: flat-cache: 4.0.1 + file-type@21.3.4: + dependencies: + '@tokenizer/inflate': 0.4.1 + strtok3: 10.3.5 + token-types: 6.1.2 + uint8array-extras: 1.5.0 + transitivePeerDependencies: + - supports-color + filename-reserved-regex@2.0.0: {} filenamify@4.3.0: @@ -14912,6 +15579,11 @@ snapshots: dependencies: locate-path: 2.0.0 + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -15313,6 +15985,10 @@ snapshots: dependencies: has-symbols: 1.1.0 + hashery@1.5.1: + dependencies: + hookified: 1.15.1 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -15449,6 +16125,10 @@ snapshots: hono@4.11.3: {} + hookified@1.15.1: {} + + hookified@2.2.0: {} + hosted-git-info@2.8.9: {} hsl-to-hex@1.0.0: @@ -15874,6 +16554,10 @@ snapshots: dependencies: json-buffer: 3.0.1 + keyv@5.6.0: + dependencies: + '@keyv/serialize': 1.1.1 + khroma@2.1.0: {} langium@4.2.2: @@ -15894,6 +16578,11 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + libsignal@6.0.0: + dependencies: + curve25519-js: 0.0.4 + protobufjs: 7.6.4 + lie@3.3.0: dependencies: immediate: 3.0.6 @@ -15981,6 +16670,10 @@ snapshots: p-locate: 2.0.0 path-exists: 3.0.0 + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -16298,6 +16991,8 @@ snapshots: media-typer@1.1.0: {} + media-typer@2.0.0: {} + mem@4.3.0: dependencies: map-age-cleaner: 0.1.3 @@ -16694,6 +17389,21 @@ snapshots: imul: 1.0.1 optional: true + music-metadata@11.13.0: + dependencies: + '@borewit/text-codec': 0.2.2 + '@tokenizer/token': 0.3.0 + content-type: 2.0.0 + debug: 4.4.3 + file-type: 21.3.4 + media-typer: 2.0.0 + strtok3: 10.3.5 + token-types: 6.1.2 + uint8array-extras: 1.5.0 + win-guid: 0.2.1 + transitivePeerDependencies: + - supports-color + mute-stream@1.0.0: {} nan@2.24.0: @@ -16808,6 +17518,8 @@ snapshots: '@ai-sdk/provider-utils': 3.0.20(zod@4.2.1) zod: 4.2.1 + on-exit-leak-free@2.1.2: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -16842,8 +17554,9 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@6.17.0(zod@4.2.1): + openai@6.17.0(ws@8.21.0)(zod@4.2.1): optionalDependencies: + ws: 8.21.0 zod: 4.2.1 openid-client@6.8.1: @@ -16890,6 +17603,10 @@ snapshots: dependencies: p-try: 1.0.0 + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -16898,6 +17615,10 @@ snapshots: dependencies: p-limit: 1.3.0 + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + p-locate@5.0.0: dependencies: p-limit: 3.1.0 @@ -16911,6 +17632,11 @@ snapshots: eventemitter3: 4.0.7 p-timeout: 3.2.0 + p-queue@9.3.0: + dependencies: + eventemitter3: 5.0.4 + p-timeout: 7.0.1 + p-retry@4.6.2: dependencies: '@types/retry': 0.12.0 @@ -16920,8 +17646,12 @@ snapshots: dependencies: p-finally: 1.0.0 + p-timeout@7.0.1: {} + p-try@1.0.0: {} + p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} package-manager-detector@1.6.0: {} @@ -17033,6 +17763,26 @@ snapshots: pify@4.0.1: {} + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-std-serializers@7.1.0: {} + + pino@9.14.0: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.1 + thread-stream: 3.2.0 + pizzip@3.2.0: dependencies: pako: 2.1.0 @@ -17051,6 +17801,8 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 15.1.1 + pngjs@5.0.0: {} + points-on-curve@0.2.0: {} points-on-path@0.2.1: @@ -17112,6 +17864,8 @@ snapshots: process-nextick-args@2.0.1: {} + process-warning@5.0.0: {} + process@0.11.10: {} progress@2.0.3: {} @@ -17221,6 +17975,20 @@ snapshots: '@types/node': 25.0.3 long: 5.3.2 + protobufjs@7.6.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 + '@protobufjs/float': 1.0.2 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.1 + '@types/node': 25.0.3 + long: 5.3.2 + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -17245,6 +18013,16 @@ snapshots: dependencies: tweetnacl: 1.0.3 + qified@0.10.1: + dependencies: + hookified: 2.2.0 + + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + qs@6.14.1: dependencies: side-channel: 1.1.0 @@ -17257,6 +18035,8 @@ snapshots: dependencies: inherits: 2.0.4 + quick-format-unescaped@4.0.4: {} + quick-lru@5.1.1: {} radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): @@ -17441,6 +18221,8 @@ snapshots: readdirp@4.1.2: {} + real-require@0.2.0: {} + recharts@3.8.1(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react-is@17.0.2)(react@19.2.3)(redux@5.0.1): dependencies: '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.3)(redux@5.0.1))(react@19.2.3) @@ -17584,6 +18366,8 @@ snapshots: require-from-string@2.0.2: {} + require-main-filename@2.0.0: {} + resedit@2.0.3: dependencies: pe-library: 1.0.1 @@ -17718,6 +18502,8 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + safe-stable-stringify@2.5.0: {} + safer-buffer@2.1.2: {} sax@1.6.0: {} @@ -17746,6 +18532,8 @@ snapshots: semver@7.7.3: {} + semver@7.8.5: {} + send@1.2.1: dependencies: debug: 4.4.3 @@ -17782,6 +18570,8 @@ snapshots: server-destroy@1.0.1: {} + set-blocking@2.0.0: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -17801,6 +18591,39 @@ snapshots: safe-buffer: 5.2.1 to-buffer: 1.2.2 + sharp@0.35.3(@types/node@25.0.3): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.3 + '@img/sharp-darwin-x64': 0.35.3 + '@img/sharp-freebsd-wasm32': 0.35.3 + '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-linux-arm': 0.35.3 + '@img/sharp-linux-arm64': 0.35.3 + '@img/sharp-linux-ppc64': 0.35.3 + '@img/sharp-linux-riscv64': 0.35.3 + '@img/sharp-linux-s390x': 0.35.3 + '@img/sharp-linux-x64': 0.35.3 + '@img/sharp-linuxmusl-arm64': 0.35.3 + '@img/sharp-linuxmusl-x64': 0.35.3 + '@img/sharp-webcontainers-wasm32': 0.35.3 + '@img/sharp-win32-arm64': 0.35.3 + '@img/sharp-win32-ia32': 0.35.3 + '@img/sharp-win32-x64': 0.35.3 + '@types/node': 25.0.3 + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 @@ -17896,6 +18719,10 @@ snapshots: ip-address: 10.1.0 smart-buffer: 4.2.0 + sonic-boom@4.2.1: + dependencies: + atomic-sleep: 1.0.0 + sonner@2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: react: 19.2.3 @@ -17926,6 +18753,8 @@ snapshots: spdx-license-ids@3.0.22: {} + split2@4.2.0: {} + sprintf-js@1.0.3: {} sprintf-js@1.1.3: @@ -18040,6 +18869,10 @@ snapshots: strnum@2.1.2: {} + strtok3@10.3.5: + dependencies: + '@tokenizer/token': 0.3.0 + style-mod@4.1.3: {} style-to-js@1.1.21: @@ -18117,6 +18950,10 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + thread-stream@3.2.0: + dependencies: + real-require: 0.2.0 + tiny-each-async@2.0.3: optional: true @@ -18181,6 +19018,12 @@ snapshots: toidentifier@1.0.1: {} + token-types@6.1.2: + dependencies: + '@borewit/text-codec': 0.2.2 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + tokenlens@1.3.1: dependencies: '@tokenlens/core': 1.3.0 @@ -18270,6 +19113,8 @@ snapshots: ufo@1.6.1: {} + uint8array-extras@1.5.0: {} + underscore@1.13.8: {} undici-types@6.21.0: {} @@ -18637,6 +19482,8 @@ snapshots: - esbuild - uglify-js + whatsapp-rust-bridge@0.5.4: {} + whatwg-mimetype@5.0.0: {} whatwg-url@16.0.1: @@ -18652,6 +19499,8 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + which-module@2.0.1: {} + which-typed-array@1.1.20: dependencies: available-typed-arrays: 1.0.7 @@ -18675,6 +19524,8 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + win-guid@0.2.1: {} + wmf@1.0.2: {} word-wrap@1.2.5: {} @@ -18701,6 +19552,8 @@ snapshots: wrappy@1.0.2: {} + ws@8.21.0: {} + wsl-utils@0.3.1: dependencies: is-wsl: 3.1.1 @@ -18740,6 +19593,8 @@ snapshots: xtend@4.0.2: {} + y18n@4.0.3: {} + y18n@5.0.8: {} yallist@3.1.1: {} @@ -18748,11 +19603,30 @@ snapshots: yaml@2.8.2: {} + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + yargs-parser@20.2.9: optional: true yargs-parser@21.1.1: {} + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + yargs@16.2.0: dependencies: cliui: 7.0.4