feat(broker): add claude-powered telegram bot with tool calling
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-13 20:40:16 +01:00
parent 4561076904
commit f4881b21b0
5 changed files with 616 additions and 36 deletions

View File

@@ -15,6 +15,7 @@
},
"prettier": "@turbostarter/prettier-config",
"dependencies": {
"@anthropic-ai/sdk": "0.71.2",
"@qdrant/js-client-rest": "1.17.0",
"@turbostarter/db": "workspace:*",
"@turbostarter/shared": "workspace:*",

View File

@@ -34,6 +34,7 @@ const envSchema = z.object({
CLI_SYNC_SECRET: z.string().default(""), // HS256 shared secret for dashboard→broker sync JWTs. Required for /cli-sync.
MAX_SERVICES_PER_MESH: z.coerce.number().int().positive().default(20),
MAX_SERVICE_ZIP_BYTES: z.coerce.number().int().positive().default(50 * 1024 * 1024),
ANTHROPIC_API_KEY: z.string().default(""), // Claude API key for Telegram AI bot
NODE_ENV: z
.enum(["development", "production", "test"])
.default("development"),

View File

@@ -0,0 +1,291 @@
/**
* Claude-powered natural language processing for Telegram mesh interactions.
*
* Uses Claude Haiku 4.5 with tool calling to interpret user intent
* and map to mesh operations. Destructive/social actions require
* confirmation via Telegram inline buttons.
*/
import Anthropic from "@anthropic-ai/sdk";
import { env } from "./env";
import { log } from "./logger";
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export interface AiTool {
name: string;
description: string;
input_schema: Record<string, unknown>;
}
export interface AiToolCall {
name: string;
input: Record<string, unknown>;
}
export interface AiResult {
type: "text" | "tool_call" | "error";
text?: string;
toolCall?: AiToolCall;
requiresConfirmation?: boolean;
}
// ---------------------------------------------------------------------------
// Tools definition
// ---------------------------------------------------------------------------
const TOOLS: AiTool[] = [
{
name: "send_message",
description: "Send a message to a peer in the mesh. Use when the user wants to tell, ask, or communicate something to a specific person or group.",
input_schema: {
type: "object",
properties: {
to: { type: "string", description: "Peer name, @group, or * for broadcast" },
message: { type: "string", description: "The message content" },
priority: { type: "string", enum: ["now", "next", "low"], description: "Delivery priority (default: next)" },
},
required: ["to", "message"],
},
},
{
name: "list_peers",
description: "List all connected peers in the mesh. Use when user asks who's online, who's available, or what everyone is doing.",
input_schema: {
type: "object",
properties: {},
},
},
{
name: "remember",
description: "Store a memory/note in the mesh's shared knowledge. Use when user wants to save information for later.",
input_schema: {
type: "object",
properties: {
content: { type: "string", description: "The content to remember" },
tags: { type: "array", items: { type: "string" }, description: "Tags for categorization" },
},
required: ["content"],
},
},
{
name: "recall",
description: "Search the mesh's shared memory. Use when user asks about something that was previously stored.",
input_schema: {
type: "object",
properties: {
query: { type: "string", description: "Search query" },
},
required: ["query"],
},
},
{
name: "get_state",
description: "Read a shared state value. Use when user asks about a specific key/variable.",
input_schema: {
type: "object",
properties: {
key: { type: "string", description: "State key to read" },
},
required: ["key"],
},
},
{
name: "set_state",
description: "Write a shared state value. Use when user wants to set/update a key.",
input_schema: {
type: "object",
properties: {
key: { type: "string", description: "State key" },
value: { type: "string", description: "Value to set" },
},
required: ["key", "value"],
},
},
{
name: "create_mesh",
description: "Create a new mesh. Use when user wants to create a new workspace/mesh.",
input_schema: {
type: "object",
properties: {
name: { type: "string", description: "Mesh name" },
},
required: ["name"],
},
},
{
name: "share_mesh",
description: "Generate an invite link or send an invite email. Use when user wants to invite someone to the mesh.",
input_schema: {
type: "object",
properties: {
email: { type: "string", description: "Email to invite (optional — if omitted, generates a link)" },
},
},
},
];
// Actions that need user confirmation before executing
const CONFIRM_ACTIONS = new Set([
"send_message",
"create_mesh",
"share_mesh",
"set_state",
"remember",
]);
const SYSTEM_PROMPT = `You are the claudemesh Telegram assistant. You help users interact with their claudemesh peer network using natural language.
You have access to tools for mesh operations. When the user's intent maps to a tool, use it. When it's a general question or conversation, respond directly.
Rules:
- Be concise — Telegram messages should be short
- When sending messages to peers, preserve the user's tone and intent
- For ambiguous peer names, ask for clarification
- Never fabricate peer names or data — use list_peers to find real names
- If unsure which mesh to target, ask the user`;
// ---------------------------------------------------------------------------
// AI Engine
// ---------------------------------------------------------------------------
let client: Anthropic | null = null;
function getClient(): Anthropic {
if (!client) {
const apiKey = env.ANTHROPIC_API_KEY;
if (!apiKey) throw new Error("ANTHROPIC_API_KEY not configured");
client = new Anthropic({ apiKey });
}
return client;
}
/**
* Process a natural language message through Claude and return the intent.
*/
export async function processMessage(
userMessage: string,
context: { meshSlug?: string; userName?: string; recentPeers?: string[] },
): Promise<AiResult> {
try {
const anthropic = getClient();
const contextInfo = [
context.meshSlug ? `Current mesh: ${context.meshSlug}` : null,
context.userName ? `User's name: ${context.userName}` : null,
context.recentPeers?.length ? `Known peers: ${context.recentPeers.join(", ")}` : null,
].filter(Boolean).join(". ");
const response = await anthropic.messages.create({
model: "claude-haiku-4-5-20251001",
max_tokens: 500,
system: SYSTEM_PROMPT + (contextInfo ? `\n\nContext: ${contextInfo}` : ""),
tools: TOOLS as Anthropic.Messages.Tool[],
messages: [{ role: "user", content: userMessage }],
});
// Check for tool use
for (const block of response.content) {
if (block.type === "tool_use") {
return {
type: "tool_call",
toolCall: { name: block.name, input: block.input as Record<string, unknown> },
requiresConfirmation: CONFIRM_ACTIONS.has(block.name),
};
}
if (block.type === "text") {
return { type: "text", text: block.text };
}
}
return { type: "text", text: "I'm not sure how to help with that." };
} catch (err) {
log.error("telegram-ai", { error: err instanceof Error ? err.message : String(err) });
return { type: "error", text: "AI processing failed. Try a /command instead." };
}
}
/**
* Format a tool call as a human-readable confirmation message for Telegram.
*/
export function formatConfirmation(toolCall: AiToolCall): string {
const { name, input } = toolCall;
switch (name) {
case "send_message":
return `📤 *Send message to ${escMd(String(input.to))}:*\n\n"${escMd(String(input.message))}"\n\nPriority: ${input.priority ?? "next"}`;
case "create_mesh":
return `🔧 *Create mesh:*\n\nName: ${escMd(String(input.name))}`;
case "share_mesh":
return input.email
? `📧 *Send invite to:*\n\n${escMd(String(input.email))}`
: `🔗 *Generate invite link*`;
case "set_state":
return `📝 *Set state:*\n\n\`${escMd(String(input.key))}\` = \`${escMd(String(input.value))}\``;
case "remember":
return `💾 *Remember:*\n\n"${escMd(String(input.content))}"${input.tags ? `\nTags: ${(input.tags as string[]).join(", ")}` : ""}`;
default:
return `⚙️ *${name}:*\n\n${JSON.stringify(input, null, 2)}`;
}
}
/**
* Format a tool result as a Telegram reply.
*/
export function formatResult(toolName: string, result: unknown): string {
switch (toolName) {
case "send_message":
return "✅ Message sent.";
case "list_peers": {
const peers = result as Array<{ displayName: string; status: string; summary?: string }>;
if (!peers || peers.length === 0) return "No peers online.";
return "👥 *Online peers:*\n\n" + peers.map(p => {
const icon = p.status === "idle" ? "🟢" : p.status === "working" ? "🟡" : p.status === "dnd" ? "🔴" : "⚪";
return `${icon} *${escMd(p.displayName)}*${p.summary ? `${escMd(p.summary)}` : ""}`;
}).join("\n");
}
case "recall": {
const memories = result as Array<{ content: string; tags: string[] }>;
if (!memories || memories.length === 0) return "No memories found.";
return "🧠 *Memories:*\n\n" + memories.map(m =>
`${escMd(m.content)}${m.tags.length ? ` _[${m.tags.join(", ")}]_` : ""}`
).join("\n");
}
case "get_state": {
const state = result as { key: string; value: unknown } | null;
if (!state) return "Key not found.";
return `📊 \`${escMd(state.key)}\` = \`${escMd(String(state.value))}\``;
}
case "remember":
return "💾 Remembered.";
case "set_state":
return "📝 State updated.";
case "create_mesh":
return "✅ Mesh created.";
case "share_mesh":
return typeof result === "string" ? `🔗 Invite: ${result}` : "✅ Invite sent.";
default:
return `✅ Done: ${JSON.stringify(result)}`;
}
}
function escMd(s: string): string {
return s.replace(/[_*[\]()~`>#+\-=|{}.!\\]/g, "\\$&");
}
export { CONFIRM_ACTIONS };

View File

@@ -535,6 +535,25 @@ const pendingVerifications = new Map<
// Conversation state: chatId → which input the bot is waiting for
const conversationState = new Map<number, "awaiting_email" | "awaiting_code">();
/** Pending AI actions awaiting user confirmation */
const pendingAiActions = new Map<string, {
chatId: number;
meshIds: string[];
toolCall: { name: string; input: Record<string, unknown> };
expiresAt: number;
}>();
/** Chat → mesh slugs mapping for AI context */
const chatMeshSlugs = new Map<number, string[]>();
// Clean expired AI actions every 5 min
setInterval(() => {
const now = Date.now();
for (const [k, v] of pendingAiActions) {
if (now > v.expiresAt) pendingAiActions.delete(k);
}
}, 5 * 60 * 1000);
/** Invite URL regex: https://claudemesh.com/join/<token> */
const INVITE_URL_RE =
/https?:\/\/(?:www\.)?claudemesh\.com\/join\/([A-Za-z0-9_\-\.]+)/;
@@ -1144,6 +1163,49 @@ function setupBotCommands(
const chatId = ctx.chat?.id;
if (!chatId) { await ctx.answerCallbackQuery(); return; }
// --- AI action confirmation ---
if (data.startsWith("ai_")) {
const [action, actionId] = data.split(":");
if (!actionId) { await ctx.answerCallbackQuery(); return; }
const pending = pendingAiActions.get(actionId);
if (!pending || pending.chatId !== chatId) {
await ctx.answerCallbackQuery({ text: "Expired. Send your message again." });
return;
}
if (action === "ai_cancel") {
pendingAiActions.delete(actionId);
await ctx.answerCallbackQuery({ text: "Cancelled" });
await ctx.editMessageText("❌ Cancelled.");
return;
}
if (action === "ai_edit") {
pendingAiActions.delete(actionId);
await ctx.answerCallbackQuery({ text: "Type your edited message" });
await ctx.editMessageText("✏️ Type your message again with corrections.");
return;
}
if (action === "ai_confirm") {
pendingAiActions.delete(actionId);
await ctx.answerCallbackQuery({ text: "Executing..." });
try {
const { formatResult } = await import("./telegram-ai");
const result = await executeAiToolCall(pending.toolCall, pending.meshIds);
await ctx.editMessageText(
formatResult(pending.toolCall.name, result),
{ parse_mode: "MarkdownV2" },
);
} catch (err) {
await ctx.editMessageText(`❌ Failed: ${err instanceof Error ? err.message : String(err)}`);
}
return;
}
}
// --- File recipient picker ---
if (data.startsWith("file:")) {
const pending = pendingFiles.get(chatId);
@@ -1523,24 +1585,140 @@ function setupBotCommands(
return;
}
// --- No mention → broadcast to all connected meshes ---
// --- No mention → process through Claude AI ---
try {
const { processMessage, formatConfirmation, formatResult, CONFIRM_ACTIONS } = await import("./telegram-ai");
// Gather context for the AI
const firstMeshId = meshIds[0]!;
const firstConn = meshConnections.get(firstMeshId);
const meshSlug = chatMeshSlugs.get(chatId)?.[0];
let recentPeers: string[] = [];
if (firstConn?.isConnected()) {
try {
const peers = await firstConn.listPeers();
recentPeers = peers.map(p => p.displayName);
} catch {}
}
const result = await processMessage(text, {
meshSlug,
userName: ctx.from?.first_name,
recentPeers,
});
if (result.type === "error") {
await ctx.reply(result.text ?? "Something went wrong.");
return;
}
if (result.type === "text") {
await ctx.reply(result.text ?? "");
return;
}
if (result.type === "tool_call" && result.toolCall) {
if (result.requiresConfirmation) {
// Store pending action and show confirmation buttons
const actionId = `ai_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
pendingAiActions.set(actionId, {
chatId,
meshIds,
toolCall: result.toolCall,
expiresAt: Date.now() + 5 * 60 * 1000,
});
const confirmText = formatConfirmation(result.toolCall);
await ctx.reply(confirmText, {
parse_mode: "MarkdownV2",
reply_markup: {
inline_keyboard: [
[
{ text: "✅ Confirm", callback_data: `ai_confirm:${actionId}` },
{ text: "✏️ Edit", callback_data: `ai_edit:${actionId}` },
{ text: "❌ Cancel", callback_data: `ai_cancel:${actionId}` },
],
],
},
});
} else {
// Read-only action — execute immediately
const execResult = await executeAiToolCall(result.toolCall, meshIds);
await ctx.reply(formatResult(result.toolCall.name, execResult), {
parse_mode: "MarkdownV2",
});
}
}
} catch (err) {
log.error("telegram-ai-handler", { error: err instanceof Error ? err.message : String(err) });
// Fallback: broadcast the text directly
let sent = 0;
for (const meshId of meshIds) {
const conn = meshConnections.get(meshId);
if (!conn?.isConnected()) continue;
const ok = await conn.sendMessage(
"*",
`[via Telegram] ${text}`,
"next",
);
const ok = await conn.sendMessage("*", `[via Telegram] ${text}`, "next");
if (ok) sent++;
}
if (sent === 0) {
await ctx.reply("❌ Not connected to any mesh.");
if (sent === 0) await ctx.reply("❌ Not connected.");
}
});
}
// ---------------------------------------------------------------------------
// AI tool call executor
// ---------------------------------------------------------------------------
async function executeAiToolCall(
toolCall: { name: string; input: Record<string, unknown> },
meshIds: string[],
): Promise<unknown> {
const firstMeshId = meshIds[0];
if (!firstMeshId) throw new Error("No mesh connected");
const conn = meshConnections.get(firstMeshId);
if (!conn?.isConnected()) throw new Error("Not connected to mesh");
switch (toolCall.name) {
case "send_message": {
const to = String(toolCall.input.to ?? "*");
const message = String(toolCall.input.message ?? "");
const priority = String(toolCall.input.priority ?? "next");
// Resolve peer name → pubkey
let targetSpec = to;
if (!to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/.test(to)) {
const peers = await conn.listPeers();
const match = peers.find(p => p.displayName.toLowerCase() === to.toLowerCase());
if (!match) {
const partials = peers.filter(p => p.displayName.toLowerCase().includes(to.toLowerCase()));
if (partials.length === 1) targetSpec = partials[0]!.pubkey;
else throw new Error(`Peer "${to}" not found`);
} else {
targetSpec = match.pubkey;
}
}
const ok = await conn.sendMessage(targetSpec, `[via Telegram] ${message}`, priority as "now" | "next" | "low");
if (!ok) throw new Error("Send failed");
return { ok: true };
}
case "list_peers":
return conn.listPeers();
case "remember":
case "recall":
case "get_state":
case "set_state":
// These operations require WS request/response patterns not yet
// implemented in MeshConnection. Coming in a future update.
throw new Error(`${toolCall.name} not yet available via Telegram. Use the CLI.`);
default:
throw new Error(`Unknown tool: ${toolCall.name}`);
}
}
// ---------------------------------------------------------------------------
// Ensure a mesh WS connection exists (create or reuse)
// ---------------------------------------------------------------------------

153
pnpm-lock.yaml generated
View File

@@ -110,6 +110,9 @@ importers:
apps/broker:
dependencies:
'@anthropic-ai/sdk':
specifier: 0.71.2
version: 0.71.2(zod@4.1.13)
'@qdrant/js-client-rest':
specifier: 1.17.0
version: 1.17.0(typescript@5.9.3)
@@ -121,7 +124,7 @@ importers:
version: link:../../packages/shared
drizzle-orm:
specifier: 0.44.7
version: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
version: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
grammy:
specifier: ^1.35.0
version: 1.42.0(encoding@0.1.13)
@@ -221,6 +224,55 @@ importers:
specifier: 'catalog:'
version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@24.0.13)(@vitest/ui@4.0.14)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@26.0.0)(lightningcss@1.30.2)(sass@1.77.4)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0)
apps/cli-v2:
dependencies:
'@modelcontextprotocol/sdk':
specifier: 1.27.1
version: 1.27.1(zod@4.1.13)
citty:
specifier: 0.2.2
version: 0.2.2
libsodium-wrappers:
specifier: 0.7.15
version: 0.7.15
ws:
specifier: 8.20.0
version: 8.20.0
zod:
specifier: 4.1.13
version: 4.1.13
devDependencies:
'@turbostarter/eslint-config':
specifier: workspace:*
version: link:../../tooling/eslint
'@turbostarter/prettier-config':
specifier: workspace:*
version: link:../../tooling/prettier
'@turbostarter/tsconfig':
specifier: workspace:*
version: link:../../tooling/typescript
'@turbostarter/vitest-config':
specifier: workspace:*
version: link:../../tooling/vitest
'@types/libsodium-wrappers':
specifier: 0.7.14
version: 0.7.14
'@types/ws':
specifier: 8.5.13
version: 8.5.13
eslint:
specifier: 'catalog:'
version: 9.39.0(jiti@2.6.1)
prettier:
specifier: 'catalog:'
version: 3.6.2
typescript:
specifier: 'catalog:'
version: 5.9.3
vitest:
specifier: 'catalog:'
version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@24.0.13)(@vitest/ui@4.0.14)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@26.0.0)(lightningcss@1.30.2)(sass@1.77.4)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0)
apps/telegram:
dependencies:
grammy:
@@ -268,10 +320,10 @@ importers:
version: 0.5.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@payloadcms/db-postgres':
specifier: 3.81.0
version: 3.81.0(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(postgres@3.4.7)
version: 3.81.0(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(postgres@3.4.7)
'@payloadcms/db-sqlite':
specifier: ^3.81.0
version: 3.81.0(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)
version: 3.81.0(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)
'@payloadcms/next':
specifier: ^3.81.0
version: 3.81.0(@types/react@19.1.14)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)
@@ -801,10 +853,10 @@ importers:
version: link:../shared
drizzle-orm:
specifier: 0.44.7
version: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
version: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
drizzle-zod:
specifier: 0.8.3
version: 0.8.3(drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))(zod@4.1.13)
version: 0.8.3(drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))(zod@4.1.13)
envin:
specifier: 'catalog:'
version: 1.1.10(arktype@2.1.20)(typescript@5.9.3)(zod@4.1.13)
@@ -829,7 +881,7 @@ importers:
version: 0.31.7
drizzle-seed:
specifier: 0.3.1
version: 0.3.1(drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))
version: 0.3.1(drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))
eslint:
specifier: 'catalog:'
version: 9.39.0(jiti@2.6.1)
@@ -8241,6 +8293,9 @@ packages:
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
bun-types@1.3.12:
resolution: {integrity: sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA==}
busboy@1.6.0:
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
engines: {node: '>=10.16.0'}
@@ -18253,13 +18308,13 @@ snapshots:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
'@payloadcms/db-postgres@3.81.0(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(postgres@3.4.7)':
'@payloadcms/db-postgres@3.81.0(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(postgres@3.4.7)':
dependencies:
'@payloadcms/drizzle': 3.81.0(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)
'@payloadcms/drizzle': 3.81.0(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)
'@types/pg': 8.10.2
console-table-printer: 2.12.1
drizzle-kit: 0.31.7
drizzle-orm: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
drizzle-orm: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
payload: 3.81.0(graphql@16.13.2)(typescript@5.9.3)
pg: 8.16.3
prompts: 2.4.2
@@ -18296,13 +18351,13 @@ snapshots:
- sqlite3
- supports-color
'@payloadcms/db-sqlite@3.81.0(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)':
'@payloadcms/db-sqlite@3.81.0(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)':
dependencies:
'@libsql/client': 0.14.0
'@payloadcms/drizzle': 3.81.0(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)
'@payloadcms/drizzle': 3.81.0(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)
console-table-printer: 2.12.1
drizzle-kit: 0.31.7
drizzle-orm: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
drizzle-orm: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
payload: 3.81.0(graphql@16.13.2)(typescript@5.9.3)
prompts: 2.4.2
to-snake-case: 1.0.0
@@ -18340,11 +18395,11 @@ snapshots:
- supports-color
- utf-8-validate
'@payloadcms/drizzle@3.81.0(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)':
'@payloadcms/drizzle@3.81.0(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)':
dependencies:
console-table-printer: 2.12.1
dequal: 2.0.3
drizzle-orm: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
drizzle-orm: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
payload: 3.81.0(graphql@16.13.2)(typescript@5.9.3)
prompts: 2.4.2
to-snake-case: 1.0.0
@@ -21154,7 +21209,7 @@ snapshots:
'@sentry/bundler-plugin-core': 4.6.1(encoding@0.1.13)
unplugin: 1.0.1
uuid: 9.0.1
webpack: 5.100.2(esbuild@0.25.0)
webpack: 5.100.2
transitivePeerDependencies:
- encoding
- supports-color
@@ -23137,6 +23192,11 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
bun-types@1.3.12:
dependencies:
'@types/node': 24.0.13
optional: true
busboy@1.6.0:
dependencies:
streamsearch: 1.1.0
@@ -23869,35 +23929,37 @@ snapshots:
transitivePeerDependencies:
- supports-color
drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7):
drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7):
optionalDependencies:
'@libsql/client': 0.14.0
'@opentelemetry/api': 1.9.0
'@types/pg': 8.10.2
better-sqlite3: 12.4.1
bun-types: 1.3.12
kysely: 0.28.5
pg: 8.16.3
postgres: 3.4.7
drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7):
drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7):
optionalDependencies:
'@libsql/client': 0.14.0
'@opentelemetry/api': 1.9.0
'@types/pg': 8.16.0
better-sqlite3: 12.4.1
bun-types: 1.3.12
kysely: 0.28.5
pg: 8.16.3
postgres: 3.4.7
drizzle-seed@0.3.1(drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)):
drizzle-seed@0.3.1(drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)):
dependencies:
pure-rand: 6.1.0
optionalDependencies:
drizzle-orm: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
drizzle-orm: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
drizzle-zod@0.8.3(drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))(zod@4.1.13):
drizzle-zod@0.8.3(drizzle-orm@0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))(zod@4.1.13):
dependencies:
drizzle-orm: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
drizzle-orm: 0.44.7(@libsql/client@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(better-sqlite3@12.4.1)(bun-types@1.3.12)(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7)
zod: 4.1.13
dunder-proto@1.0.1:
@@ -27217,7 +27279,7 @@ snapshots:
postcss: 8.4.31
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.3)
styled-jsx: 5.1.6(react@19.2.3)
optionalDependencies:
'@next/swc-darwin-arm64': 16.2.2
'@next/swc-darwin-x64': 16.2.2
@@ -29759,6 +29821,12 @@ snapshots:
react: 19.2.3
optionalDependencies:
'@babel/core': 7.28.5
optional: true
styled-jsx@5.1.6(react@19.2.3):
dependencies:
client-only: 0.0.1
react: 19.2.3
styleq@0.1.3:
optional: true
@@ -29916,6 +29984,15 @@ snapshots:
optionalDependencies:
esbuild: 0.25.0
terser-webpack-plugin@5.3.14(webpack@5.100.2):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
jest-worker: 27.5.1
schema-utils: 4.3.2
serialize-javascript: 6.0.2
terser: 5.43.1
webpack: 5.100.2
terser@5.43.1:
dependencies:
'@jridgewell/source-map': 0.3.10
@@ -30628,6 +30705,38 @@ snapshots:
webpack-virtual-modules@0.5.0: {}
webpack@5.100.2:
dependencies:
'@types/eslint-scope': 3.7.7
'@types/estree': 1.0.8
'@types/json-schema': 7.0.15
'@webassemblyjs/ast': 1.14.1
'@webassemblyjs/wasm-edit': 1.14.1
'@webassemblyjs/wasm-parser': 1.14.1
acorn: 8.16.0
acorn-import-phases: 1.0.4(acorn@8.16.0)
browserslist: 4.25.1
chrome-trace-event: 1.0.4
enhanced-resolve: 5.18.3
es-module-lexer: 1.7.0
eslint-scope: 5.1.1
events: 3.3.0
glob-to-regexp: 0.4.1
graceful-fs: 4.2.11
json-parse-even-better-errors: 2.3.1
loader-runner: 4.3.0
mime-types: 2.1.35
neo-async: 2.6.2
schema-utils: 4.3.2
tapable: 2.2.2
terser-webpack-plugin: 5.3.14(webpack@5.100.2)
watchpack: 2.4.4
webpack-sources: 3.3.3
transitivePeerDependencies:
- '@swc/core'
- esbuild
- uglify-js
webpack@5.100.2(esbuild@0.25.0):
dependencies:
'@types/eslint-scope': 3.7.7