feat(broker): add claude-powered telegram bot with tool calling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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:*",
|
||||
|
||||
@@ -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"),
|
||||
|
||||
291
apps/broker/src/telegram-ai.ts
Normal file
291
apps/broker/src/telegram-ai.ts
Normal 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 };
|
||||
@@ -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 ---
|
||||
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",
|
||||
);
|
||||
if (ok) sent++;
|
||||
}
|
||||
if (sent === 0) {
|
||||
await ctx.reply("❌ Not connected to any mesh.");
|
||||
// --- 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");
|
||||
if (ok) sent++;
|
||||
}
|
||||
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
153
pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user