diff --git a/templates/_shared/nextjs-ui/app/dashboard/page.tsx b/templates/_shared/nextjs-ui/app/dashboard/page.tsx index b27212d..af456ae 100644 --- a/templates/_shared/nextjs-ui/app/dashboard/page.tsx +++ b/templates/_shared/nextjs-ui/app/dashboard/page.tsx @@ -25,7 +25,7 @@ import { CardHeader, CardTitle, } from "@arcadeai/design-system"; -import { Info, Loader2, ShieldAlert, AlertTriangle, RotateCcw } from "lucide-react"; +import { Info, Loader2, ShieldAlert, AlertTriangle, RotateCcw, X } from "lucide-react"; // --- Config health warnings --- @@ -127,12 +127,31 @@ function DashboardContent() { if (toolName) markSourceAuthRequired(toolName); addAuthUrl(url, toolName); }, + onElicitation: (_elicitationId, authUrl, message) => { + addAuthUrl(authUrl, message); + }, onSourcesDone: markAllCheckingAsConnected, }); // --- Config health check --- const [configWarnings, setConfigWarnings] = useState([]); + // --- Dismissible callout state --- + const [showAgentWorkingCallout, setShowAgentWorkingCallout] = useState(() => + typeof window !== "undefined" ? localStorage.getItem("callout-agent-working") !== "1" : true + ); + const dismissAgentWorkingCallout = () => { + localStorage.setItem("callout-agent-working", "1"); + setShowAgentWorkingCallout(false); + }; + const [showCustomizeCallout, setShowCustomizeCallout] = useState(() => + typeof window !== "undefined" ? localStorage.getItem("callout-customize-agent") !== "1" : true + ); + const dismissCustomizeCallout = () => { + localStorage.setItem("callout-customize-agent", "1"); + setShowCustomizeCallout(false); + }; + useEffect(() => { fetch("/api/health") .then((r) => r.json()) @@ -159,6 +178,8 @@ function DashboardContent() { // --- Handlers --- const handleLogout = useCallback(async () => { + // Clear Arcade tokens so the next user who signs in has to re-authenticate. + await fetch("/api/auth/arcade/disconnect", { method: "POST" }).catch(() => {}); await authClient.signOut(); router.push("/"); }, [router]); @@ -206,15 +227,9 @@ function DashboardContent() { - - Why Arcade? + Why sign into Arcade? The agent uses Arcade as an MCP Gateway to read from your tools on your behalf. Signing in here links your Arcade identity so the gateway knows which @@ -325,19 +340,29 @@ function DashboardContent() { {loading && !hasItems && authUrls.length === 0 && (
- - - What's happening - - The agent is reading from your connected sources and classifying what it finds. - This behavior is driven by the system prompt in{" "} - - app/api/plan/route.ts - {" "} - — edit it to change what gets fetched, how items are prioritized, or what the - agent focuses on. - - + {showAgentWorkingCallout && ( + + + + What's happening + + + + The agent is reading from your connected sources and classifying what it finds. + This behavior is driven by the system prompt in{" "} + + app/api/plan/route.ts + {" "} + — edit it to change what gets fetched, how items are prioritized, or what the + agent focuses on. + + + )}
{Array.from({ length: 3 }).map((_, i) => ( @@ -368,19 +393,29 @@ function DashboardContent() { )}
- - - Make it yours - - Start with the system prompt in{" "} - - app/api/plan/route.ts - {" "} - — that's where the agent's behavior is defined. From there, check out{" "} - AGENT_PLAYBOOK.md{" "} - for a full walkthrough of customization points. - - + {showCustomizeCallout && ( + + + + Make it yours + + + + Start with the system prompt in{" "} + + app/api/plan/route.ts + {" "} + — that's where the agent's behavior is defined. From there, check out{" "} + AGENT_PLAYBOOK.md{" "} + for a full walkthrough of customization points. + + + )}

Authorization required

-

- The tool {toolName} needs permission to continue. -

-
- - -
+

{toolName} needs permission to continue.

+ {clicked ? ( +
+ + Waiting for authorization… the agent will resume automatically. +
+ ) : ( +
+ + +
+ )}
); } diff --git a/templates/_shared/nextjs-ui/components/dashboard/empty-state.tsx b/templates/_shared/nextjs-ui/components/dashboard/empty-state.tsx index 941b13a..5592997 100644 --- a/templates/_shared/nextjs-ui/components/dashboard/empty-state.tsx +++ b/templates/_shared/nextjs-ui/components/dashboard/empty-state.tsx @@ -1,12 +1,25 @@ -import { Inbox, Info, Loader2 } from "lucide-react"; +"use client"; + +import { useState } from "react"; +import { Inbox, Info, Loader2, X } from "lucide-react"; import { Alert, AlertTitle, AlertDescription, Button } from "@arcadeai/design-system"; +const CALLOUT_KEY = "callout-how-planning-works"; + interface EmptyStateProps { onPlan: () => void; loading: boolean; } export function EmptyState({ onPlan, loading }: EmptyStateProps) { + const [showCallout, setShowCallout] = useState(() => + typeof window !== "undefined" ? localStorage.getItem(CALLOUT_KEY) !== "1" : true + ); + const dismissCallout = () => { + localStorage.setItem(CALLOUT_KEY, "1"); + setShowCallout(false); + }; + return (
@@ -19,17 +32,27 @@ export function EmptyState({ onPlan, loading }: EmptyStateProps) { build your action plan.

- - - How this works - - When you click “Plan my day”, the agent connects to your Slack, Gmail, Google - Calendar, Linear, and GitHub through Arcade's MCP Gateway. It will first check if any - of those need authentication — if so, you'll be prompted to authorize before the run - starts. Then it reads your recent messages, emails, events, issues, and pull requests, and - classifies each item by priority and suggests next steps. - - + {showCallout && ( + + + + How this works + + + + When you click “Plan my day”, the agent connects to your Slack, Gmail, + Google Calendar, Linear, and GitHub through Arcade's MCP Gateway. It will first + check if any of those need authentication — if so, you'll be prompted to authorize + before the run starts. Then it reads your recent messages, emails, events, issues, and + pull requests, and classifies each item by priority and suggests next steps. + + + )} + + + Each tool connects on your behalf using OAuth — the agent only gets read access to scan + for items to triage. You can skip any source you don't use, and revoke access + anytime from your Arcade dashboard. + +
+ )}
{Object.entries(sourceStatuses).map(([source, status]) => { diff --git a/templates/_shared/nextjs-ui/hooks/use-arcade-connection.ts b/templates/_shared/nextjs-ui/hooks/use-arcade-connection.ts index 829a309..3f5f55c 100644 --- a/templates/_shared/nextjs-ui/hooks/use-arcade-connection.ts +++ b/templates/_shared/nextjs-ui/hooks/use-arcade-connection.ts @@ -64,13 +64,17 @@ export function useArcadeConnection(): { useEffect(() => { checkConnection(); - const onFocus = () => { + const onFocusOrVisible = () => { if (Date.now() - lastCheckRef.current < 2000) return; authInProgress.current = false; // User returned from OAuth tab — re-check checkConnection(); }; - window.addEventListener("focus", onFocus); - return () => window.removeEventListener("focus", onFocus); + window.addEventListener("focus", onFocusOrVisible); + document.addEventListener("visibilitychange", onFocusOrVisible); + return () => { + window.removeEventListener("focus", onFocusOrVisible); + document.removeEventListener("visibilitychange", onFocusOrVisible); + }; }, [checkConnection]); const retryConnection = useCallback(() => checkConnection({ isRetry: true }), [checkConnection]); diff --git a/templates/_shared/nextjs-ui/hooks/use-plan-stream.ts b/templates/_shared/nextjs-ui/hooks/use-plan-stream.ts index cb659fc..7761119 100644 --- a/templates/_shared/nextjs-ui/hooks/use-plan-stream.ts +++ b/templates/_shared/nextjs-ui/hooks/use-plan-stream.ts @@ -5,6 +5,7 @@ import type { InboxItem, PlanEvent } from "@/types/inbox"; interface PlanStreamCallbacks { onAuthRequired?: (authUrl: string, toolName?: string) => void; + onElicitation?: (elicitationId: string, authUrl: string, message: string) => void; onSourcesDone?: () => void; } @@ -92,6 +93,13 @@ export function usePlanStream(callbacks?: PlanStreamCallbacks): { case "auth_required": callbacksRef.current?.onAuthRequired?.(event.authUrl, event.toolName); break; + case "elicitation": + callbacksRef.current?.onElicitation?.( + event.elicitationId, + event.authUrl, + event.message + ); + break; case "status": setStatusMessage(event.message); break; diff --git a/templates/_shared/nextjs-ui/hooks/use-source-check.ts b/templates/_shared/nextjs-ui/hooks/use-source-check.ts index 682a956..e976908 100644 --- a/templates/_shared/nextjs-ui/hooks/use-source-check.ts +++ b/templates/_shared/nextjs-ui/hooks/use-source-check.ts @@ -69,14 +69,24 @@ export function useSourceCheck(options: { enabled: boolean }): { checkSources(); }, [enabled, checkSources]); - // Re-check sources when user returns from an auth tab (while gate is active) + // Re-check sources while the auth gate is open: + // - focus / visibilitychange: immediately when user switches back to this tab + // - setInterval: every 3s so auth completion is detected quickly even if the + // user stays on the external auth page for a moment before returning useEffect(() => { if (!authGateActive) return; - const onFocus = () => { - checkSources(); + const onFocus = () => checkSources(); + const onVisibilityChange = () => { + if (document.visibilityState === "visible") checkSources(); }; window.addEventListener("focus", onFocus); - return () => window.removeEventListener("focus", onFocus); + document.addEventListener("visibilitychange", onVisibilityChange); + const interval = setInterval(checkSources, 3000); + return () => { + window.removeEventListener("focus", onFocus); + document.removeEventListener("visibilitychange", onVisibilityChange); + clearInterval(interval); + }; }, [authGateActive, checkSources]); const skipSource = (source: string) => { diff --git a/templates/_shared/nextjs-ui/types/inbox.ts b/templates/_shared/nextjs-ui/types/inbox.ts index 9828ded..8cbe2f4 100644 --- a/templates/_shared/nextjs-ui/types/inbox.ts +++ b/templates/_shared/nextjs-ui/types/inbox.ts @@ -31,6 +31,7 @@ export type PlanEvent = | { type: "task"; data: InboxItem } | { type: "summary"; data: { total: number; bySource: Record } } | { type: "auth_required"; authUrl: string; toolName?: string } + | { type: "elicitation"; elicitationId: string; authUrl: string; message: string } | { type: "sources"; sources: string[] } | { type: "error"; message: string } | { type: "done" }; diff --git a/templates/_shared/partials/arcade-oauth-provider.hbs b/templates/_shared/partials/arcade-oauth-provider.hbs index 4d9522c..5578d7e 100644 --- a/templates/_shared/partials/arcade-oauth-provider.hbs +++ b/templates/_shared/partials/arcade-oauth-provider.hbs @@ -104,6 +104,14 @@ export function clearPendingAuthUrl() { } } +export function clearArcadeTokens() { + try { + unlinkSync(TOKENS_FILE); + } catch { + // Ignore if file doesn't exist. + } +} + // --- OAuth provider (implements OAuthClientProvider from MCP SDK) --- class ArcadeOAuthProvider implements OAuthClientProvider { @@ -169,12 +177,38 @@ export async function initiateOAuth(): Promise<"AUTHORIZED" | "REDIRECT"> { } {{#if (eq name "mastra")}} -// Mastra MCP client (used by the Mastra agent for chat) +// Mastra MCP client — uses a per-request fetch to inject bearer tokens instead +// of authProvider, so the singleton never triggers its own OAuth handshake and +// races with the connect route's initiateOAuth() call. +// +// URL-mode elicitation is declared so the gateway proactively sends auth URLs +// when a tool needs authorization. The frontend opens these in a new tab +// (pointing to app.arcade.dev) — this is expected for tool auth and is +// separate from the initial Arcade gateway connection, which always redirects +// back to localhost via PKCE. + +// Per-request callback bridging the singleton elicitation handler to the +// active response stream. Safe for single-user local dev use. +let _elicitationCallback: ((authUrl: string, message: string) => void) | null = null; + +export function setElicitationCallback( + cb: ((authUrl: string, message: string) => void) | null +) { + _elicitationCallback = cb; +} + export const mcpClient = new MCPClient({ servers: { arcade: { url: new URL(getGatewayUrl()), - authProvider: oauthProvider, + fetch: async (url: RequestInfo | URL, init?: RequestInit) => { + const tokens = oauthProvider.tokens(); + const headers = new Headers(init?.headers); + if (tokens?.access_token) { + headers.set("Authorization", `Bearer ${tokens.access_token}`); + } + return fetch(url, { ...init, headers }); + }, }, }, }); @@ -183,6 +217,20 @@ export const mcpClient = new MCPClient({ /** * Create an AI SDK MCP client for Arcade Gateway using stored OAuth tokens. * Auto-detects transport: SSE for /sse URLs, Streamable HTTP otherwise. + * + * WHY NO URL-MODE ELICITATION (as of April 2026): + * @ai-sdk/mcp only supports form-mode elicitation. Declaring + * `capabilities: { elicitation: { url: {} } }` causes the Arcade gateway to + * route tool auth through URL elicitation instead of embedding auth URLs in + * tool output — but the AI SDK client has no handler for URL-mode requests, + * so those requests are silently dropped and tool auth never surfaces to the + * user. The Mastra template uses URL-mode elicitation instead via + * @mastra/mcp's mcpClient.elicitation.onRequest(). + * + * Revisit when @ai-sdk/mcp adds URL-mode elicitation support. + * + * In the meantime, auth URLs are surfaced via extractAuthUrlFromToolOutput + * in onStepFinish, which scrapes them from tool result text. */ export async function getArcadeMCPClient() { const gatewayUrl = getGatewayUrl(); diff --git a/templates/_shared/partials/extract-auth-url.hbs b/templates/_shared/partials/extract-auth-url.hbs index 129d6f4..9506c6d 100644 --- a/templates/_shared/partials/extract-auth-url.hbs +++ b/templates/_shared/partials/extract-auth-url.hbs @@ -1,3 +1,4 @@ +// Fallback: extract auth URL from tool output for gateways that don't support elicitation function extractAuthUrlFromToolOutput(output: unknown): string | null { const fromRecord = (value: unknown): string | null => { if (!value || typeof value !== "object") return null; diff --git a/templates/_shared/partials/plan-route-body.hbs b/templates/_shared/partials/plan-route-body.hbs index 4f74e8a..90ac0f7 100644 --- a/templates/_shared/partials/plan-route-body.hbs +++ b/templates/_shared/partials/plan-route-body.hbs @@ -7,6 +7,7 @@ type PlanEvent = | { type: "task"; data: InboxItem } | { type: "summary"; data: { total: number; bySource: Record } } | { type: "auth_required"; authUrl: string; toolName?: string } + | { type: "elicitation"; elicitationId: string; authUrl: string; message: string } | { type: "sources"; sources: string[] } | { type: "error"; message: string } | { type: "done" }; @@ -67,15 +68,49 @@ function extractJsonBlocks(text: string): { }; } +// --- Shared helpers for tool filtering & truncation --- + +const MUTATION = /create|update|delete|send|reply|post|archive|remove|add|invite|merge|close|assign|edit|publish|comment/i; + +function isTriageTool(name: string): boolean { + return !MUTATION.test(name); +} + +const MAX_TOOL_RESULT_CHARS = 4000; + +function truncateToolResult(result: unknown): unknown { + if (result && typeof result === "object" && "content" in (result as Record)) { + const obj = result as { content: { type: string; text: string }[] }; + return { + ...obj, + content: obj.content.map((item) => { + if (item.type === "text" && item.text && item.text.length > MAX_TOOL_RESULT_CHARS) { + return { + ...item, + text: item.text.slice(0, MAX_TOOL_RESULT_CHARS) + + `\n...[truncated ${item.text.length - MAX_TOOL_RESULT_CHARS} chars]`, + }; + } + return item; + }), + }; + } + if (typeof result === "string" && result.length > MAX_TOOL_RESULT_CHARS) { + return result.slice(0, MAX_TOOL_RESULT_CHARS) + + `\n...[truncated ${result.length - MAX_TOOL_RESULT_CHARS} chars]`; + } + return result; +} + // --- Route handler --- +{{#if (eq name "mastra")}} export async function POST() { const user = await getSession(); if (!user) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } - let mcpClient: Awaited> | null = null; let streamController: ReadableStreamDefaultController | undefined; const stream = new ReadableStream({ @@ -92,55 +127,268 @@ export async function POST() { } } - // Process async — return stream immediately (async () => { try { emit({ type: "status", message: "Connecting to Arcade Gateway..." }); - mcpClient = await getArcadeMCPClient(); - const allTools = await mcpClient.tools(); + // Register the URL elicitation handler for this request, then connect. + // mcpClient.elicitation.onRequest requires the server name as the first + // argument and must be awaited — it connects to the server if needed and + // registers the handler on the underlying MCP client so the gateway can + // send auth URLs when tools need authorization. + let elicitationCounter = 0; + setElicitationCallback((authUrl, message) => { + emit({ + type: "elicitation", + elicitationId: `elicit-${++elicitationCounter}`, + authUrl, + message, + }); + }); + await mcpClient.elicitation.onRequest("arcade", async (request) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const params = request as any; + // Arcade sends the auth URL in message or a url field + const authUrl: string = params?.url ?? params?.message ?? ""; + const message: string = + typeof params?.message === "string" ? params.message : "Authorization required"; + if (authUrl && _elicitationCallback) { + _elicitationCallback(authUrl, message); + } + return { action: "accept" }; + }); - // Only keep tools useful for triage — filter out mutations (create, delete, send, etc.) - // so the agent only reads data during planning. - const MUTATION = /create|update|delete|send|reply|post|archive|remove|add|invite|merge|close|assign|edit|publish|comment/i; + const allToolsets = await mcpClient.listToolsets(); + + // Filter toolsets: keep only read-only triage tools, wrap execute for truncation. + // listToolsets() returns { serverName: { toolName: tool, ... } }. + const filteredToolsets: Record> = {}; + const toolNames: string[] = []; + let allToolCount = 0; + + for (const [setName, toolsObj] of Object.entries(allToolsets)) { + const tools = toolsObj as unknown as Record Promise; [k: string]: unknown }>; + const filtered: Record = {}; + + for (const [toolName, tool] of Object.entries(tools)) { + allToolCount++; + if (!isTriageTool(toolName)) continue; + if (/[._]WhoAmI$/i.test(toolName)) continue; + + if (tool.execute) { + const orig = tool.execute; + filtered[toolName] = { + ...tool, + execute: async (...args: unknown[]) => truncateToolResult(await orig(...args)), + }; + } else { + filtered[toolName] = tool; + } + toolNames.push(toolName); + } - function isTriageTool(name: string): boolean { - return !MUTATION.test(name); + if (Object.keys(filtered).length > 0) { + filteredToolsets[setName] = filtered; + } } - // Cap individual tool results so they don't blow the model's context window. - // Gmail threads, GitHub notifications, etc. can return 50k+ chars each. - // MCP tools return { content: [{ type: "text", text: "..." }] } — we must - // preserve that structure or the AI SDK crashes. - const MAX_TOOL_RESULT_CHARS = 4000; - - function truncateToolResult(result: unknown): unknown { - if (result && typeof result === "object" && "content" in (result as Record)) { - const obj = result as { content: { type: string; text: string }[] }; - return { - ...obj, - content: obj.content.map((item) => { - if (item.type === "text" && item.text && item.text.length > MAX_TOOL_RESULT_CHARS) { - return { - ...item, - text: item.text.slice(0, MAX_TOOL_RESULT_CHARS) + - `\n...[truncated ${item.text.length - MAX_TOOL_RESULT_CHARS} chars]`, - }; - } - return item; - }), - }; + + const sources = [...new Set(toolNames.map((n) => mapToolToSource(n)))]; + console.log(`[plan] ${toolNames.length} triage tools (of ${allToolCount} total) from sources: ${sources.join(", ")}`); + + emit({ + type: "status", + message: `Found ${toolNames.length} tools across ${sources.length} sources. Starting triage...`, + }); + + emit({ type: "sources", sources }); + + const toolsBySource: Record = {}; + for (const name of toolNames) { + const src = mapToolToSource(name); + if (!toolsBySource[src]) toolsBySource[src] = []; + toolsBySource[src].push(name); + } + const toolInventory = Object.entries(toolsBySource) + .map(([src, names]) => `${src}: ${names.join(", ")}`) + .join("\n"); + + const prompt = `Plan my day. Today is ${new Date().toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" })}.\n\nHere are the tools available by source:\n\n${toolInventory}\n\nIn your FIRST step, call ALL non-WhoAmI tools in parallel — one call per tool. Do NOT skip any source.\nFor calendar tools, make sure to pass today's date range so you get today's events.\nAfter getting results, classify every item. If any source returned a list you can drill into, make follow-up calls.\nI need a COMPLETE picture: notifications, assigned work, upcoming events, and unread messages.`; + + let accumulatedText = ""; + let emittedTaskCount = 0; + let emittedSummary = false; + + const agent = new Agent({ + id: "plan-agent", + name: "Plan Agent", + model: getModel(), + instructions: planPrompt, + }); + + const result = await agent.stream(prompt, { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + toolsets: filteredToolsets as any, + maxSteps: 20, + onStepFinish: (step) => { + // Mastra's step.toolCalls/toolResults are ChunkType objects: + // { type: "tool-call"|"tool-result", payload: { toolCallId, toolName, output, ... } } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const stepData = step as any; + const toolCalls: { type: string; payload: { toolCallId: string; toolName: string } }[] = + stepData.toolCalls ?? []; + const toolResults: { type: string; payload: { toolCallId?: string; toolName?: string; output: unknown } }[] = + stepData.toolResults ?? []; + console.log(`[plan] Step: ${toolCalls.length} calls, ${toolResults.length} results`); + + for (const call of toolCalls) { + const toolName = call.payload?.toolName; + if (!toolName) continue; + const source = mapToolToSource(toolName); + emit({ + type: "status", + message: `Calling ${source}: ${toolName}...`, + }); + } + + const toolNameByCallId = new Map( + toolCalls.map((call) => [call.payload?.toolCallId, call.payload?.toolName] as const) + ); + + for (let i = 0; i < toolResults.length; i++) { + const tr = toolResults[i]; + const authUrl = extractAuthUrlFromToolOutput(tr.payload?.output); + if (authUrl) { + const matchedToolName = tr.payload?.toolCallId + ? toolNameByCallId.get(tr.payload.toolCallId) + : undefined; + const fallbackToolName = i < toolCalls.length + ? toolCalls[i].payload?.toolName + : undefined; + const toolName = mapToolToSource(matchedToolName ?? fallbackToolName ?? ""); + emit({ type: "auth_required", authUrl, toolName }); + } + } + }, + }); + + for await (const chunk of result.textStream) { + if (chunk) { + accumulatedText += chunk; + const { tasks, summary, remaining } = extractJsonBlocks(accumulatedText); + accumulatedText = remaining; + + for (const task of tasks) { + emittedTaskCount++; + emit({ type: "task", data: task }); + emit({ + type: "status", + message: `Classified ${emittedTaskCount} item${emittedTaskCount > 1 ? "s" : ""}...`, + }); + } + if (summary) { + emit({ type: "summary", data: summary }); + emittedSummary = true; + } + } + } + + if (accumulatedText.length > 0) { + const { tasks, summary } = extractJsonBlocks(accumulatedText); + for (const task of tasks) { + emittedTaskCount++; + emit({ type: "task", data: task }); } - if (typeof result === "string" && result.length > MAX_TOOL_RESULT_CHARS) { - return result.slice(0, MAX_TOOL_RESULT_CHARS) + - `\n...[truncated ${result.length - MAX_TOOL_RESULT_CHARS} chars]`; + if (summary) { + emit({ type: "summary", data: summary }); + emittedSummary = true; } - return result; } + if (!emittedSummary && emittedTaskCount > 0) { + emit({ + type: "summary", + data: { total: emittedTaskCount, bySource: {} }, + }); + } + + emit({ type: "done" }); + } catch (error) { + console.error("[plan] Error:", error); + const msg = error instanceof Error ? error.message : "Unknown error"; + const isAuthError = + msg.includes("401") || + msg.includes("invalid_authorization") || + msg.includes("Missing Authorization") || + msg.includes("Unauthorized"); + if (isAuthError) { + // Clear stored tokens so the connect route detects re-auth is needed + // on the next check (page reload or window focus). + clearArcadeTokens(); + } + emit({ + type: "error", + message: isAuthError + ? "Not connected to Arcade. Please connect your Arcade account from the dashboard." + : msg, + }); + emit({ type: "done" }); + } finally { + setElicitationCallback(null); + try { + streamController?.close(); + } catch { + /* ignore */ + } + } + })(); + + return new Response(stream, { + headers: { + "Content-Type": "application/x-ndjson", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }, + }); +} +{{else}} +export async function POST() { + const user = await getSession(); + if (!user) { + return Response.json({ error: "Unauthorized" }, { status: 401 }); + } + + let mcpClient: Awaited> | null = null; + let streamController: ReadableStreamDefaultController | undefined; + + const stream = new ReadableStream({ + start(controller) { + streamController = controller; + }, + }); + + function emit(event: PlanEvent) { + try { + streamController?.enqueue(encodeEvent(event)); + } catch { + // Stream closed by client + } + } + + // Process async — return stream immediately + (async () => { + try { + emit({ type: "status", message: "Connecting to Arcade Gateway..." }); + + mcpClient = await getArcadeMCPClient({ + onElicitation: ({ elicitationId, message, authUrl }) => { + emit({ type: "elicitation", elicitationId, authUrl, message }); + }, + }); + const allTools = await mcpClient.tools(); + const tools: typeof allTools = {}; for (const [name, tool] of Object.entries(allTools)) { if (!isTriageTool(name)) continue; - // Don't give WhoAmI tools to the model — they're for auth checks, not data if (/[._]WhoAmI$/i.test(name)) continue; const orig = tool.execute; tools[name] = { @@ -312,3 +560,4 @@ export async function POST() { }, }); } +{{/if}} diff --git a/templates/ai-sdk/app/api/auth/arcade/connect/route.ts b/templates/ai-sdk/app/api/auth/arcade/connect/route.ts index 3c0f724..c68c2c7 100644 --- a/templates/ai-sdk/app/api/auth/arcade/connect/route.ts +++ b/templates/ai-sdk/app/api/auth/arcade/connect/route.ts @@ -1,6 +1,5 @@ import { oauthProvider, - getArcadeMCPClient, getPendingAuthUrl, clearPendingAuthUrl, initiateOAuth, @@ -30,23 +29,15 @@ export async function POST() { ); } - // Fast path: tokens already on disk. + // Fast path: tokens already on disk — trust them without a live verification + // request. Making a live request here caused a race condition where freshly- + // exchanged tokens could get a transient 401 from the gateway before it fully + // processed them, forcing an unnecessary second sign-in. If a stored token is + // actually expired the plan route surfaces an auth error and the user can + // reconnect from the error state on the dashboard. const existingTokens = oauthProvider.tokens(); if (existingTokens?.access_token) { - const connStatus = await verifyExistingConnection(); - if (connStatus === "ok") return Response.json({ connected: true }); - if (connStatus === "unreachable") { - // Don't trigger a new OAuth redirect for transient connectivity failures — - // that would force the user to re-authorize when the gateway is just briefly down. - return Response.json( - { - connected: false, - error: "Cannot reach Arcade Gateway. Check ARCADE_GATEWAY_URL and try again.", - }, - { status: 502 } - ); - } - // "needs_reauth": tokens invalid/expired — fall through to OAuth + return Response.json({ connected: true }); } if (!connectPromise) { @@ -63,7 +54,6 @@ async function doConnect(): Promise<{ data: Record; status?: number; }> { - let mcpClient: Awaited> | null = null; try { // Trigger MCP OAuth flow (discovery, registration, PKCE) const result = await initiateOAuth(); @@ -76,12 +66,8 @@ async function doConnect(): Promise<{ } } - // AUTHORIZED — verify by listing tools - mcpClient = await getArcadeMCPClient(); - const tools = await mcpClient.tools(); - return { - data: { connected: true, toolCount: Object.keys(tools).length }, - }; + // AUTHORIZED + return { data: { connected: true } }; } catch { const authUrl = getPendingAuthUrl(); if (authUrl) { @@ -97,34 +83,5 @@ async function doConnect(): Promise<{ }, status: 502, }; - } finally { - if (mcpClient) { - try { - await mcpClient.close(); - } catch { - // Ignore close errors from failed initialization attempts. - } - } - } -} - -async function verifyExistingConnection(): Promise<"ok" | "needs_reauth" | "unreachable"> { - let mcpClient: Awaited> | null = null; - try { - mcpClient = await getArcadeMCPClient(); - await mcpClient.tools(); - return "ok"; - } catch (e) { - const msg = e instanceof Error ? e.message : String(e); - if (/401|403|unauthorized|forbidden/i.test(msg)) return "needs_reauth"; - return "unreachable"; - } finally { - if (mcpClient) { - try { - await mcpClient.close(); - } catch { - // Best effort cleanup. - } - } } } diff --git a/templates/ai-sdk/app/api/auth/arcade/disconnect/route.ts b/templates/ai-sdk/app/api/auth/arcade/disconnect/route.ts new file mode 100644 index 0000000..ab445d5 --- /dev/null +++ b/templates/ai-sdk/app/api/auth/arcade/disconnect/route.ts @@ -0,0 +1,11 @@ +import { clearArcadeTokens } from "@/lib/arcade"; +import { getSession } from "@/lib/auth"; + +export async function POST() { + const session = await getSession(); + if (!session) { + return Response.json({ error: "Unauthorized" }, { status: 401 }); + } + clearArcadeTokens(); + return Response.json({ success: true }); +} diff --git a/templates/ai-sdk/package.json b/templates/ai-sdk/package.json index 675de8d..35a3bf9 100644 --- a/templates/ai-sdk/package.json +++ b/templates/ai-sdk/package.json @@ -15,10 +15,10 @@ }, "dependencies": { "@ai-sdk/anthropic": "^3.0.0", - "@ai-sdk/mcp": "^1.0.21", + "@ai-sdk/mcp": "^1.0.36", "@ai-sdk/openai": "^3.0.29", "@ai-sdk/react": "^3.0.92", - "@modelcontextprotocol/sdk": "^1.12.1", + "@modelcontextprotocol/sdk": "^1.27.1", "ai": "^6.0.90", "@better-auth/drizzle-adapter": "^1.5.3", "better-auth": "^1.5.3", diff --git a/templates/langchain/app/arcade_oauth.py b/templates/langchain/app/arcade_oauth.py index 8885cbd..2b4f881 100644 --- a/templates/langchain/app/arcade_oauth.py +++ b/templates/langchain/app/arcade_oauth.py @@ -16,6 +16,7 @@ import logging import secrets import time +from collections.abc import Callable from pathlib import Path from urllib.parse import urlencode, urlparse @@ -358,6 +359,32 @@ async def exchange_code(code: str) -> dict: return tokens +# --- Per-request elicitation bridge --- +# The MCP client is a module-level singleton, but the NDJSON emitter is +# per-request. A module-level callback ref bridges the two. + +_elicitation_callback: Callable[[str, str, str], None] | None = None + + +def set_elicitation_callback(cb: Callable[[str, str, str], None] | None): + """Set (or clear) the per-request elicitation event callback.""" + global _elicitation_callback + _elicitation_callback = cb + + +async def _on_elicitation(mcp_context, params, context): + """Handle URL-mode elicitation requests from Arcade gateway.""" + from mcp.types import ElicitResult + + message = getattr(params, "message", "Authorization required") + auth_url = getattr(params, "url", "") + elicitation_id = getattr(params, "elicitation_id", "") or getattr(params, "elicitationId", "") + logging.getLogger(__name__).info("[MCP] Elicitation request: %s", message) + if _elicitation_callback: + _elicitation_callback(elicitation_id, message, auth_url) + return ElicitResult(action="accept") + + # --- MCP Client factory --- @@ -367,6 +394,7 @@ def create_mcp_client(): After OAuth is complete, the stored access_token is sent as a Bearer token to authenticate with the Arcade MCP Gateway. """ + from langchain_mcp_adapters.callbacks import Callbacks from langchain_mcp_adapters.client import MultiServerMCPClient if not settings.arcade_gateway_url: @@ -387,7 +415,8 @@ def create_mcp_client(): "url": settings.arcade_gateway_url, "headers": headers, } - } + }, + callbacks=Callbacks(on_elicitation=_on_elicitation), ) diff --git a/templates/langchain/app/routes/auth.py b/templates/langchain/app/routes/auth.py index baad4a7..d71927e 100644 --- a/templates/langchain/app/routes/auth.py +++ b/templates/langchain/app/routes/auth.py @@ -4,7 +4,9 @@ all password hashing and user management to FastAPI Users' UserManager. """ +import contextlib from dataclasses import dataclass +from pathlib import Path from fastapi import APIRouter, Depends, Response from fastapi.responses import JSONResponse @@ -15,6 +17,8 @@ from app.auth_manager import UserManager, get_jwt_strategy, get_user_manager from app.config import settings +_ARCADE_TOKENS_FILE = Path(__file__).resolve().parents[2] / ".arcade-auth" / "tokens.json" + router = APIRouter(prefix="/api/auth", tags=["auth"]) @@ -84,5 +88,8 @@ async def login( @router.post("/logout") async def logout(response: Response): + # Clear Arcade tokens so the next user who signs in has to re-authenticate. + with contextlib.suppress(OSError): + _ARCADE_TOKENS_FILE.unlink() response.delete_cookie("session_id", path="/") return {"success": True} diff --git a/templates/langchain/app/routes/plan.py b/templates/langchain/app/routes/plan.py index ef92375..1cb8dd9 100644 --- a/templates/langchain/app/routes/plan.py +++ b/templates/langchain/app/routes/plan.py @@ -17,7 +17,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.agent import get_llm -from app.arcade_oauth import get_cached_tools, get_mcp_client +from app.arcade_oauth import get_cached_tools, get_mcp_client, set_elicitation_callback from app.auth import get_current_user from app.database import get_db @@ -110,15 +110,15 @@ def _build_plan_prompt(): "```\n\n" "URL RULES:\n" "Prefer a direct deep link to the item itself:\n" - "- Slack: use the \"permalink\" field if present" + '- Slack: use the "permalink" field if present' " (https://.slack.com/archives//p)\n" "- GitHub: use the issue or PR URL on github.com\n" "- Linear: use the Linear issue URL\n" "- Gmail: use the Gmail thread URL (https://mail.google.com/mail/u/0/#inbox/)\n" - "- Google Calendar: use the \"htmlLink\" field if present\n" + '- Google Calendar: use the "htmlLink" field if present\n' "If no direct deep link is available, fall back to the most relevant URL" " found anywhere in the tool response.\n" - "Only omit \"url\" if there is truly no URL available in the response at all.\n\n" + 'Only omit "url" if there is truly no URL available in the response at all.\n\n' "Rules:\n" "- One json:task block per ACTIONABLE item (skip empty results, metadata, and errors)\n" "- Brief status text between blocks is fine\n" @@ -140,7 +140,7 @@ def _patch_tool_schemas(tools): def _extract_auth_url(content) -> str | None: - """Check if a tool result contains an Arcade authorization URL.""" + """Fallback: extract auth URL from tool output for gateways that don't support elicitation.""" if not isinstance(content, str): return None try: @@ -206,6 +206,16 @@ async def stream(): yield _encode_event({"type": "status", "message": "Connecting to Arcade Gateway..."}) + # Wire per-request elicitation events to the NDJSON stream. + # The elicitation callback fires during tool execution; we buffer events + # and drain them on each iteration of the stream loop. + elicitation_events: list[dict] = [] + set_elicitation_callback( + lambda eid, msg, url: elicitation_events.append( + {"type": "elicitation", "elicitationId": eid, "authUrl": url, "message": msg} + ) + ) + mcp_client = get_mcp_client() try: @@ -216,7 +226,8 @@ async def stream(): # Uses pattern matching so it works regardless of Arcade's exact naming # convention (underscores, dots, or mixed casing are all handled). _KNOWN_SERVICE = re.compile( - r"^(github|gmail|google|calendar|linear|slack)", re.IGNORECASE, + r"^(github|gmail|google|calendar|linear|slack)", + re.IGNORECASE, ) _MUTATION = re.compile( r"create|update|delete|send|reply|post|archive|remove" @@ -228,7 +239,15 @@ def _is_triage_tool(name: str) -> bool: if not _KNOWN_SERVICE.search(name): return False return not _MUTATION.search(name) - MAX_TOOL_RESULT_CHARS = 4000 + + MAX_TOOL_RESULT_CHARS = 2000 + + def _truncate(result) -> str: + """Serialize result to string and truncate if over the limit.""" + s = result if isinstance(result, str) else json.dumps(result) + if len(s) > MAX_TOOL_RESULT_CHARS: + return s[:MAX_TOOL_RESULT_CHARS] + f"\n...[truncated {len(s) - MAX_TOOL_RESULT_CHARS} chars]" + return s def _wrap_tool(tool): """Wrap a tool to truncate large results and prevent context overflow.""" @@ -237,26 +256,15 @@ def _wrap_tool(tool): async def _truncated_ainvoke(*args, **kwargs): result = await original_coroutine(*args, **kwargs) - if isinstance(result, str) and len(result) > MAX_TOOL_RESULT_CHARS: - return ( - result[:MAX_TOOL_RESULT_CHARS] - + f"\n...[truncated {len(result) - MAX_TOOL_RESULT_CHARS} chars]" - ) - return result + return _truncate(result) updates = {"coroutine": _truncated_ainvoke} if original_func is not None: + def _truncated_invoke(*args, **kwargs): result = original_func(*args, **kwargs) - s = result if isinstance(result, str) else ( - json.dumps(result) if result else "" - ) - if len(s) > MAX_TOOL_RESULT_CHARS: - return ( - s[:MAX_TOOL_RESULT_CHARS] - + f"\n...[truncated {len(s) - MAX_TOOL_RESULT_CHARS} chars]" - ) - return result + return _truncate(result) + updates["func"] = _truncated_invoke return tool.model_copy(update=updates) @@ -321,10 +329,17 @@ def _truncated_invoke(*args, **kwargs): "work, upcoming events, and unread messages." ) + # recursion_limit counts graph-node transitions; react_agent has + # 2 nodes (agent + tools), so limit 40 ≈ 20 LLM iterations. async for event in agent.astream( {"messages": [HumanMessage(content=user_message)]}, stream_mode="updates", + config={"recursion_limit": 40}, ): + # Drain any elicitation events that arrived during tool execution + while elicitation_events: + yield _encode_event(elicitation_events.pop(0)) + for node_name, update in event.items(): # Check for tool results with auth URLs if node_name == "tools" and "messages" in update: @@ -413,6 +428,8 @@ def _truncated_invoke(*args, **kwargs): } ) yield _encode_event({"type": "done"}) + finally: + set_elicitation_callback(None) return StreamingResponse( stream(), diff --git a/templates/langchain/app/static/dashboard.js b/templates/langchain/app/static/dashboard.js index 6be914e..ef2ee2e 100644 --- a/templates/langchain/app/static/dashboard.js +++ b/templates/langchain/app/static/dashboard.js @@ -8,8 +8,6 @@ const gateAuth = document.getElementById("gate-auth"); const gateAuthLink = document.getElementById("gate-auth-link"); const gateAuthCta = document.getElementById("gate-auth-cta"); const gateAuthWaiting = document.getElementById("gate-auth-waiting"); -const gateAuthRetry = document.getElementById("gate-auth-retry"); -const gateAuthWaitingRetry = document.getElementById("gate-auth-waiting-retry"); const gateError = document.getElementById("gate-error"); const gateErrorMsg = document.getElementById("gate-error-msg"); const dashboardArea = document.getElementById("dashboard-area"); @@ -120,8 +118,6 @@ function retryArcadeConnection() { } gateAuthLink.addEventListener("click", showGateAuthWaiting); -gateAuthRetry.addEventListener("click", retryArcadeConnection); -gateAuthWaitingRetry.addEventListener("click", retryArcadeConnection); checkArcadeConnection(); window.addEventListener("focus", () => { if (!dashboardArea.classList.contains("hidden")) return; @@ -129,6 +125,13 @@ window.addEventListener("focus", () => { retryArcadeConnection(); }); +// Re-check source statuses quickly after the user returns from an auth tab. +// visibilitychange fires as soon as the tab becomes visible (faster than focus). +document.addEventListener("visibilitychange", () => { + if (document.visibilityState !== "visible") return; + if (authPrompts.children.length) checkSourceStatuses(); +}); + // --- Logout --- logoutBtn.addEventListener("click", async () => { await fetch("/api/auth/logout", { method: "POST" }); @@ -314,9 +317,23 @@ function showState() { } // --- Auth prompts --- +let _sourceAuthPollId = null; +function startSourceAuthPolling() { + if (_sourceAuthPollId) return; + _sourceAuthPollId = setInterval(() => { + if (!authPrompts.children.length) { + clearInterval(_sourceAuthPollId); + _sourceAuthPollId = null; + return; + } + checkSourceStatuses(); + }, 3000); +} + function addAuthPrompt(url, toolName) { if (authPrompts.querySelector(`[data-url="${CSS.escape(url)}"]`)) return; authPrompts.classList.remove("hidden"); + startSourceAuthPolling(); const label = toolName || "Service"; const card = document.createElement("div"); diff --git a/templates/langchain/app/templates/dashboard.html b/templates/langchain/app/templates/dashboard.html index 0d9ba29..e0676e3 100644 --- a/templates/langchain/app/templates/dashboard.html +++ b/templates/langchain/app/templates/dashboard.html @@ -109,56 +109,32 @@

Connect to Arcade

href="#" target="_blank" rel="noopener noreferrer" - class="block w-full px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 text-sm font-medium transition-colors mb-3" + class="block w-full px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 text-sm font-medium transition-colors" > Sign in with Arcade -
-
+
- - - - - +
-

Why Arcade?

-

- The agent uses Arcade as an MCP Gateway to read from your tools on your behalf. - Signing in here links your Arcade identity so the gateway knows which user's tools - to access. -

+
+

Why sign into Arcade?

+ +
+

The agent uses Arcade as an MCP Gateway to read from your tools on your behalf. Signing in here links your Arcade identity so the gateway knows which user's tools to access.

Ready to triage? Your agent will scan your inbox, calendar, tasks, and PRs, then prioritize everything and build your action plan.

-
+
- - - - - +
-

How this works

-

- When you click "Plan my day", the agent connects to your Slack, Gmail, Google - Calendar, Linear, and GitHub through Arcade's MCP Gateway. It will first check if - any of those need authentication — if so, you'll be prompted to authorize before - the run starts. Then it reads your recent messages, emails, events, issues, and - pull requests, and classifies each item by priority and suggests next steps. -

+
+

How this works

+ +
+

When you click "Plan my day", the agent connects to your Slack, Gmail, Google Calendar, Linear, and GitHub through Arcade's MCP Gateway. It will first check if any of those need authentication — if so, you'll be prompted to authorize before the run starts. Then it reads your recent messages, emails, events, issues, and pull requests, and classifies each item by priority and suggests next steps.

@@ -271,31 +233,17 @@

Ready to triage?

-
+
- - - - - +
-

Two separate sign-ins

-

- This creates a local account for your agent app — it's just for session management and - stays in your own database. You'll connect your Arcade account on the next screen to - give the agent access to your tools. -

+
+

Two separate sign-ins

+ +
+

This creates a local account for your agent app — it's just for session management and stays in your own database. You'll connect your Arcade account on the next screen to give the agent access to your tools.

@@ -92,6 +80,15 @@
{% endblock %} {% block scripts %} + + + + + + + + {% block content %}{% endblock %} {% block scripts %}{% endblock %} + + + diff --git a/test-fresh-py/app/templates/dashboard.html b/test-fresh-py/app/templates/dashboard.html new file mode 100644 index 0000000..9400855 --- /dev/null +++ b/test-fresh-py/app/templates/dashboard.html @@ -0,0 +1,324 @@ +{% extends "base.html" %} {% block title %}Dashboard - Arcade Agent{% endblock %} {% block content +%} +
+ +
+
+
+ + Arcade Agent +
+ +
+
+ + +
+
+ +
+ +
+
+
+

Connecting to Arcade...

+
+ + +
+ + + +
+
+{% endblock %} {% block scripts %} + + +{% endblock %} diff --git a/test-fresh-py/app/templates/login.html b/test-fresh-py/app/templates/login.html new file mode 100644 index 0000000..23b9654 --- /dev/null +++ b/test-fresh-py/app/templates/login.html @@ -0,0 +1,154 @@ +{% extends "base.html" %} {% block title %}Login - Arcade Agent{% endblock %} {% block content %} +
+
+
+ + Arcade Agent +
+ +
+
+ +
+
+

Two separate sign-ins

+ +
+

This creates a local account for your agent app — it's just for session management and stays in your own database. You'll connect your Arcade account on the next screen to give the agent access to your tools.

+
+
+
+ +
+ +
+ + +
+ +

+ Get started with Arcade Agent +

+ +
+ + + + + + +
+
+
+
+{% endblock %} {% block scripts %} + + +{% endblock %} diff --git a/test-fresh-py/pyproject.toml b/test-fresh-py/pyproject.toml new file mode 100644 index 0000000..29db5ad --- /dev/null +++ b/test-fresh-py/pyproject.toml @@ -0,0 +1,5 @@ +[project] +name = "test-fresh-py" +version = "0.1.0" +description = "AI agent powered by LangGraph + Arcade" +requires-python = ">=3.10" diff --git a/test-fresh-py/requirements.txt b/test-fresh-py/requirements.txt new file mode 100644 index 0000000..5ed0983 --- /dev/null +++ b/test-fresh-py/requirements.txt @@ -0,0 +1,17 @@ +langchain-openai>=0.3 +langchain-anthropic>=0.3 +langchain-mcp-adapters>=0.2.2 +mcp>=1.27 +langgraph>=0.3 +fastapi>=0.115 +fastapi-users[sqlalchemy]>=14.0 +uvicorn[standard]>=0.34 +sqlalchemy[asyncio]>=2.0 +aiosqlite>=0.20 +python-dotenv>=1.0 +jinja2>=3.1 +sse-starlette>=2.0 +pydantic-settings>=2.0 +httpx>=0.27 +alembic>=1.14 +email-validator>=2.0 diff --git a/test-fresh-py/ruff.toml b/test-fresh-py/ruff.toml new file mode 100644 index 0000000..d33e0dc --- /dev/null +++ b/test-fresh-py/ruff.toml @@ -0,0 +1,25 @@ +line-length = 100 +target-version = "py312" + +[lint] +select = [ + "E", "W", # pycodestyle + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "S", # flake8-bandit (security) + "C4", # flake8-comprehensions + "SIM", # flake8-simplify + "UP", # pyupgrade + "RUF", # ruff-specific rules +] +ignore = [ + "S105", "S106", "S107", # hardcoded password — too many false positives + "B008", # Depends() in args is standard FastAPI +] + +[lint.per-file-ignores] +"alembic/**/*.py" = ["E501", "UP"] + +[format] +quote-style = "double" diff --git a/test-fresh-py/server.py b/test-fresh-py/server.py new file mode 100644 index 0000000..2636381 --- /dev/null +++ b/test-fresh-py/server.py @@ -0,0 +1,4 @@ +import uvicorn + +if __name__ == "__main__": + uvicorn.run("app.main:app", host="localhost", port=8765) diff --git a/test-fresh-py/ty.toml b/test-fresh-py/ty.toml new file mode 100644 index 0000000..d88018c --- /dev/null +++ b/test-fresh-py/ty.toml @@ -0,0 +1,12 @@ +[environment] +python-version = "3.12" + +[rules] +# Pydantic models use dynamic constructors with aliases (e.g. `model` vs `model_name`) +# that ty doesn't resolve yet. SQLAlchemy Column types also cause false positives. +unknown-argument = "ignore" +missing-argument = "ignore" +invalid-argument-type = "ignore" +# ty doesn't narrow types through hasattr() or isinstance+continue patterns +no-matching-overload = "ignore" +not-iterable = "ignore" diff --git a/test-mastra/.env.example b/test-mastra/.env.example new file mode 100644 index 0000000..883850e --- /dev/null +++ b/test-mastra/.env.example @@ -0,0 +1,29 @@ +# --- Arcade AI --- +# Your MCP Gateway URL from https://app.arcade.dev/mcp-gateways +ARCADE_GATEWAY_URL= + +# --- LLM Provider (set one or both) --- +# Get from https://platform.openai.com +OPENAI_API_KEY= +# Get from https://console.anthropic.com +ANTHROPIC_API_KEY= + +# --- App --- +PORT=8765 + +# --- App --- +# If using ARCADE_CUSTOM_VERIFIER, set this to your public URL (e.g. your ngrok URL). +# The verify route redirects users back here after authorization — it must match the host +# you're accessing the app from, or the session cookie won't be found. +NEXT_PUBLIC_APP_URL=http://localhost:8765 + +# --- Database --- +DATABASE_URL=local.db # SQLite file path (default: local.db) + +# --- Auth (Better Auth) --- +BETTER_AUTH_SECRET= # Random secret for session signing; generate with: openssl rand -hex 32 +BETTER_AUTH_URL=http://localhost:8765 # Must match NEXT_PUBLIC_APP_URL + +# --- Custom User Verification (optional, recommended for production) --- +# ARCADE_CUSTOM_VERIFIER=true +# ARCADE_API_KEY= # Get from https://app.arcade.dev/settings diff --git a/test-mastra/.gitignore b/test-mastra/.gitignore new file mode 100644 index 0000000..623cc09 --- /dev/null +++ b/test-mastra/.gitignore @@ -0,0 +1,41 @@ +# dependencies +/node_modules +/.pnp +.pnp.* + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* + +# env files +.env* +!.env.example + +# database +*.db + +# drizzle +drizzle/migrations/ + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# arcade auth tokens +.arcade-auth/ diff --git a/test-mastra/.prettierignore b/test-mastra/.prettierignore new file mode 100644 index 0000000..670ba26 --- /dev/null +++ b/test-mastra/.prettierignore @@ -0,0 +1,3 @@ +.next/ +node_modules/ +*.lock diff --git a/test-mastra/.prettierrc b/test-mastra/.prettierrc new file mode 100644 index 0000000..a4a2d60 --- /dev/null +++ b/test-mastra/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "es5", + "printWidth": 100, + "tabWidth": 2 +} diff --git a/test-mastra/AGENT_PLAYBOOK.md b/test-mastra/AGENT_PLAYBOOK.md new file mode 100644 index 0000000..fc70a00 --- /dev/null +++ b/test-mastra/AGENT_PLAYBOOK.md @@ -0,0 +1,36 @@ +# Agent Playbook + +This project is intentionally structured so coding agents can safely customize it. + +## Safe Edit Zones + +Look for these markers: + +- `CUSTOMIZATION POINT` — expected user customization area +- `AI-EDIT-SAFE` — safe for automated edits +- `AI-EDIT-CAUTION` — integration-sensitive; edit carefully + +## First Customization Steps + +1. Edit `src/mastra/agents/system-prompt.md` to change agent behavior. +2. Change model choice in `src/mastra/agents/triage-agent.ts`. +3. Extend schema in `lib/db/schema.ts` if you need app-specific data. +4. Ensure `ARCADE_GATEWAY_URL` is set and the gateway has the expected tools. + +## Gateway Checklist + +Create/configure your gateway at `https://app.arcade.dev/mcp-gateways` and add: + +- Slack +- Google Calendar +- Linear +- GitHub +- Gmail + +## Verification Commands + +```bash +bun run doctor +bun run typecheck +bun run lint +``` diff --git a/test-mastra/CLAUDE.md b/test-mastra/CLAUDE.md new file mode 100644 index 0000000..fa70dc1 --- /dev/null +++ b/test-mastra/CLAUDE.md @@ -0,0 +1,73 @@ +# Arcade Agent Template + +AI agent template: Mastra + Next.js + Arcade MCP Gateway. + +## Key Commands + +```bash +bun run dev # Start development server +bun run build # Production build +bun run lint # ESLint +bun run doctor # Environment + gateway setup checks +bun run format # Prettier format +bun run format:check # Prettier check +bunx drizzle-kit generate # Generate DB migrations after schema changes +bunx drizzle-kit migrate # Apply migrations to SQLite +``` + +## Architecture + +- **Mastra** (`@mastra/core`) — agent framework. Agent defined in `src/mastra/agents/triage-agent.ts`. +- **MCPClient** (`@mastra/mcp`) — connects to Arcade MCP Gateway for tool discovery + execution. Uses Arcade OAuth (not header auth). Defined in `src/mastra/tools/arcade.ts`. +- **`@mastra/ai-sdk`** — bridges Mastra streams to Vercel AI SDK format. `handleChatStream` is used in the API route. +- **Drizzle ORM** + `better-sqlite3` — SQLite for auth storage (Better Auth tables). +- **[Better Auth](https://www.better-auth.com)** — email/password auth with session cookies (`lib/auth.ts`, `lib/auth-client.ts`). +- **`useChat`** from `@ai-sdk/react` — frontend streaming chat hook. + +## Key Files + +| File | Purpose | +| --------------------------------------- | ------------------------------------------------------------------ | +| `src/mastra/agents/triage-agent.ts` | Agent definition with system prompt + model + tools | +| `src/mastra/tools/arcade.ts` | MCPClient with custom OAuthClientProvider (file-based persistence) | +| `src/mastra/index.ts` | Mastra instance registration | +| `app/api/chat/route.ts` | Streaming chat endpoint (auth-protected) | +| `app/api/auth/arcade/callback/route.ts` | OAuth callback for Arcade MCP authentication | +| `app/api/auth/arcade/connect/route.ts` | Pre-flight connection check (surfaces auth URL to frontend) | +| `app/api/auth/arcade/verify/route.ts` | Custom user verifier for COAT protection (opt-in via env var) | +| `lib/auth.ts` | Better Auth server config + `getSession()` helper | +| `lib/auth-client.ts` | Better Auth React client (signIn, signUp, signOut) | +| `lib/db/schema.ts` | Drizzle schema (Better Auth tables + custom tables) | +| `app/dashboard/page.tsx` | Daily triage dashboard (main UI entry point) | +| `app/page.tsx` | Login/register form | + +## Auth + +Three auth layers: + +1. **App auth** — [Better Auth](https://www.better-auth.com) email/password authentication with SQLite sessions via Drizzle adapter (`lib/auth.ts`). Configure `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL` in `.env`. Protects the chat endpoint so only registered users can access it. +2. **Arcade OAuth** — custom `OAuthClientProvider` implementation (`src/mastra/tools/arcade.ts`). Authenticates the MCP connection to Arcade Gateway. No API keys needed — the user authenticates via browser. Tokens persist in `.arcade-auth/` (gitignored). The OAuth callback is at `/api/auth/arcade/callback`. +3. **Custom user verifier** (optional) — `/api/auth/arcade/verify`. When `ARCADE_CUSTOM_VERIFIER=true`, binds Arcade tool authorizations to the app's user session, preventing COAT attacks. Requires `ARCADE_API_KEY`. Enabling the custom verifier also requires: (a) setting up custom OAuth applications with each auth provider (Slack, GitHub, etc.) in the Arcade dashboard — Arcade's default shared OAuth apps cannot be used with a custom verifier, and (b) exposing the local dev server via ngrok (`ngrok http 3000`) so Arcade can reach the verifier endpoint, then configuring the ngrok URL in the Arcade dashboard. When using ngrok: set `NEXT_PUBLIC_APP_URL` to the ngrok URL, delete `.arcade-auth/` (cached OAuth registration has the old callback URL), and restart the dev server. + +## Constraints + +- `next.config.ts` uses `serverExternalPackages` for `better-sqlite3` and `@mastra/core` (native modules). +- Tools are loaded lazily via a dynamic function (`async () => mcpClient.listTools()`) to avoid MCP connection at build time. +- No `@arcadeai/arcadejs` — all Arcade interaction goes through MCP Gateway via `@mastra/mcp`. + +## Customization Points + +Files with `// --- CUSTOMIZATION POINT ---` comments: + +- `src/mastra/agents/triage-agent.ts` — agent purpose, model, system prompt +- `src/mastra/tools/arcade.ts` — gateway URL, OAuth config +- `src/mastra/index.ts` — agent registration + +## Claude Code Notes + +- **Main UI**: `app/dashboard/page.tsx` — layout, plan-run flow, and ChatPanel toggle. This is the primary entry point after login. +- **Agent logic**: `src/mastra/agents/triage-agent.ts` — system prompt, model, and tool list. +- **Component library**: import UI components from `@arcadeai/design-system`; import brand icons (Slack, GitHub, Gmail, etc.) from `@arcadeai/design-system/components/ui/atoms/icons`. +- **Startup checks**: add new env-var warnings in `app/api/health/route.ts` — push a new `ConfigWarning` object to the `warnings` array. +- **Safe to edit**: `src/mastra/agents/system-prompt.md`, `src/mastra/agents/triage-agent.ts` (model/prompt), `lib/db/schema.ts` (schema extensions). +- **Edit with care**: `src/mastra/tools/arcade.ts` — custom `OAuthClientProvider` for MCP OAuth. Token persistence (`.arcade-auth/`) and PKCE are stateful; read the full flow before changing it. diff --git a/test-mastra/README.md b/test-mastra/README.md new file mode 100644 index 0000000..0dfbde4 --- /dev/null +++ b/test-mastra/README.md @@ -0,0 +1,230 @@ +# test-mastra + +A reference AI agent template that ships with a working Slack triage use case. Built with [Mastra](https://mastra.ai), [Next.js](https://nextjs.org), and [Arcade](https://arcade.dev). + +The agent connects to Arcade's MCP Gateway to call Slack tools (and any other tools you configure), with automatic OAuth handling and streaming responses. + +## What Is an MCP Gateway? + +An MCP Gateway is a managed tool endpoint in Arcade that your agent connects to via one URL. + +Benefits: + +- **One connection point** — use one `ARCADE_GATEWAY_URL` instead of wiring many tool servers +- **Tool curation** — choose exactly which tools your agent can see +- **Faster iteration** — update tool access in Arcade without changing integration code +- **Cleaner model context** — smaller, focused toolsets improve tool selection reliability +- **Portable setup** — same gateway pattern works across frameworks and MCP clients + +## Prerequisites + +- [Bun](https://bun.sh) (or Node.js 18+) +- [Arcade account](https://app.arcade.dev) + MCP Gateway URL +- [OpenAI API key](https://platform.openai.com) or [Anthropic API key](https://console.anthropic.com) + +## Quick Start + +1. **Install dependencies:** + ```bash + bun install + ``` + +2. **Configure environment:** + ```bash + cp .env.example .env + ``` + Fill in your `ARCADE_GATEWAY_URL`, at least one LLM API key, and generate a `BETTER_AUTH_SECRET` in `.env`: + ```bash + echo "BETTER_AUTH_SECRET=$(openssl rand -hex 32)" >> .env + ``` + +3. **Run setup doctor:** + ```bash + bun run doctor + ``` + +4. **Set up the database:** + ```bash + bunx drizzle-kit generate + bunx drizzle-kit migrate + ``` + +5. **Start the dev server:** + ```bash + bun run dev + ``` + +6. **Open [http://localhost:8765](http://localhost:8765)**, register an account, and run your first plan from the dashboard. + +When the agent scans your tools it will prompt you to authorize each one (Slack, GitHub, etc.) via OAuth in your browser. After authorizing each service, re-run the plan. + +## Arcade Gateway Setup Checklist + +1. Create a gateway at [app.arcade.dev/mcp-gateways](https://app.arcade.dev/mcp-gateways). +2. Add only these minimum tools (exact names): + - Slack: `Slack_ListConversations`, `Slack_GetMessages`, `Slack_GetConversationMetadata`, `Slack_WhoAmI` + - Google Calendar: `GoogleCalendar_ListEvents`, `GoogleCalendar_ListCalendars`, `GoogleCalendar_WhoAmI` + - Linear: `Linear_GetNotifications`, `Linear_GetRecentActivity`, `Linear_ListIssues`, `Linear_GetIssue`, `Linear_ListProjects`, `Linear_GetProject`, `Linear_WhoAmI` + - GitHub: `Github_ListNotifications`, `Github_GetNotificationSummary`, `Github_ListPullRequests`, `Github_GetPullRequest`, `Github_GetUserOpenItems`, `Github_GetUserRecentActivity`, `Github_GetReviewWorkload`, `Github_GetIssue`, `Github_WhoAmI` + - Gmail: `Gmail_ListEmails`, `Gmail_ListThreads`, `Gmail_GetThread`, `Gmail_SearchThreads`, `Gmail_WhoAmI` +3. Avoid broad "all tools" access. Smaller toolsets improve tool selection quality. +4. Copy the gateway URL into `.env` as `ARCADE_GATEWAY_URL`. +5. Retry the connection check in the app. + +If you see `ARCADE_GATEWAY_URL is missing`, your app cannot connect to Arcade until this value is set. + +## Customization + +### Change the agent's purpose + +Edit `src/mastra/agents/system-prompt.md` to change what the agent does (PR reviews, calendar management, email drafting, etc.). The agent definition in `src/mastra/agents/triage-agent.ts` has `// --- CUSTOMIZATION POINT ---` markers for model selection and other settings. + +### Add or change Arcade tools + +1. Go to [app.arcade.dev/mcp-gateways](https://app.arcade.dev/mcp-gateways) +2. Add tools to your gateway (Gmail, GitHub, Google Calendar, etc.) +3. Update `ARCADE_GATEWAY_URL` in `.env` if needed + +The agent automatically discovers all tools available on your gateway. + +### Switch LLM provider + +The template auto-detects which provider to use based on which API key is set in `.env`: +- Set `ANTHROPIC_API_KEY` → uses Claude +- Set `OPENAI_API_KEY` → uses GPT-4o +- If both are set, Anthropic takes priority + +You can also manually configure the model in `src/mastra/agents/triage-agent.ts`. + +### Modify the database + +Edit `lib/db/schema.ts` to add tables, then regenerate migrations: + +```bash +npx drizzle-kit generate +npx drizzle-kit migrate +``` + +### Production considerations + +This template uses app-level token storage — all users share the same Arcade Gateway connection via a single `.arcade-auth/` directory. For production deployments: + +- **Per-user tokens**: Store OAuth tokens in the database keyed by user ID +- **Per-session PKCE**: Associate PKCE verifiers with user sessions to prevent cross-session conflicts +- **Token refresh**: Implement automatic token refresh per-user + +## Project Structure + +``` +├── app/ +│ ├── page.tsx # Login/register page +│ ├── layout.tsx # Root layout +│ ├── globals.css # Tailwind + CSS vars +│ ├── dashboard/ +│ │ └── page.tsx # Daily triage dashboard (main UI) +│ └── api/ +│ ├── chat/route.ts # Agent streaming endpoint +│ ├── plan/route.ts # Daily plan generation (SSE stream) +│ ├── sources/route.ts # Tool auth status check +│ ├── health/route.ts # Startup env-var validation +│ └── auth/ +│ ├── arcade/ +│ │ ├── callback/route.ts # Arcade OAuth callback +│ │ ├── connect/route.ts # Pre-flight connection check +│ │ └── verify/route.ts # Custom user verifier (optional) +│ └── [...all]/route.ts # Better Auth catch-all handler +├── src/mastra/ +│ ├── index.ts # Mastra instance +│ ├── agents/ +│ │ ├── triage-agent.ts # Agent definition (customization point) +│ │ └── system-prompt.md # System prompt (customization point) +│ └── tools/ +│ └── arcade.ts # MCP Gateway connection + OAuth provider +├── lib/ +│ ├── auth.ts # Better Auth server config + getSession helper +│ ├── auth-client.ts # Better Auth React client (signIn, signUp, signOut) +│ └── db/ +│ ├── index.ts # Drizzle client +│ └── schema.ts # Database schema +├── .env.example # Environment variable template +├── drizzle.config.ts # Drizzle Kit configuration +└── next.config.ts # Next.js configuration +``` + +## How It Works + +1. **Mastra agent** (`src/mastra/agents/triage-agent.ts`) defines the AI agent with a system prompt and tools +2. **MCPClient** (`src/mastra/tools/arcade.ts`) connects to Arcade's MCP Gateway using OAuth authentication — no API keys needed +3. **Chat API route** (`app/api/chat/route.ts`) streams agent responses using `handleChatStream` from `@mastra/ai-sdk` +4. **Dashboard** (`app/dashboard/page.tsx`) displays the triage results; a slide-over `ChatPanel` uses `useChat` from `@ai-sdk/react` for follow-up questions +5. **Auth layer** (`lib/auth.ts`) uses [Better Auth](https://www.better-auth.com) for email/password authentication with httpOnly cookie sessions backed by SQLite + +When Arcade tools require OAuth authorization (e.g., Slack access), the gateway returns an authorization URL. The agent relays it to the user, who authorizes in a new tab, then clicks "Continue After Authorization" to retry. + +## Production Security: Custom User Verification + +By default, anyone who has the Arcade authorization link can complete the OAuth flow. In production with multiple users, this opens a [COAT attack](https://www.arcade.dev/blog/arcade-proactively-addressed-coat-vulnerability-in-agentic-ai) vector — an attacker could send an auth link to a victim, and if the victim completes it, the attacker gains access to the victim's account. + +The **custom user verifier** solves this by confirming that the person completing the authorization is the same user who started it, using your app's session. + +### Setup + +1. **Enable in `.env`:** + ``` + ARCADE_CUSTOM_VERIFIER=true + ARCADE_API_KEY=your-arcade-api-key # From https://app.arcade.dev/settings + ``` + +2. **Configure in Arcade Dashboard:** + - Go to your gateway settings at [app.arcade.dev/mcp-gateways](https://app.arcade.dev/mcp-gateways) + - Under **Auth > Settings**, set the custom verifier URL to: + ``` + {your-app-url}/api/auth/arcade/verify + ``` + +3. **Set up custom OAuth apps for each auth provider:** + When you enable a custom user verifier, Arcade's default shared OAuth applications can no longer be used. You must register your own OAuth application with each provider you want to use (e.g., Slack, GitHub, Google) and configure them in the Arcade dashboard under your gateway's auth provider settings. See the [Arcade Custom Auth Provider docs](https://docs.arcade.dev/en/guides/user-facing-agents/secure-auth-production) for details. + +4. **Expose your local server for development (ngrok):** + Arcade must be able to reach your verifier endpoint over the internet, so `localhost` URLs won't work in the dashboard. Use [ngrok](https://ngrok.com/) to create a public tunnel: + ```bash + # Install ngrok: https://ngrok.com/download + ngrok http 8765 + ``` + ngrok will print a forwarding URL like `https://abc123.ngrok-free.app`. Use that as your verifier URL in the Arcade dashboard: + ``` + https://abc123.ngrok-free.app/api/auth/arcade/verify + ``` + > **Tip:** The free ngrok URL changes every time you restart it. Use `ngrok http 8765 --url=your-static-domain.ngrok-free.app` if you have a static domain configured in your ngrok account, so the dashboard URL stays stable. + + After starting ngrok, update your `.env`: + ``` + NEXT_PUBLIC_APP_URL=https://abc123.ngrok-free.app + ``` + Then delete `.arcade-auth/` (the cached OAuth registration contains the old callback URL) and restart the dev server. + + > **Important:** You must also **open and log in to the app via the ngrok URL** (not `localhost`). Session cookies are scoped to the host that sets them. If you log in at `localhost` but Arcade redirects you to the ngrok verify URL, the browser won't send the `localhost` cookie — verification will silently fail and redirect you back to the dashboard with an error. + +5. **Test it:** When a tool requires authorization, Arcade will redirect the user through your verifier endpoint. The endpoint checks the user's session, confirms their identity with Arcade, and redirects them back. + +### Troubleshooting + +**`verify_session_required` error or redirect loop back to login:** +The session cookie doesn't match the host the verify request came in on. Make sure you logged in via `NEXT_PUBLIC_APP_URL` (the ngrok URL), not `localhost`. + +**Repeated `400 Bad Request` from Arcade even after fixing the above:** +Arcade caches a pending auth flow server-side. After too many failed verify attempts, the same `flow_id` keeps being returned by WhoAmI but it's no longer valid. To get a fresh `flow_id`: delete `.arcade-auth/`, restart the dev server, re-authenticate the gateway, then try authorizing the tool again from the dashboard. + +For full details, see the [Arcade Secure Auth Guide](https://docs.arcade.dev/en/guides/user-facing-agents/secure-auth-production). + +## Environment Variables + +| Variable | Required | Description | +|---|---|---| +| `ARCADE_GATEWAY_URL` | Yes | MCP Gateway URL from [app.arcade.dev/mcp-gateways](https://app.arcade.dev/mcp-gateways) | +| `OPENAI_API_KEY` | One of these | OpenAI API key | +| `ANTHROPIC_API_KEY` | One of these | Anthropic API key | +| `NEXT_PUBLIC_APP_URL` | No | App URL for OAuth redirects (default: `http://localhost:8765`). When using `ARCADE_CUSTOM_VERIFIER`, set this to your public URL (e.g. ngrok URL) and access the app from that URL — session cookies are scoped to this host. | +| `DATABASE_URL` | No | SQLite file path (default: `local.db`) | +| `ARCADE_CUSTOM_VERIFIER` | No | Set to `true` to enable COAT protection (see below) | +| `ARCADE_API_KEY` | When verifier enabled | Arcade API key for user verification | diff --git a/test-mastra/app/api/auth/[...all]/route.ts b/test-mastra/app/api/auth/[...all]/route.ts new file mode 100644 index 0000000..7cbe91b --- /dev/null +++ b/test-mastra/app/api/auth/[...all]/route.ts @@ -0,0 +1,4 @@ +import { auth } from "@/lib/auth"; +import { toNextJsHandler } from "better-auth/next-js"; + +export const { POST, GET } = toNextJsHandler(auth); diff --git a/test-mastra/app/api/auth/arcade/callback/route.ts b/test-mastra/app/api/auth/arcade/callback/route.ts new file mode 100644 index 0000000..5903758 --- /dev/null +++ b/test-mastra/app/api/auth/arcade/callback/route.ts @@ -0,0 +1,43 @@ +import { NextResponse } from "next/server"; +import { auth, oauthProvider } from "@/src/mastra/tools/arcade"; + +/** Redirect using NEXT_PUBLIC_APP_URL when set (for ngrok/proxies), otherwise req.url. */ +function appRedirect(reqUrl: string, path: string): NextResponse { + const base = process.env.NEXT_PUBLIC_APP_URL + ? process.env.NEXT_PUBLIC_APP_URL.replace(/\/$/, "") + : new URL(reqUrl).origin; + return NextResponse.redirect(`${base}${path}`); +} + +export async function GET(req: Request) { + const gatewayUrl = process.env.ARCADE_GATEWAY_URL?.trim(); + if (!gatewayUrl) { + return appRedirect(req.url, "/dashboard?error=gateway_missing"); + } + + const url = new URL(req.url); + const code = url.searchParams.get("code"); + + if (!code) { + return NextResponse.json( + { error: "Missing authorization code" }, + { status: 400 } + ); + } + + try { + const result = await auth(oauthProvider, { + serverUrl: gatewayUrl, + authorizationCode: code, + }); + + if (result === "AUTHORIZED") { + return appRedirect(req.url, "/dashboard"); + } + + return appRedirect(req.url, "/dashboard?error=auth_incomplete"); + } catch (error) { + console.error("Arcade OAuth callback error:", error); + return appRedirect(req.url, "/dashboard?error=auth_failed"); + } +} diff --git a/test-mastra/app/api/auth/arcade/connect/route.ts b/test-mastra/app/api/auth/arcade/connect/route.ts new file mode 100644 index 0000000..a93d77e --- /dev/null +++ b/test-mastra/app/api/auth/arcade/connect/route.ts @@ -0,0 +1,130 @@ +import { + oauthProvider, + getArcadeMCPClient, + getPendingAuthUrl, + clearPendingAuthUrl, + initiateOAuth, +} from "@/src/mastra/tools/arcade"; +import { getSession } from "@/lib/auth"; + +// Serialize concurrent connect attempts to prevent PKCE verifier overwrites +let connectPromise: Promise<{ + data: Record; + status?: number; +}> | null = null; + +export async function POST() { + const session = await getSession(); + if (!session) { + return Response.json({ error: "Unauthorized" }, { status: 401 }); + } + + if (!process.env.ARCADE_GATEWAY_URL?.trim()) { + return Response.json( + { + connected: false, + error: + "ARCADE_GATEWAY_URL is missing. Create one at https://app.arcade.dev/mcp-gateways, add only the minimum required tools from Slack, Google Calendar, Linear, GitHub, and Gmail, then set ARCADE_GATEWAY_URL in .env.", + }, + { status: 400 } + ); + } + + // Fast path: tokens already on disk. + const existingTokens = oauthProvider.tokens(); + if (existingTokens?.access_token) { + const connStatus = await verifyExistingConnection(); + if (connStatus === "ok") return Response.json({ connected: true }); + if (connStatus === "unreachable") { + // Don't trigger a new OAuth redirect for transient connectivity failures — + // that would force the user to re-authorize when the gateway is just briefly down. + return Response.json( + { + connected: false, + error: "Cannot reach Arcade Gateway. Check ARCADE_GATEWAY_URL and try again.", + }, + { status: 502 } + ); + } + // "needs_reauth": tokens invalid/expired — fall through to OAuth + } + + if (!connectPromise) { + connectPromise = doConnect().finally(() => { + connectPromise = null; + }); + } + + const result = await connectPromise; + return Response.json(result.data, result.status ? { status: result.status } : undefined); +} + +async function doConnect(): Promise<{ + data: Record; + status?: number; +}> { + let mcpClient: Awaited> | null = null; + try { + // Trigger MCP OAuth flow (discovery, registration, PKCE) + const result = await initiateOAuth(); + + if (result === "REDIRECT") { + const authUrl = getPendingAuthUrl(); + if (authUrl) { + clearPendingAuthUrl(); + return { data: { connected: false, authUrl } }; + } + } + + // AUTHORIZED — verify by listing tools + mcpClient = await getArcadeMCPClient(); + const tools = await mcpClient.tools(); + return { + data: { connected: true, toolCount: Object.keys(tools).length }, + }; + } catch { + const authUrl = getPendingAuthUrl(); + if (authUrl) { + clearPendingAuthUrl(); + return { data: { connected: false, authUrl } }; + } + + return { + data: { + connected: false, + error: + "Could not connect to Arcade Gateway. Check that ARCADE_GATEWAY_URL is set correctly in your .env file.", + }, + status: 502, + }; + } finally { + if (mcpClient) { + try { + await mcpClient.close(); + } catch { + // Ignore close errors from failed initialization attempts. + } + } + } +} + +async function verifyExistingConnection(): Promise<"ok" | "needs_reauth" | "unreachable"> { + let mcpClient: Awaited> | null = null; + try { + mcpClient = await getArcadeMCPClient(); + await mcpClient.tools(); + return "ok"; + } catch (e) { + const msg = e instanceof Error ? e.message : String(e); + if (/401|403|unauthorized|forbidden/i.test(msg)) return "needs_reauth"; + return "unreachable"; + } finally { + if (mcpClient) { + try { + await mcpClient.close(); + } catch { + // Best effort cleanup. + } + } + } +} diff --git a/test-mastra/app/api/auth/arcade/verify/route.ts b/test-mastra/app/api/auth/arcade/verify/route.ts new file mode 100644 index 0000000..03b5326 --- /dev/null +++ b/test-mastra/app/api/auth/arcade/verify/route.ts @@ -0,0 +1,143 @@ +/** + * Custom User Verifier — COAT Attack Protection + * + * WHAT: This endpoint confirms that the person completing an Arcade tool + * authorization (e.g. granting Slack access) is the same user who initiated it + * from your app. + * + * WHY: Without this, the OAuth authorization link is a bearer token — anyone + * who has it can complete the flow. An attacker could start a tool auth, + * send the link to a victim, and if the victim clicks it, the attacker gains + * access to the victim's account. This is called a COAT attack (Cross-app + * OAuth Account Takeover). See: https://www.arcade.dev/blog/arcade-proactively-addressed-coat-vulnerability-in-agentic-ai + * + * HOW IT WORKS: + * 1. User triggers a tool that needs authorization (e.g. Slack) + * 2. Arcade redirects the user's browser here with a `flow_id` query param + * 3. This endpoint checks the user's app session (they must be logged in) + * 4. Calls Arcade's confirm_user API with the flow_id + the user's identity + * 5. Arcade verifies the match and redirects the user back + * + * SETUP: + * 1. Set ARCADE_CUSTOM_VERIFIER=true and ARCADE_API_KEY in your .env + * 2. In the Arcade dashboard (app.arcade.dev/mcp-gateways), under + * Auth > Settings, set the custom verifier URL to: + * {your-app-url}/api/auth/arcade/verify + * 3. Register custom OAuth apps for each auth provider (Slack, GitHub, etc.) + * in the Arcade dashboard. Arcade's default shared OAuth apps cannot be + * used when a custom verifier is enabled. + * 4. For local dev, use ngrok to expose your server (`ngrok http 8765`) and + * set the ngrok URL as the verifier URL in the Arcade dashboard. + * IMPORTANT: You must also access your app via the ngrok URL (not localhost). + * Session cookies are scoped to the host that set them — if you log in through + * localhost, the ngrok-fronted verify request won't carry the cookie, causing + * a silent redirect loop. Login at https://.ngrok-free.app instead. + * 5. Full guide: https://docs.arcade.dev/en/guides/user-facing-agents/secure-auth-production + * + * This endpoint is disabled by default. It returns 404 unless + * ARCADE_CUSTOM_VERIFIER=true is set in your environment. + */ + +import { NextResponse } from "next/server"; +import { getSession } from "@/lib/auth"; + +const ARCADE_API_URL = "https://cloud.arcade.dev/api/v1/oauth/confirm_user"; + +/** Redirect using NEXT_PUBLIC_APP_URL when set (for ngrok/proxies), otherwise req.url. */ +function appRedirect(reqUrl: string, path: string): NextResponse { + const base = process.env.NEXT_PUBLIC_APP_URL + ? process.env.NEXT_PUBLIC_APP_URL.replace(/\/$/, "") + : new URL(reqUrl).origin; + return NextResponse.redirect(`${base}${path}`); +} + +export async function GET(req: Request) { + // Feature gate: only active when explicitly enabled + if (process.env.ARCADE_CUSTOM_VERIFIER !== "true") { + return NextResponse.json( + { + error: + "Custom user verification is not enabled. Set ARCADE_CUSTOM_VERIFIER=true in your .env to activate it.", + }, + { status: 404 } + ); + } + + const apiKey = process.env.ARCADE_API_KEY; + if (!apiKey) { + console.error("ARCADE_CUSTOM_VERIFIER is enabled but ARCADE_API_KEY is not set."); + return appRedirect(req.url, "/dashboard?error=verify_misconfigured"); + } + + const url = new URL(req.url); + const flowId = url.searchParams.get("flow_id"); + + if (!flowId) { + return NextResponse.json({ error: "Missing flow_id parameter" }, { status: 400 }); + } + + // Verify the user is logged into this app. + // NOTE: In local dev, you must access the app via NEXT_PUBLIC_APP_URL (the ngrok URL), + // NOT localhost. The session cookie is scoped to the host that set it — if you log in + // through localhost, your ngrok-proxied verify request won't carry the cookie. + const user = await getSession(); + if (!user) { + console.warn( + "[verify] No session found. If you're using ngrok, make sure you logged in through " + + "the ngrok URL (not localhost) so the session cookie is scoped to the same host." + ); + return appRedirect(req.url, "/dashboard?error=verify_session_required"); + } + + const requestBody = { flow_id: flowId, user_id: user.email }; + + try { + const response = await fetch(ARCADE_API_URL, { + method: "POST", + headers: { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(requestBody), + }); + + if (!response.ok) { + const body = await response.text(); + console.error( + "Arcade confirm_user failed:", + response.status, + body, + "\n flow_id:", + flowId, + "\n user_id:", + user.email, + "\n endpoint:", + ARCADE_API_URL + ); + return appRedirect(req.url, "/dashboard?error=verify_failed"); + } + + const data = await response.json(); + + // Redirect to Arcade's next_uri if provided and same-origin, otherwise dashboard + const base = process.env.NEXT_PUBLIC_APP_URL + ? process.env.NEXT_PUBLIC_APP_URL.replace(/\/$/, "") + : new URL(req.url).origin; + const baseUrl = new URL(base); + let redirectTo = `${base}/dashboard`; + if (data.next_uri) { + try { + const nextUrl = new URL(data.next_uri, base); + if (nextUrl.origin === baseUrl.origin) { + redirectTo = nextUrl.toString(); + } + } catch { + // Invalid URL — fall through to dashboard + } + } + return NextResponse.redirect(redirectTo); + } catch (error) { + console.error("Arcade verify error:", error); + return appRedirect(req.url, "/dashboard?error=verify_failed"); + } +} diff --git a/test-mastra/app/api/health/route.ts b/test-mastra/app/api/health/route.ts new file mode 100644 index 0000000..4267740 --- /dev/null +++ b/test-mastra/app/api/health/route.ts @@ -0,0 +1,30 @@ +import type { ConfigWarning } from "@/types/dashboard"; + +export async function GET() { + const warnings: ConfigWarning[] = []; + + if (!process.env.ANTHROPIC_API_KEY && !process.env.OPENAI_API_KEY) { + warnings.push({ + id: "llm_key", + title: "No LLM API key configured", + message: + "Set ANTHROPIC_API_KEY in .env to use Claude (get one at console.anthropic.com), " + + "or set OPENAI_API_KEY to use GPT-4 (platform.openai.com). " + + "The agent will fail when you try to run a plan.", + docsUrl: "https://console.anthropic.com", + }); + } + + if (!process.env.ARCADE_GATEWAY_URL?.trim()) { + warnings.push({ + id: "gateway_url", + title: "ARCADE_GATEWAY_URL is not set", + message: + "Create an MCP Gateway at app.arcade.dev/mcp-gateways — add tools for Slack, " + + "Google Calendar, Linear, GitHub, and Gmail — then add ARCADE_GATEWAY_URL= to .env.", + docsUrl: "https://app.arcade.dev/mcp-gateways", + }); + } + + return Response.json({ warnings }); +} diff --git a/test-mastra/app/api/plan/route.ts b/test-mastra/app/api/plan/route.ts new file mode 100644 index 0000000..323153c --- /dev/null +++ b/test-mastra/app/api/plan/route.ts @@ -0,0 +1,443 @@ +/** + * Plan API Route — Daily Planning Agent + * + * POST /api/plan + * + * Triggers the triage agent to scan connected services (Slack, Calendar, + * Linear, GitHub, Gmail, etc.), classify each item, and stream back + * structured InboxItem data as NDJSON. + */ + +import { Agent } from "@mastra/core/agent"; +import { getSession } from "@/lib/auth"; +import { getModel } from "@/src/mastra/agents/triage-agent"; +import { mcpClient, setElicitationCallback } from "@/src/mastra/tools/arcade"; +import type { InboxItem } from "@/types/inbox"; + +export const maxDuration = 60; + +// --- Types --- + +type PlanEvent = + | { type: "status"; message: string } + | { type: "task"; data: InboxItem } + | { type: "summary"; data: { total: number; bySource: Record } } + | { type: "auth_required"; authUrl: string; toolName?: string } + | { type: "elicitation"; elicitationId: string; authUrl: string; message: string } + | { type: "sources"; sources: string[] } + | { type: "error"; message: string } + | { type: "done" }; + +function encodeEvent(event: PlanEvent): Uint8Array { + return new TextEncoder().encode(JSON.stringify(event) + "\n"); +} + +import { mapToolToSource } from "@/lib/sources"; + +// Fallback: extract auth URL from tool output for gateways that don't support elicitation +function extractAuthUrlFromToolOutput(output: unknown): string | null { + const looksLikeAuthUrl = (value: unknown): value is string => + typeof value === "string" && + /oauth|authorize/i.test(value); + + const fromRecord = (value: unknown): string | null => { + if (!value || typeof value !== "object") return null; + const obj = value as Record; + const direct = + (typeof obj.authorization_url === "string" && obj.authorization_url) || + (looksLikeAuthUrl(obj.url) ? obj.url : null); + if (direct) return direct; + + if (obj.structuredContent && typeof obj.structuredContent === "object") { + const nested = obj.structuredContent as Record; + const nestedUrl = + (typeof nested.authorization_url === "string" && + nested.authorization_url) || + (looksLikeAuthUrl(nested.url) ? nested.url : null); + if (nestedUrl) return nestedUrl; + } + return null; + }; + + const fromObject = fromRecord(output); + if (fromObject) return fromObject; + + const raw = + typeof output === "string" ? output : JSON.stringify(output ?? ""); + const match = raw.match( + /https:\/\/[^\s"'\]}>]+\/oauth\/[^\s"'\]}>]+|https:\/\/[^\s"'\]}>]+authorize[^\s"'\]}>]*/i + ); + return match ? match[0] : null; +} + +// --- System prompt for plan mode --- + +const PLAN_SYSTEM_PROMPT = `You are a daily planning and triage agent. You have access to tools that connect to the user's services (e.g. Slack, Google Calendar, Linear, GitHub, Gmail). Your job is to thoroughly scan all available sources, read recent items AND currently assigned work, and classify each one. + +WORKFLOW: +1. In your FIRST step, call every available non-WhoAmI tool in parallel — one per source. +2. After getting results, if any tool offers parameters for deeper queries (e.g. filtering, pagination, or fetching specific items), make follow-up calls to get more data. +3. Classify each item and output a structured JSON block. +4. After processing ALL sources, output a summary. +5. Do NOT call *_WhoAmI tools — those are for auth checking, not data fetching. +6. If a tool returns truncated results, work with what you have — do not retry the same call. + +IMPORTANT RULES FOR TOOL RESULTS: +- Do NOT create items for empty results. If a source returns 0 items, skip it silently. +- Do NOT create items for metadata like "you are a member of N channels" — that is not actionable. +- Only create items for ACTUAL content: messages, notifications, events, issues, emails, PRs, etc. +- If a tool returns a list of channels/conversations, do NOT classify the list itself. Only classify individual messages or items with actual content. +- If a tool returns an authorization error, skip it and move on — do not create an item for the error. + +CLASSIFICATION: +- category: NEEDS_REPLY | NEEDS_FEEDBACK | NEEDS_DECISION | NEEDS_REVIEW | ATTEND | FYI | IGNORE +- priority: P0 (urgent) | P1 (important) | P2 (can wait) | FYI +- effort: XS (<5min) | S (5-15min) | M (15-30min) | L (>30min) +- confidence: 0.0 to 1.0 + +SOURCE MAPPING: +- Tools starting with "Slack" → source: "slack" +- Tools starting with "Google", "GoogleCalendar", or "Calendar" → source: "google_calendar" +- Tools starting with "Linear" → source: "linear" +- Tools starting with "Git" or "GitHub" → source: "github" +- Tools starting with "Gmail" → source: "gmail" +- Anything else → source: lowercase service name (e.g. "notion", "dropbox") + +OUTPUT: For EACH item, output EXACTLY this on its own line: + +\`\`\`json:task +{ + "id": "", + "source": "slack", + "sourceDetail": "DM with Alice", + "summary": "<1-2 sentences>", + "category": "NEEDS_REPLY", + "priority": "P1", + "effort": "S", + "why": "", + "suggestedNextStep": "", + "confidence": 0.85, + "participants": [{"id": "", "name": ""}], + "url": "", + "scheduledTime": "" +} +\`\`\` + +After all items from all sources, output: +\`\`\`json:summary +{"total": , "bySource": {"slack": 5, "google_calendar": 3, "linear": 2}} +\`\`\` + +URL RULES: +Prefer a direct deep link to the item itself: +- Slack: use the "permalink" field if present (https://.slack.com/archives//p) +- GitHub: use the issue or PR URL on github.com +- Linear: use the Linear issue URL +- Gmail: use the Gmail thread URL (https://mail.google.com/mail/u/0/#inbox/) +- Google Calendar: use the "htmlLink" field if present +If no direct deep link is available, fall back to the most relevant URL found anywhere in the tool response. +Only omit "url" if there is truly no URL available in the response at all. + +Rules: +- One json:task block per ACTIONABLE item (skip empty results, metadata, and errors) +- Brief status text between blocks is fine +- Process ALL available sources before the summary +- If a tool requires authorization, skip it and move on to other sources +- If errors occur reading a source, skip it silently +- Use ATTEND category for calendar events you need to join +- Use NEEDS_REVIEW for code reviews (PRs, etc.)`; + +// --- Parse structured JSON blocks from streamed text --- + +function extractJsonBlocks(text: string): { + tasks: InboxItem[]; + summary: { total: number; bySource: Record } | null; + remaining: string; +} { + const tasks: InboxItem[] = []; + let summary: { total: number; bySource: Record } | null = null; + let lastConsumedIndex = 0; + + const taskPattern = /```json:task\s*\n([\s\S]*?)```/g; + let match: RegExpExecArray | null; + while ((match = taskPattern.exec(text)) !== null) { + try { + tasks.push(JSON.parse(match[1].trim()) as InboxItem); + lastConsumedIndex = match.index + match[0].length; + } catch { + // Incomplete JSON + } + } + + const summaryPattern = /```json:summary\s*\n([\s\S]*?)```/g; + while ((match = summaryPattern.exec(text)) !== null) { + try { + const raw = JSON.parse(match[1].trim()); + // Normalize: handle both old { tasks, conversations } and new { total, bySource } shapes + if (raw.total !== undefined && raw.bySource !== undefined) { + summary = raw; + } else if (raw.tasks !== undefined) { + summary = { total: raw.tasks, bySource: {} }; + } else { + summary = { total: 0, bySource: {} }; + } + const endIdx = match.index + match[0].length; + if (endIdx > lastConsumedIndex) lastConsumedIndex = endIdx; + } catch { + // Incomplete JSON + } + } + + return { + tasks, + summary, + remaining: lastConsumedIndex > 0 ? text.slice(lastConsumedIndex) : text, + }; +} + +// --- Shared helpers for tool filtering & truncation --- + +const MUTATION = /create|update|delete|send|reply|post|archive|remove|add|invite|merge|close|assign|edit|publish|comment/i; + +function isTriageTool(name: string): boolean { + return !MUTATION.test(name); +} + +const MAX_TOOL_RESULT_CHARS = 4000; + +function truncateToolResult(result: unknown): unknown { + if (result && typeof result === "object" && "content" in (result as Record)) { + const obj = result as { content: { type: string; text: string }[] }; + return { + ...obj, + content: obj.content.map((item) => { + if (item.type === "text" && item.text && item.text.length > MAX_TOOL_RESULT_CHARS) { + return { + ...item, + text: item.text.slice(0, MAX_TOOL_RESULT_CHARS) + + `\n...[truncated ${item.text.length - MAX_TOOL_RESULT_CHARS} chars]`, + }; + } + return item; + }), + }; + } + if (typeof result === "string" && result.length > MAX_TOOL_RESULT_CHARS) { + return result.slice(0, MAX_TOOL_RESULT_CHARS) + + `\n...[truncated ${result.length - MAX_TOOL_RESULT_CHARS} chars]`; + } + return result; +} + +// --- Route handler --- + +export async function POST() { + const user = await getSession(); + if (!user) { + return Response.json({ error: "Unauthorized" }, { status: 401 }); + } + + let streamController: ReadableStreamDefaultController | undefined; + + const stream = new ReadableStream({ + start(controller) { + streamController = controller; + }, + }); + + function emit(event: PlanEvent) { + try { + streamController?.enqueue(encodeEvent(event)); + } catch { + // Stream closed by client + } + } + + (async () => { + try { + emit({ type: "status", message: "Connecting to Arcade Gateway..." }); + + setElicitationCallback(({ elicitationId, message, authUrl }) => { + emit({ type: "elicitation", elicitationId, authUrl, message }); + }); + + const allToolsets = await mcpClient.listToolsets(); + + // Filter toolsets: keep only read-only triage tools, wrap execute for truncation. + // listToolsets() returns { serverName: { toolName: tool, ... } }. + const filteredToolsets: Record> = {}; + const toolNames: string[] = []; + let allToolCount = 0; + + for (const [setName, toolsObj] of Object.entries(allToolsets)) { + const tools = toolsObj as unknown as Record Promise; [k: string]: unknown }>; + const filtered: Record = {}; + + for (const [toolName, tool] of Object.entries(tools)) { + allToolCount++; + if (!isTriageTool(toolName)) continue; + if (/[._]WhoAmI$/i.test(toolName)) continue; + + if (tool.execute) { + const orig = tool.execute; + filtered[toolName] = { + ...tool, + execute: async (...args: unknown[]) => truncateToolResult(await orig(...args)), + }; + } else { + filtered[toolName] = tool; + } + toolNames.push(toolName); + } + + if (Object.keys(filtered).length > 0) { + filteredToolsets[setName] = filtered; + } + } + + const sources = [...new Set(toolNames.map((n) => mapToolToSource(n)))]; + console.log(`[plan] ${toolNames.length} triage tools (of ${allToolCount} total) from sources: ${sources.join(", ")}`); + + emit({ + type: "status", + message: `Found ${toolNames.length} tools across ${sources.length} sources. Starting triage...`, + }); + + emit({ type: "sources", sources }); + + const toolsBySource: Record = {}; + for (const name of toolNames) { + const src = mapToolToSource(name); + if (!toolsBySource[src]) toolsBySource[src] = []; + toolsBySource[src].push(name); + } + const toolInventory = Object.entries(toolsBySource) + .map(([src, names]) => `${src}: ${names.join(", ")}`) + .join("\n"); + + const prompt = `Plan my day. Today is ${new Date().toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" })}.\n\nHere are the tools available by source:\n\n${toolInventory}\n\nIn your FIRST step, call ALL non-WhoAmI tools in parallel — one call per tool. Do NOT skip any source.\nFor calendar tools, make sure to pass today's date range so you get today's events.\nAfter getting results, classify every item. If any source returned a list you can drill into, make follow-up calls.\nI need a COMPLETE picture: notifications, assigned work, upcoming events, and unread messages.`; + + let accumulatedText = ""; + let emittedTaskCount = 0; + let emittedSummary = false; + + const agent = new Agent({ + id: "plan-agent", + name: "Plan Agent", + model: getModel(), + instructions: PLAN_SYSTEM_PROMPT, + }); + + const result = await agent.stream(prompt, { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + toolsets: filteredToolsets as any, + maxSteps: 20, + onStepFinish: (step) => { + const { toolCalls = [], toolResults = [] } = step as unknown as { + toolCalls: { toolCallId: string; toolName: string }[]; + toolResults: { toolCallId?: string; output: unknown }[]; + }; + console.log(`[plan] Step: ${toolCalls.length} calls, ${toolResults.length} results`); + + for (const call of toolCalls) { + const source = mapToolToSource(call.toolName); + emit({ + type: "status", + message: `Calling ${source}: ${call.toolName}...`, + }); + } + + const toolNameByCallId = new Map( + toolCalls.map((call) => [call.toolCallId, call.toolName] as const) + ); + + for (let i = 0; i < toolResults.length; i++) { + const tr = toolResults[i]; + const authUrl = extractAuthUrlFromToolOutput(tr.output); + if (authUrl) { + const matchedToolName = tr.toolCallId + ? toolNameByCallId.get(tr.toolCallId) + : undefined; + const fallbackToolName = i < toolCalls.length + ? toolCalls[i].toolName + : undefined; + const toolName = mapToolToSource(matchedToolName ?? fallbackToolName ?? ""); + emit({ type: "auth_required", authUrl, toolName }); + } + } + }, + }); + + for await (const chunk of result.textStream) { + if (chunk) { + accumulatedText += chunk; + const { tasks, summary, remaining } = extractJsonBlocks(accumulatedText); + accumulatedText = remaining; + + for (const task of tasks) { + emittedTaskCount++; + emit({ type: "task", data: task }); + emit({ + type: "status", + message: `Classified ${emittedTaskCount} item${emittedTaskCount > 1 ? "s" : ""}...`, + }); + } + if (summary) { + emit({ type: "summary", data: summary }); + emittedSummary = true; + } + } + } + + if (accumulatedText.length > 0) { + const { tasks, summary } = extractJsonBlocks(accumulatedText); + for (const task of tasks) { + emittedTaskCount++; + emit({ type: "task", data: task }); + } + if (summary) { + emit({ type: "summary", data: summary }); + emittedSummary = true; + } + } + + if (!emittedSummary && emittedTaskCount > 0) { + emit({ + type: "summary", + data: { total: emittedTaskCount, bySource: {} }, + }); + } + + emit({ type: "done" }); + } catch (error) { + console.error("[plan] Error:", error); + const msg = error instanceof Error ? error.message : "Unknown error"; + const isAuthError = + msg.includes("401") || + msg.includes("Missing Authorization") || + msg.includes("Unauthorized"); + emit({ + type: "error", + message: isAuthError + ? "Not connected to Arcade. Please connect your Arcade account from the dashboard." + : msg, + }); + emit({ type: "done" }); + } finally { + setElicitationCallback(null); + try { + streamController?.close(); + } catch { + /* ignore */ + } + } + })(); + + return new Response(stream, { + headers: { + "Content-Type": "application/x-ndjson", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }, + }); +} diff --git a/test-mastra/app/api/sources/route.ts b/test-mastra/app/api/sources/route.ts new file mode 100644 index 0000000..c894805 --- /dev/null +++ b/test-mastra/app/api/sources/route.ts @@ -0,0 +1,105 @@ +/** + * Sources API Route — WhoAmI Auth Check + * + * POST /api/sources + * + * Calls *_WhoAmI tools in parallel to check which services are + * authenticated. Returns a map of source → { status, authUrl? }. + */ + +import { getSession } from "@/lib/auth"; +import { getArcadeMCPClient } from "@/src/mastra/tools/arcade"; + +// --- Helpers --- + +import { mapToolToSource } from "@/lib/sources"; + +// Fallback: extract auth URL from tool output for gateways that don't support elicitation +function extractAuthUrlFromToolOutput(output: unknown): string | null { + const looksLikeAuthUrl = (value: unknown): value is string => + typeof value === "string" && + /oauth|authorize/i.test(value); + + const fromRecord = (value: unknown): string | null => { + if (!value || typeof value !== "object") return null; + const obj = value as Record; + const direct = + (typeof obj.authorization_url === "string" && obj.authorization_url) || + (looksLikeAuthUrl(obj.url) ? obj.url : null); + if (direct) return direct; + + if (obj.structuredContent && typeof obj.structuredContent === "object") { + const nested = obj.structuredContent as Record; + const nestedUrl = + (typeof nested.authorization_url === "string" && + nested.authorization_url) || + (looksLikeAuthUrl(nested.url) ? nested.url : null); + if (nestedUrl) return nestedUrl; + } + return null; + }; + + const fromObject = fromRecord(output); + if (fromObject) return fromObject; + + const raw = + typeof output === "string" ? output : JSON.stringify(output ?? ""); + const match = raw.match( + /https:\/\/[^\s"'\]}>]+\/oauth\/[^\s"'\]}>]+|https:\/\/[^\s"'\]}>]+authorize[^\s"'\]}>]*/i + ); + return match ? match[0] : null; +} + +// --- Route handler --- + +export async function POST() { + const user = await getSession(); + if (!user) { + return Response.json({ error: "Unauthorized" }, { status: 401 }); + } + + let mcpClient: Awaited> | null = null; + try { + mcpClient = await getArcadeMCPClient(); + const allTools = await mcpClient.tools(); + + // Find all WhoAmI tools — lightweight identity checks + const whoAmITools = Object.entries(allTools).filter(([name]) => + /[._]WhoAmI$/i.test(name) + ); + + // Call them all in parallel + const results = await Promise.allSettled( + whoAmITools.map(async ([name, tool]) => { + const result = await tool.execute!({}, { toolCallId: name, messages: [] }); + const authUrl = extractAuthUrlFromToolOutput(result); + const source = mapToolToSource(name); + return { source, authUrl }; + }) + ); + + // Build response: { sources: { gmail: { status, authUrl? }, ... } } + const sources: Record = {}; + for (const r of results) { + if (r.status === "fulfilled") { + const { source, authUrl } = r.value; + sources[source] = authUrl + ? { status: "auth_required", authUrl } + : { status: "connected" }; + } + } + + return Response.json({ sources }); + } catch (error) { + console.error("[sources] Error checking WhoAmI tools:", error); + return Response.json({ sources: {} }); + } finally { + if (mcpClient) { + try { + await mcpClient.close(); + } catch { + /* ignore */ + } + } + } +} diff --git a/test-mastra/app/dashboard/page.tsx b/test-mastra/app/dashboard/page.tsx new file mode 100644 index 0000000..792f07c --- /dev/null +++ b/test-mastra/app/dashboard/page.tsx @@ -0,0 +1,439 @@ +"use client"; + +import { Suspense, useCallback, useEffect, useMemo, useState } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { authClient } from "@/lib/auth-client"; +import type { ConfigWarning } from "@/types/dashboard"; +import { Header } from "@/components/layout/header"; +import { EmptyState } from "@/components/dashboard/empty-state"; +import { StatsBar } from "@/components/dashboard/stats-bar"; +import { TaskList } from "@/components/dashboard/task-list"; +import { SourceAuthGate } from "@/components/dashboard/source-auth-gate"; +import { AuthPrompt } from "@/components/dashboard/auth-prompt"; +import { useArcadeConnection } from "@/hooks/use-arcade-connection"; +import { useSourceCheck } from "@/hooks/use-source-check"; +import { usePlanStream } from "@/hooks/use-plan-stream"; +import { + Alert, + AlertTitle, + AlertDescription, + Skeleton, + Button, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@arcadeai/design-system"; +import { Info, Loader2, ShieldAlert, AlertTriangle, RotateCcw, X } from "lucide-react"; + +// --- Config health warnings --- + +function ConfigWarningBanner({ warnings }: { warnings: ConfigWarning[] }) { + if (warnings.length === 0) return null; + return ( +
+
+ {warnings.map((w) => ( +
+ +
+ {w.title}:{" "} + {w.message}{" "} + + Docs → + +
+
+ ))} +
+
+ ); +} + +function ArcadeSignInButton({ authUrl }: { authUrl: string }) { + const [loading, setLoading] = useState(false); + return ( + + ); +} + +export default function DashboardPage() { + return ( + + + + ); +} + +function DashboardContent() { + const router = useRouter(); + const searchParams = useSearchParams(); + const urlError = searchParams.get("error"); + + // --- Hooks --- + const { arcadeStatus, retryConnection } = useArcadeConnection(); + + const { + sourceCheckPhase, + authGateActive, + setAuthGateActive, + skippedSources, + skipSource, + sourceStatuses, + authUrls, + markSourceAuthRequired, + markAllCheckingAsConnected, + addAuthUrl, + dismissAuthUrl, + resetForNewPlan, + } = useSourceCheck({ enabled: arcadeStatus.state === "connected" }); + + const { + items, + stats, + loading, + error, + showError, + activeSource, + setActiveSource, + statusMessage, + planRan, + resetPlan, + handlePlan, + } = usePlanStream({ + onAuthRequired: (url, toolName) => { + if (toolName) markSourceAuthRequired(toolName); + addAuthUrl(url, toolName); + }, + onElicitation: (_elicitationId, authUrl, message) => { + addAuthUrl(authUrl, message); + }, + onSourcesDone: markAllCheckingAsConnected, + }); + + // --- Config health check --- + const [configWarnings, setConfigWarnings] = useState([]); + + // --- Dismissible callout state --- + const [showAgentWorkingCallout, setShowAgentWorkingCallout] = useState(() => + typeof window !== "undefined" ? localStorage.getItem("callout-agent-working") !== "1" : true + ); + const dismissAgentWorkingCallout = () => { + localStorage.setItem("callout-agent-working", "1"); + setShowAgentWorkingCallout(false); + }; + const [showCustomizeCallout, setShowCustomizeCallout] = useState(() => + typeof window !== "undefined" ? localStorage.getItem("callout-customize-agent") !== "1" : true + ); + const dismissCustomizeCallout = () => { + localStorage.setItem("callout-customize-agent", "1"); + setShowCustomizeCallout(false); + }; + + useEffect(() => { + fetch("/api/health") + .then((r) => r.json()) + .then((data) => setConfigWarnings(data.warnings ?? [])) + .catch(() => {}); + }, []); + + // --- URL error parameter handling --- + useEffect(() => { + if (urlError) { + const messages: Record = { + auth_incomplete: "Authorization was not completed. Please try connecting again.", + auth_failed: "Authorization failed. Please try again.", + gateway_missing: + "ARCADE_GATEWAY_URL is missing. Create one at https://app.arcade.dev/mcp-gateways, add only the minimum required tools from Slack, Google Calendar, Linear, GitHub, and Gmail, then set ARCADE_GATEWAY_URL in .env.", + verify_failed: "User verification failed. Please try again.", + verify_session_required: + "Verification failed: no session found. If using ngrok, log in through the ngrok URL (not localhost) so the session cookie matches the verifier host.", + }; + showError(messages[urlError] || `Authentication error: ${urlError}`); + window.history.replaceState({}, "", window.location.pathname); + } + }, [urlError, showError]); + + // --- Handlers --- + const handleLogout = useCallback(async () => { + await authClient.signOut(); + router.push("/"); + }, [router]); + + // Resets cross-hook state then kicks off a new plan run + const startPlan = useCallback(() => { + resetPlan(); + resetForNewPlan(); + handlePlan(); + }, [resetPlan, resetForNewPlan, handlePlan]); + + // --- Computed --- + const hasItems = items.length > 0; + const filteredItems = useMemo( + () => (activeSource !== null ? items.filter((i) => i.source === activeSource) : items), + [activeSource, items] + ); + const showEmpty = !hasItems && !loading && !planRan; + const showNoResults = !hasItems && !loading && planRan && !error; + + // --- Arcade connection gate --- + if (arcadeStatus.state !== "connected") { + return ( +
+
+ +
+ {arcadeStatus.state === "checking" && ( +
+ +

Connecting to Arcade...

+
+ )} + + {arcadeStatus.state === "needs_auth" && ( + + +
+ +
+ Connect to Arcade + + Sign in with your Arcade account to give the agent access to your tools. + +
+ + + + + + Why Arcade? + + The agent uses Arcade as an MCP Gateway to read from your tools on your behalf. + Signing in here links your Arcade identity so the gateway knows which + user's tools to access. Your{" "} + ARCADE_API_KEY in{" "} + .env handles the + server-side connection. + + + +
+ )} + + {arcadeStatus.state === "error" && ( + + +
+ +
+ Connection Failed + {arcadeStatus.message} +
+ + + +
+ )} +
+
+ ); + } + + // --- Connected: show dashboard --- + return ( +
+
+ + + {/* Source check loading */} + {sourceCheckPhase !== "done" && ( +
+
+ +

Checking tool permissions...

+
+
+ )} + + {/* Pre-flight auth gate */} + {sourceCheckPhase === "done" && authGateActive && ( +
+ setAuthGateActive(false)} + /> +
+ )} + + {/* Normal dashboard (source check done, gate dismissed) */} + {sourceCheckPhase === "done" && !authGateActive && ( +
+ {error && ( +
+ {error} +
+ )} + + {/* Mid-run auth prompts (fallback for tools not covered by pre-flight) */} + {authUrls.length > 0 && ( +
+ {authUrls.map((auth) => ( + dismissAuthUrl(auth.url)} + /> + ))} +
+ )} + + {loading && statusMessage && ( +
+ + {statusMessage} +
+ )} + + {showEmpty && } + + {showNoResults && ( +
+ +
+

No items found

+

+ The agent finished scanning but didn't find any items to triage. This can + happen if tools need authorization or if there's no recent activity. +

+
+ +
+ )} + + {loading && !hasItems && authUrls.length === 0 && ( +
+ {showAgentWorkingCallout && ( + + + + What's happening + + + + The agent is reading from your connected sources and classifying what it finds. + This behavior is driven by the system prompt in{" "} + + app/api/plan/route.ts + {" "} + — edit it to change what gets fetched, how items are prioritized, or what the + agent focuses on. + + + )} +
+ {Array.from({ length: 3 }).map((_, i) => ( + + ))} +
+
+ {Array.from({ length: 4 }).map((_, i) => ( + + ))} +
+
+ )} + + {hasItems && ( + <> +
+ +
+ {showCustomizeCallout && ( + + + + Make it yours + + + + Start with the system prompt in{" "} + + app/api/plan/route.ts + {" "} + — that's where the agent's behavior is defined. From there, check out{" "} + AGENT_PLAYBOOK.md{" "} + for a full walkthrough of customization points. + + + )} + + + + )} +
+ )} +
+ ); +} diff --git a/test-mastra/app/favicon.ico b/test-mastra/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/test-mastra/app/favicon.ico differ diff --git a/test-mastra/app/globals.css b/test-mastra/app/globals.css new file mode 100644 index 0000000..1969d5c --- /dev/null +++ b/test-mastra/app/globals.css @@ -0,0 +1,149 @@ +@import "tailwindcss"; +@import "tw-animate-css"; +@import "@arcadeai/design-system/index.css"; +@source "../node_modules/@arcadeai/design-system"; +@plugin "@tailwindcss/typography"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --radius-2xl: calc(var(--radius) + 8px); + --radius-3xl: calc(var(--radius) + 12px); + --radius-4xl: calc(var(--radius) + 16px); +} + +:root { + --font-geist-sans: + "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, + "Noto Sans", sans-serif; + --font-geist-mono: + "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.631 0.255 22.8); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.631 0.255 22.8); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.5 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.205 0 0); + --sidebar-accent: oklch(0.631 0.255 22.8); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.13 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.18 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.18 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.631 0.255 22.8); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.24 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.24 0 0); + --muted-foreground: oklch(0.65 0 0); + --accent: oklch(0.24 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 12%); + --ring: oklch(0.631 0.255 22.8); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.18 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.631 0.255 22.8); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.24 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.631 0.255 22.8); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground font-sans; + } +} + +/* Card entrance animation */ +@keyframes card-in { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.animate-card-in { + animation: card-in 0.3s ease-out both; +} diff --git a/test-mastra/app/icon.png b/test-mastra/app/icon.png new file mode 100644 index 0000000..c68aa62 Binary files /dev/null and b/test-mastra/app/icon.png differ diff --git a/test-mastra/app/layout.tsx b/test-mastra/app/layout.tsx new file mode 100644 index 0000000..f60cae3 --- /dev/null +++ b/test-mastra/app/layout.tsx @@ -0,0 +1,24 @@ +import type { Metadata } from "next"; +import "./globals.css"; +import { ThemeProvider } from "@/components/theme-provider"; + +export const metadata: Metadata = { + title: "Arcade Agent", + description: "AI agent powered by Arcade", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + {children} + + + + ); +} diff --git a/test-mastra/app/page.tsx b/test-mastra/app/page.tsx new file mode 100644 index 0000000..db02f58 --- /dev/null +++ b/test-mastra/app/page.tsx @@ -0,0 +1,11 @@ +import { redirect } from "next/navigation"; +import { getSession } from "@/lib/auth"; +import { LoginForm } from "@/components/auth/login-form"; + +export default async function AuthPage() { + const session = await getSession(); + if (session) { + redirect("/dashboard"); + } + return ; +} diff --git a/test-mastra/bun.lock b/test-mastra/bun.lock new file mode 100644 index 0000000..869768a --- /dev/null +++ b/test-mastra/bun.lock @@ -0,0 +1,2110 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "test-mastra", + "dependencies": { + "@ai-sdk/anthropic": "^3.0.0", + "@ai-sdk/mcp": "^1.0.36", + "@ai-sdk/openai": "^3.0.29", + "@ai-sdk/react": "^3.0.92", + "@arcadeai/design-system": "^3.30.0", + "@better-auth/drizzle-adapter": "^1.5.3", + "@mastra/ai-sdk": "^1.3.3", + "@mastra/core": "^1.24.1", + "@mastra/mcp": "^1.4.2", + "@modelcontextprotocol/sdk": "^1.27.1", + "ai": "^6.0.90", + "better-auth": "^1.5.3", + "better-sqlite3": "^12.6.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "drizzle-orm": "^0.45.1", + "lucide-react": "^0.575.0", + "next": "16.1.6", + "next-themes": "^0.4.6", + "radix-ui": "^1.4.3", + "react": "19.2.3", + "react-dom": "19.2.3", + "react-markdown": "^10.1.0", + "remark-gfm": "^4.0.1", + "tailwind-merge": "^3.5.0", + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@tailwindcss/typography": "^0.5.16", + "@types/better-sqlite3": "^7.6.13", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "drizzle-kit": "^0.31.9", + "eslint": "^9", + "eslint-config-next": "16.1.6", + "prettier": "^3", + "tailwindcss": "^4", + "tw-animate-css": "^1.4.0", + "typescript": "^5", + }, + }, + }, + "packages": { + "@a2a-js/sdk": ["@a2a-js/sdk@0.2.5", "", { "dependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.23", "body-parser": "^2.2.0", "cors": "^2.8.5", "express": "^4.21.2", "uuid": "^11.1.0" } }, "sha512-VTDuRS5V0ATbJ/LkaQlisMnTAeYKXAK6scMguVBstf+KIBQ7HIuKhiXLv+G/hvejkV+THoXzoNifInAkU81P1g=="], + + "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.69", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LshR7X3pFugY0o41G2VKTmg1XoGpSl7uoYWfzk6zjVZLhCfeFiwgpOga+eTV4XY1VVpZwKVqRnkDbIL7K2eH5g=="], + + "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.95", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZmUNNbZl3V42xwQzPaNUi+s8eqR2lnrxf0bvB6YbLXpLjHYv0k2Y78t12cNOfY0bxGeuVVTLyk856uLuQIuXEQ=="], + + "@ai-sdk/mcp": ["@ai-sdk/mcp@1.0.36", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "pkce-challenge": "^5.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-THQKwlknp7OU2ViLPfIU7W01XvDRM2eqH+4UULQgP64AopnwI9mGqqJeGIx2l/pxUu9yIDQtW9YtYM8kHm2CQg=="], + + "@ai-sdk/openai": ["@ai-sdk/openai@3.0.52", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4Rr8NCGmfWTz6DCUvixn9UmyZcMatiHn0zWoMzI3JCUe9R1P/vsPOpCBALKoSzVYOjyJnhtnVIbfUKujcS39uw=="], + + "@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], + + "@ai-sdk/provider-utils-v5": ["@ai-sdk/provider-utils@3.0.20", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="], + + "@ai-sdk/provider-utils-v6": ["@ai-sdk/provider-utils@4.0.0", "", { "dependencies": { "@ai-sdk/provider": "3.0.0", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HyCyOls9I3a3e38+gtvOJOEjuw9KRcvbBnCL5GBuSmJvS9Jh9v3fz7pRC6ha1EUo/ZH1zwvLWYXBMtic8MTguA=="], + + "@ai-sdk/provider-v5": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], + + "@ai-sdk/provider-v6": ["@ai-sdk/provider@3.0.5", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-2Xmoq6DBJqmSl80U6V9z5jJSJP7ehaJJQMy2iFUqTay06wdCqTnPVBBQbtEL8RCChenL+q5DC5H5WzU3vV3v8w=="], + + "@ai-sdk/react": ["@ai-sdk/react@3.0.160", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.23", "ai": "6.0.158", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-ZDD42ggZYyBJjiX3PAl03t58uMJsIgreHfRhbdQR0hyuGlxcg1nXS5OvPRQUejNyGz/cM24/uGzt3Mn5ncdoOQ=="], + + "@ai-sdk/ui-utils-v5": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="], + + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@14.2.1", "", { "dependencies": { "js-yaml": "^4.1.0" }, "peerDependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-HmdFw9CDYqM6B25pqGBpNeLCKvGPlIx1EbLrVL0zPvj50CJQUHyBNBw45Muk0kEIkogo1VZvOKHajdMuAzSxRg=="], + + "@arcadeai/design-system": ["@arcadeai/design-system@3.37.0", "", { "dependencies": { "@streamdown/code": "1.1.0", "class-variance-authority": "0.7.1", "clsx": "2.1.1", "cmdk": "1.1.1", "date-fns": "4.1.0", "radix-ui": "1.4.3", "react-day-picker": "9.14.0", "react-resizable-panels": "3.0.6", "react-shiki": "0.9.1", "remark-breaks": "4.0.0", "remark-gfm": "4.0.1", "streamdown": "2.4.0", "tailwind-merge": "3.5.0", "use-stick-to-bottom": "1.1.3" }, "peerDependencies": { "@hookform/resolvers": ">=5.2.1", "lucide-react": ">=0.544.0", "react": ">=19.2.1", "react-dom": ">=19.2.1", "react-hook-form": ">=7.62.0", "recharts": ">=3.2.0", "tailwindcss": ">=4.1.13" } }, "sha512-hfWSDoes2X1+Z9cYJENKtF2Y4+7xLa+YjOVyUi+7e80uuPn77pzBSUgmQBfBRro8TguON2nkxMqiiuIgDYEa3A=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], + + "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], + + "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], + + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="], + + "@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], + + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], + + "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "@better-auth/core": ["@better-auth/core@1.6.2", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.39.0", "@standard-schema/spec": "^1.1.0", "zod": "^4.3.6" }, "peerDependencies": { "@better-auth/utils": "0.4.0", "@better-fetch/fetch": "1.1.21", "@cloudflare/workers-types": ">=4", "@opentelemetry/api": "^1.9.0", "better-call": "1.3.5", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" }, "optionalPeers": ["@cloudflare/workers-types"] }, "sha512-nBftDp+eN1fwXor1O4KQorCXa0tJNDgpab7O1z4NcWUU+3faDpdzqLn5mbXZer2E8ZD4VhjqOfYZ041xnBF5NA=="], + + "@better-auth/drizzle-adapter": ["@better-auth/drizzle-adapter@1.6.2", "", { "peerDependencies": { "@better-auth/core": "^1.6.2", "@better-auth/utils": "0.4.0", "drizzle-orm": ">=0.41.0" }, "optionalPeers": ["drizzle-orm"] }, "sha512-KawrNNuhgmpcc5PgLs6HesMckxCscz5J+BQ99iRmU1cLzG/A87IcydrmYtep+K8WHPN0HmZ/i4z/nOBCtxE2qA=="], + + "@better-auth/kysely-adapter": ["@better-auth/kysely-adapter@1.6.2", "", { "peerDependencies": { "@better-auth/core": "^1.6.2", "@better-auth/utils": "0.4.0", "kysely": "^0.27.0 || ^0.28.0" }, "optionalPeers": ["kysely"] }, "sha512-YMMm75jek/MNCAFWTAaq/U3VPmFnrwZW4NhBjjAwruHQJEIrSZZaOaUEXuUpFRRBhWqg7OOltQcHMwU/45CkuA=="], + + "@better-auth/memory-adapter": ["@better-auth/memory-adapter@1.6.2", "", { "peerDependencies": { "@better-auth/core": "^1.6.2", "@better-auth/utils": "0.4.0" } }, "sha512-QvuK5m7NFgkzLPHyab+NORu3J683nj36Tix58qq6DPcniyY6KZk5gY2yyh4+z1wgSjrxwY5NFx/DC2qz8B8NJg=="], + + "@better-auth/mongo-adapter": ["@better-auth/mongo-adapter@1.6.2", "", { "peerDependencies": { "@better-auth/core": "^1.6.2", "@better-auth/utils": "0.4.0", "mongodb": "^6.0.0 || ^7.0.0" }, "optionalPeers": ["mongodb"] }, "sha512-IvR2Q+1pjzxA4JXI3ED76+6fsqervIpZ2K5MxoX/+miLQhLEmNcbqqcItg4O2kfkxN8h33/ev57sjTW8QH9Tuw=="], + + "@better-auth/prisma-adapter": ["@better-auth/prisma-adapter@1.6.2", "", { "peerDependencies": { "@better-auth/core": "^1.6.2", "@better-auth/utils": "0.4.0", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@prisma/client", "prisma"] }, "sha512-bQkXYTo1zPau+xAiMpo1yCjEDSy7i7oeYlkYO+fSfRDCo52DE/9oPOOuI+EStmFkPUNSk9L2rhk8Fulifi8WCg=="], + + "@better-auth/telemetry": ["@better-auth/telemetry@1.6.2", "", { "peerDependencies": { "@better-auth/core": "^1.6.2", "@better-auth/utils": "0.4.0", "@better-fetch/fetch": "1.1.21" } }, "sha512-o4gHKXqizUxVUUYChZZTowLEzdsz3ViBE/fKFzfHqNFUnF+aVt8QsbLSfipq1WpTIXyJVT/SnH0hgSdWxdssbQ=="], + + "@better-auth/utils": ["@better-auth/utils@0.4.0", "", { "dependencies": { "@noble/hashes": "^2.0.1" } }, "sha512-RpMtLUIQAEWMgdPLNVbIF5ON2mm+CH0U3rCdUCU1VyeAUui4m38DyK7/aXMLZov2YDjG684pS1D0MBllrmgjQA=="], + + "@better-fetch/fetch": ["@better-fetch/fetch@1.1.21", "", {}, "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A=="], + + "@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="], + + "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], + + "@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], + + "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.5", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg=="], + + "@eslint/js": ["@eslint/js@9.39.4", "", {}, "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.8", "", { "dependencies": { "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + + "@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="], + + "@hookform/resolvers": ["@hookform/resolvers@5.2.2", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + + "@isaacs/ttlcache": ["@isaacs/ttlcache@2.1.4", "", {}, "sha512-7kMz0BJpMvgAMkyglums7B2vtrn5g0a0am77JY0GjkZZNetOBCFn7AG7gKCwT0QPiXyxW7YIQSgtARknUEOcxQ=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@lukeed/csprng": ["@lukeed/csprng@1.1.0", "", {}, "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA=="], + + "@lukeed/uuid": ["@lukeed/uuid@2.0.1", "", { "dependencies": { "@lukeed/csprng": "^1.1.0" } }, "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w=="], + + "@mastra/ai-sdk": ["@mastra/ai-sdk@1.3.3", "", { "peerDependencies": { "@mastra/core": ">=1.5.0-0 <2.0.0-0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-1w4bFm3+xz15dp9T0KaBh4x7twwJtLlnWgaFc2sd6pARsAIBX0q+0H9Y38kg4uW191wa6PfUCkFXoqFD+c7fXg=="], + + "@mastra/core": ["@mastra/core@1.24.1", "", { "dependencies": { "@a2a-js/sdk": "~0.2.5", "@ai-sdk/provider-utils-v5": "npm:@ai-sdk/provider-utils@3.0.20", "@ai-sdk/provider-utils-v6": "npm:@ai-sdk/provider-utils@4.0.0", "@ai-sdk/provider-v5": "npm:@ai-sdk/provider@2.0.1", "@ai-sdk/provider-v6": "npm:@ai-sdk/provider@3.0.5", "@ai-sdk/ui-utils-v5": "npm:@ai-sdk/ui-utils@1.2.11", "@isaacs/ttlcache": "^2.1.4", "@lukeed/uuid": "^2.0.1", "@mastra/schema-compat": "1.2.7", "@modelcontextprotocol/sdk": "^1.27.1", "@sindresorhus/slugify": "^2.2.1", "@standard-schema/spec": "^1.1.0", "ajv": "^8.18.0", "chat": "^4.23.0", "dotenv": "^17.3.1", "execa": "^9.6.1", "gray-matter": "^4.0.3", "hono": "^4.12.8", "hono-openapi": "^1.3.0", "ignore": "^7.0.5", "js-tiktoken": "^1.0.21", "json-schema": "^0.4.0", "lru-cache": "^11.2.7", "p-map": "^7.0.4", "p-retry": "^7.1.1", "picomatch": "^4.0.3", "radash": "^12.1.1", "tokenx": "^1.3.0", "ws": "^8.19.0", "xxhash-wasm": "^1.1.0" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Y0a0HZl7AMsuqrKM1OLePTJRaCr71w0RcUbCuaTgGN527jUTpfKmj9KJoNKqx9OTYUgZQ5ssNJMM6oDxsj2gxQ=="], + + "@mastra/mcp": ["@mastra/mcp@1.4.2", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^14.2.1", "@modelcontextprotocol/sdk": "^1.27.1", "exit-hook": "^5.1.0", "fast-deep-equal": "^3.1.3", "uuid": "^13.0.0" }, "peerDependencies": { "@mastra/core": ">=1.0.0-0 <2.0.0-0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-zyn+CiFtHvWPf6BGTTnfWQ2nuJZDuXe646F9ByEOy4e1HJ90FpdwUnK/u4wu53i/tpui4Av64kH6T/Oc6yVUPQ=="], + + "@mastra/schema-compat": ["@mastra/schema-compat@1.2.7", "", { "dependencies": { "json-schema-to-zod": "^2.7.0", "zod-from-json-schema": "^0.5.2", "zod-from-json-schema-v3": "npm:zod-from-json-schema@^0.0.5", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-t63E0f5HcH8neXPfs3D5x4qqQM6Pf/pbhFUVk0cTC0bFo6609sT/+189I+2HY4sbAF3uzurOgy2fXIS4vfMkOA=="], + + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], + + "@next/env": ["@next/env@16.1.6", "", {}, "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ=="], + + "@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.1.6", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ=="], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw=="], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A=="], + + "@noble/ciphers": ["@noble/ciphers@2.2.0", "", {}, "sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA=="], + + "@noble/hashes": ["@noble/hashes@2.2.0", "", {}, "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + + "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], + + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-accessible-icon": ["@radix-ui/react-accessible-icon@1.1.7", "", { "dependencies": { "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], + + "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + + "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g=="], + + "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.10", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog=="], + + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-form": ["@radix-ui/react-form@0.1.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ=="], + + "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="], + + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="], + + "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA=="], + + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="], + + "@radix-ui/react-one-time-password-field": ["@radix-ui/react-one-time-password-field@0.1.8", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg=="], + + "@radix-ui/react-password-toggle-field": ["@radix-ui/react-password-toggle-field@0.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.7", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg=="], + + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], + + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="], + + "@radix-ui/react-slider": ["@radix-ui/react-slider@1.3.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + + "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g=="], + + "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ=="], + + "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q=="], + + "@radix-ui/react-toolbar": ["@radix-ui/react-toolbar@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-toggle-group": "1.1.11" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg=="], + + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.0", "", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + + "@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="], + + "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + + "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], + + "@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], + + "@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], + + "@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], + + "@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], + + "@sindresorhus/slugify": ["@sindresorhus/slugify@2.2.1", "", { "dependencies": { "@sindresorhus/transliterate": "^1.0.0", "escape-string-regexp": "^5.0.0" } }, "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw=="], + + "@sindresorhus/transliterate": ["@sindresorhus/transliterate@1.6.0", "", { "dependencies": { "escape-string-regexp": "^5.0.0" } }, "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ=="], + + "@standard-community/standard-json": ["@standard-community/standard-json@0.3.5", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.16.8", "quansync": "^0.2.11", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA=="], + + "@standard-community/standard-openapi": ["@standard-community/standard-openapi@0.2.9", "", { "peerDependencies": { "@standard-community/standard-json": "^0.3.5", "@standard-schema/spec": "^1.0.0", "arktype": "^2.1.20", "effect": "^3.17.14", "openapi-types": "^12.1.3", "sury": "^10.0.0", "typebox": "^1.0.0", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-openapi": "^4" }, "optionalPeers": ["arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-openapi"] }, "sha512-htj+yldvN1XncyZi4rehbf9kLbu8os2Ke/rfqoZHCMHuw34kiF3LP/yQPdA0tQ940y8nDq3Iou8R3wG+AGGyvg=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], + + "@streamdown/code": ["@streamdown/code@1.1.0", "", { "dependencies": { "shiki": "^3.19.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-swypCjtE6vv01bnEtPeaw2ew9cbL2nbsLc06HAIK3K6nYXj5WDA8VLR6GEiwdh7HLIPt5dGze+PJ0eJVkqesug=="], + + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], + + "@tabby_ai/hijri-converter": ["@tabby_ai/hijri-converter@1.0.5", "", {}, "sha512-r5bClKrcIusDoo049dSL8CawnHR6mRdDwhlQuIgZRNty68q0x8k3Lf1BtPAMxRf/GgnHBnIO4ujd3+GQdLWzxQ=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.2.2", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "postcss": "^8.5.6", "tailwindcss": "4.2.2" } }, "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ=="], + + "@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@types/better-sqlite3": ["@types/better-sqlite3@7.6.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA=="], + + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], + + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + + "@types/cors": ["@types/cors@2.8.19", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="], + + "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], + + "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], + + "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], + + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], + + "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], + + "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], + + "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="], + + "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], + + "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], + + "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + + "@types/express": ["@types/express@4.17.25", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "^1" } }, "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw=="], + + "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.8", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA=="], + + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + + "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/node": ["@types/node@20.19.39", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw=="], + + "@types/qs": ["@types/qs@6.15.0", "", {}, "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow=="], + + "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + + "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], + + "@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.58.2", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.58.2", "@typescript-eslint/type-utils": "8.58.2", "@typescript-eslint/utils": "8.58.2", "@typescript-eslint/visitor-keys": "8.58.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.58.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.58.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.58.2", "@typescript-eslint/types": "8.58.2", "@typescript-eslint/typescript-estree": "8.58.2", "@typescript-eslint/visitor-keys": "8.58.2", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.58.2", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.58.2", "@typescript-eslint/types": "^8.58.2", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.58.2", "", { "dependencies": { "@typescript-eslint/types": "8.58.2", "@typescript-eslint/visitor-keys": "8.58.2" } }, "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.58.2", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.58.2", "", { "dependencies": { "@typescript-eslint/types": "8.58.2", "@typescript-eslint/typescript-estree": "8.58.2", "@typescript-eslint/utils": "8.58.2", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.58.2", "", {}, "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.58.2", "", { "dependencies": { "@typescript-eslint/project-service": "8.58.2", "@typescript-eslint/tsconfig-utils": "8.58.2", "@typescript-eslint/types": "8.58.2", "@typescript-eslint/visitor-keys": "8.58.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.58.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.58.2", "@typescript-eslint/types": "8.58.2", "@typescript-eslint/typescript-estree": "8.58.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.2", "", { "dependencies": { "@typescript-eslint/types": "8.58.2", "eslint-visitor-keys": "^5.0.0" } }, "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], + + "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="], + + "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.11.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g=="], + + "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.11.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ=="], + + "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.11.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw=="], + + "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw=="], + + "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw=="], + + "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ=="], + + "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w=="], + + "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.11.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA=="], + + "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ=="], + + "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew=="], + + "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.11.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg=="], + + "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w=="], + + "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA=="], + + "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.11.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ=="], + + "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.11.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw=="], + + "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.11.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ=="], + + "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="], + + "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="], + + "@workflow/serde": ["@workflow/serde@4.1.0-beta.2", "", {}, "sha512-8kkeoQKLDaKXefjV5dbhBj2aErfKp1Mc4pb6tj8144cF+Em5SPbyMbyLCHp+BVrFfFVCBluCtMx+jjvaFVZGww=="], + + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ai": ["ai@6.0.158", "", { "dependencies": { "@ai-sdk/gateway": "3.0.95", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-gLTp1UXFtMqKUi3XHs33K7UFglbvojkxF/aq337TxnLGOhHIW9+GyP2jwW4hYX87f1es+wId3VQoPRRu9zEStQ=="], + + "ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], + + "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="], + + "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="], + + "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="], + + "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], + + "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], + + "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="], + + "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + + "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], + + "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "axe-core": ["axe-core@4.11.2", "", {}, "sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A=="], + + "better-auth": ["better-auth@1.6.2", "", { "dependencies": { "@better-auth/core": "1.6.2", "@better-auth/drizzle-adapter": "1.6.2", "@better-auth/kysely-adapter": "1.6.2", "@better-auth/memory-adapter": "1.6.2", "@better-auth/mongo-adapter": "1.6.2", "@better-auth/prisma-adapter": "1.6.2", "@better-auth/telemetry": "1.6.2", "@better-auth/utils": "0.4.0", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.1.1", "@noble/hashes": "^2.0.1", "better-call": "1.3.5", "defu": "^6.1.4", "jose": "^6.1.3", "kysely": "^0.28.14", "nanostores": "^1.1.1", "zod": "^4.3.6" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": ">=0.41.0", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-5nqDAIj5xexmnk+GjjdrBknJCabi1mlvsVWJbxs4usHreao4vNdxIxINWDzCyDF9iDR1ildRZdXWSiYPAvTHhA=="], + + "better-call": ["better-call@1.3.5", "", { "dependencies": { "@better-auth/utils": "^0.4.0", "@better-fetch/fetch": "^1.1.21", "rou3": "^0.7.12", "set-cookie-parser": "^3.0.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-kOFJkBP7utAQLEYrobZm3vkTH8mXq5GNgvjc5/XEST1ilVHaxXUXfeDeFlqoETMtyqS4+3/h4ONX2i++ebZrvA=="], + + "better-sqlite3": ["better-sqlite3@12.9.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ=="], + + "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], + + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + + "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], + + "brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + + "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001787", "", {}, "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + + "chat": ["chat@4.25.0", "", { "dependencies": { "@workflow/serde": "4.1.0-beta.2", "mdast-util-to-string": "^4.0.0", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "remend": "^1.2.1", "unified": "^11.0.5" } }, "sha512-QM8ex4Gpn8zYIPyQXh41Who6R9Wq3WcQeOjAy4EuR1m1ha0tASuzHkLQfjaTAGLgrgrThV0Zh5KKoH0S92iwNA=="], + + "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="], + + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], + + "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], + + "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + + "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], + + "date-fns-jalali": ["date-fns-jalali@4.1.0-0", "", {}, "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], + + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], + + "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], + + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], + + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], + + "drizzle-kit": ["drizzle-kit@0.31.10", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "tsx": "^4.21.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw=="], + + "drizzle-orm": ["drizzle-orm@0.45.2", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.335", "", {}, "sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q=="], + + "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + + "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="], + + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "es-abstract": ["es-abstract@1.24.2", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-iterator-helpers": ["es-iterator-helpers@1.3.2", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "math-intrinsics": "^1.1.0" } }, "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], + + "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], + + "es-toolkit": ["es-toolkit@1.45.1", "", {}, "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw=="], + + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.39.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ=="], + + "eslint-config-next": ["eslint-config-next@16.1.6", "", { "dependencies": { "@next/eslint-plugin-next": "16.1.6", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^7.0.0", "globals": "16.4.0", "typescript-eslint": "^8.46.0" }, "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA=="], + + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.10", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.16.1", "resolve": "^2.0.0-next.6" } }, "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ=="], + + "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="], + + "eslint-module-utils": ["eslint-module-utils@2.12.1", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw=="], + + "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="], + + "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="], + + "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.0.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + + "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], + + "exit-hook": ["exit-hook@5.1.0", "", {}, "sha512-INjr2xyxHo7bhAqf5ong++GZPPnpcuBcaXUKt03yf7Fie9yWD7FapL4teOU0+awQazGs5ucBh7xWs/AD+6nhog=="], + + "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + + "express-rate-limit": ["express-rate-limit@8.3.2", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], + + "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + + "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], + + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], + + "get-tsconfig": ["get-tsconfig@4.13.7", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q=="], + + "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], + + "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], + + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], + + "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], + + "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="], + + "hast-util-sanitize": ["hast-util-sanitize@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "unist-util-position": "^5.0.0" } }, "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + + "hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], + + "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], + + "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], + + "hono": ["hono@4.12.12", "", {}, "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q=="], + + "hono-openapi": ["hono-openapi@1.3.0", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.8.3", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-xDvCWpWEIv0weEmnl3EjRQzqbHIO8LnfzMuYOCmbuyE5aes6aXxLg4vM3ybnoZD5TiTUkA6PuRQPJs3R7WRBig=="], + + "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], + + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "immer": ["immer@10.2.0", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + + "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + + "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + + "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], + + "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], + + "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], + + "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], + + "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], + + "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], + + "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], + + "is-network-error": ["is-network-error@1.3.1", "", {}, "sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], + + "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], + + "is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], + + "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], + + "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], + + "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], + + "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], + + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], + + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], + + "js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + + "json-schema-to-zod": ["json-schema-to-zod@2.8.1", "", { "bin": { "json-schema-to-zod": "dist/cjs/cli.js" } }, "sha512-fRr1mHgZ7hboLKBUdR428gd9dIHUFGivUqOeiDcSmyXkNZCtB1uGaZLvsjZ4GaN5pwBIs+TGIOf6s+Rp5/R/zA=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + + "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], + + "kysely": ["kysely@0.28.16", "", {}, "sha512-3i5pmOiZvMDj00qhrIVbH0AnioVTx22DMP7Vn5At4yJO46iy+FM8Y/g61ltenLVSo3fiO8h8Q3QOFgf/gQ72ww=="], + + "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="], + + "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lru-cache": ["lru-cache@11.3.5", "", {}, "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw=="], + + "lucide-react": ["lucide-react@0.575.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-VuXgKZrk0uiDlWjGGXmKV6MSk9Yy4l10qgVvzGn2AWBx1Ylt0iBexKOAoA6I7JO3m+M9oeovJd3yYENfkUbOeg=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + + "marked": ["marked@17.0.6", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], + + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], + + "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], + + "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], + + "mdast-util-newline-to-break": ["mdast-util-newline-to-break@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-find-and-replace": "^3.0.0" } }, "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], + + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], + + "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "nanostores": ["nanostores@1.2.0", "", {}, "sha512-F0wCzbsH80G7XXo0Jd9/AVQC7ouWY6idUCTnMwW5t/Rv9W8qmO6endavDwg7TNp5GbugwSukFMVZqzPSrSMndg=="], + + "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], + + "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="], + + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + + "node-abi": ["node-abi@3.89.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA=="], + + "node-exports-info": ["node-exports-info@1.6.0", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw=="], + + "node-releases": ["node-releases@2.0.37", "", {}, "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg=="], + + "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="], + + "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], + + "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="], + + "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.5", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ=="], + + "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], + + "p-retry": ["p-retry@7.1.1", "", { "dependencies": { "is-network-error": "^1.1.0" } }, "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + + "parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], + + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-to-regexp": ["path-to-regexp@0.1.13", "", {}, "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "postcss": ["postcss@8.5.9", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw=="], + + "postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], + + "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.8.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q=="], + + "pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "radash": ["radash@12.1.1", "", {}, "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA=="], + + "radix-ui": ["radix-ui@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-accessible-icon": "1.1.7", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7", "@radix-ui/react-avatar": "1.1.10", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-context-menu": "2.2.16", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-dropdown-menu": "2.1.16", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-form": "0.1.8", "@radix-ui/react-hover-card": "1.1.15", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-menubar": "1.1.16", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-one-time-password-field": "0.1.8", "@radix-ui/react-password-toggle-field": "0.1.3", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-progress": "1.1.7", "@radix-ui/react-radio-group": "1.3.8", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-toolbar": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-escape-keydown": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + + "react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], + + "react-day-picker": ["react-day-picker@9.14.0", "", { "dependencies": { "@date-fns/tz": "^1.4.1", "@tabby_ai/hijri-converter": "1.0.5", "date-fns": "^4.1.0", "date-fns-jalali": "4.1.0-0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-tBaoDWjPwe0M5pGrum4H0SR6Lyk+BO9oHnp9JbKpGKW2mlraNPgP9BMfsg5pWpwrssARmeqk7YBl2oXutZTaHA=="], + + "react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="], + + "react-hook-form": ["react-hook-form@7.72.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-RhwBoy2ygeVZje+C+bwJ8g0NjTdBmDlJvAUHTxRjTmSUKPYsKfMphkS2sgEMotsY03bP358yEYlnUeZy//D9Ig=="], + + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="], + + "react-redux": ["react-redux@9.2.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-resizable-panels": ["react-resizable-panels@3.0.6", "", { "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew=="], + + "react-shiki": ["react-shiki@0.9.1", "", { "dependencies": { "clsx": "^2.1.1", "dequal": "^2.0.3", "hast-util-to-jsx-runtime": "^2.3.6", "shiki": "^3.11.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@types/react": ">=16.8.0", "@types/react-dom": ">=16.8.0", "react": ">= 16.8.0", "react-dom": ">= 16.8.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ln1PnISi7WaSlheSBRdxVruVbU1zMUkCmxe+vmbIvZSsHdfvOF5NBOgf1h4cCr6OjdR0dLAxmPKcx3tobdyxVA=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + + "recharts": ["recharts@3.8.1", "", { "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg=="], + + "redux": ["redux@5.0.1", "", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="], + + "redux-thunk": ["redux-thunk@3.1.0", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="], + + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], + + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], + + "rehype-harden": ["rehype-harden@1.1.8", "", { "dependencies": { "unist-util-visit": "^5.0.0" } }, "sha512-Qn7vR1xrf6fZCrkm9TDWi/AB4ylrHy+jqsNm1EHOAmbARYA6gsnVJBq/sdBh6kmT4NEZxH5vgIjrscefJAOXcw=="], + + "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], + + "rehype-sanitize": ["rehype-sanitize@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-sanitize": "^5.0.0" } }, "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg=="], + + "remark-breaks": ["remark-breaks@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-newline-to-break": "^2.0.0", "unified": "^11.0.0" } }, "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ=="], + + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + + "remend": ["remend@1.2.2", "", {}, "sha512-4ZJgIB9EG9fQE41mOJCRHMmnxDTKHWawQoJWZyUbZuj680wVyogu2ihnj8Edqm7vh2mo/TWHyEZpn2kqeDvS7w=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], + + "resolve": ["resolve@2.0.0-next.6", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="], + + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="], + + "secure-json-parse": ["secure-json-parse@2.7.0", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="], + + "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + + "set-cookie-parser": ["set-cookie-parser@3.1.0", "", {}, "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], + + "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], + + "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], + + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + + "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + + "streamdown": ["streamdown@2.4.0", "", { "dependencies": { "clsx": "^2.1.1", "hast-util-to-jsx-runtime": "^2.3.6", "html-url-attributes": "^3.0.1", "marked": "^17.0.1", "rehype-harden": "^1.1.8", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remend": "1.2.2", "tailwind-merge": "^3.4.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-fRk4HEYNznRLmxoVeT8wsGBwHF6/Yrdey6k+ZrE1Qtp4NyKwm7G/6e2Iw8penY4yLx31TlAHWT5Bsg1weZ9FZg=="], + + "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="], + + "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], + + "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="], + + "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], + + "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="], + + "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="], + + "strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], + + "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "swr": ["swr@2.4.1", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA=="], + + "tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="], + + "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="], + + "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="], + + "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], + + "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + + "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="], + + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + + "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "tokenx": ["tokenx@1.3.0", "", {}, "sha512-NLdXTEZkKiO0gZuLtMoZKjCXTREXeZZt8nnnNeyoXtNZAfG/GKGSbQtLU5STspc0rMSwcA+UJfWZkbNU01iKmQ=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + + "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], + + "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], + + "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], + + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], + + "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="], + + "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "typescript-eslint": ["typescript-eslint@8.58.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.58.2", "@typescript-eslint/parser": "8.58.2", "@typescript-eslint/typescript-estree": "8.58.2", "@typescript-eslint/utils": "8.58.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ=="], + + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], + + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "use-stick-to-bottom": ["use-stick-to-bottom@1.1.3", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-GgRLdeGhxBxpcbrBbEIEoOKUQ9d46/eaSII+wyv1r9Du+NbCn1W/OE+VddefvRP4+5w/1kATN/6g2/BAC/yowQ=="], + + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], + + "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="], + + "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], + + "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], + + "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], + + "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], + + "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], + + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "zod-from-json-schema": ["zod-from-json-schema@0.5.2", "", { "dependencies": { "zod": "^4.0.17" } }, "sha512-/dNaicfdhJTOuUd4RImbLUE2g5yrSzzDjI/S6C2vO2ecAGZzn9UcRVgtyLSnENSmAOBRiSpUdzDS6fDWX3Z35g=="], + + "zod-from-json-schema-v3": ["zod-from-json-schema@0.0.5", "", { "dependencies": { "zod": "^3.24.2" } }, "sha512-zYEoo86M1qpA1Pq6329oSyHLS785z/mTwfr9V1Xf/ZLhuuBGaMlDGu/pDVGVUe4H4oa1EFgWZT53DP0U3oT9CQ=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], + + "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@a2a-js/sdk/express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="], + + "@a2a-js/sdk/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + + "@ai-sdk/provider-utils-v5/@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], + + "@ai-sdk/provider-utils-v6/@ai-sdk/provider": ["@ai-sdk/provider@3.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-m9ka3ptkPQbaHHZHqDXDF9C9B5/Mav0KTdky1k2HZ3/nrW2t1AgObxIVPyGDWQNS9FXT/FS6PIoSjpcP/No8rQ=="], + + "@ai-sdk/ui-utils-v5/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], + + "@ai-sdk/ui-utils-v5/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], + + "@ai-sdk/ui-utils-v5/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@mastra/core/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + + "@mastra/core/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + + "@reduxjs/toolkit/immer": ["immer@11.1.4", "", {}, "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw=="], + + "@sindresorhus/slugify/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "@sindresorhus/transliterate/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + + "@typescript-eslint/typescript-estree/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], + + "ajv-formats/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "gray-matter/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + + "is-bun-module/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + + "node-abi/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + + "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + + "router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + + "sharp/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "tsx/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], + + "zod-from-json-schema-v3/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@a2a-js/sdk/express/accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + + "@a2a-js/sdk/express/body-parser": ["body-parser@1.20.4", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="], + + "@a2a-js/sdk/express/content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], + + "@a2a-js/sdk/express/cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="], + + "@a2a-js/sdk/express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "@a2a-js/sdk/express/finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="], + + "@a2a-js/sdk/express/fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], + + "@a2a-js/sdk/express/merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], + + "@a2a-js/sdk/express/qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="], + + "@a2a-js/sdk/express/send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="], + + "@a2a-js/sdk/express/serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], + + "@a2a-js/sdk/express/type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], + + "@mastra/core/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], + + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "gray-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], + + "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], + + "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="], + + "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="], + + "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="], + + "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="], + + "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="], + + "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="], + + "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="], + + "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="], + + "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="], + + "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="], + + "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="], + + "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="], + + "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="], + + "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="], + + "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="], + + "tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="], + + "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="], + + "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="], + + "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="], + + "tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="], + + "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="], + + "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="], + + "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="], + + "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + + "@a2a-js/sdk/express/accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "@a2a-js/sdk/express/accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + + "@a2a-js/sdk/express/body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + + "@a2a-js/sdk/express/body-parser/raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="], + + "@a2a-js/sdk/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "@a2a-js/sdk/express/type-is/media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + + "@a2a-js/sdk/express/type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "@a2a-js/sdk/express/accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "@a2a-js/sdk/express/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + } +} diff --git a/test-mastra/components/auth/login-form.tsx b/test-mastra/components/auth/login-form.tsx new file mode 100644 index 0000000..e87a78b --- /dev/null +++ b/test-mastra/components/auth/login-form.tsx @@ -0,0 +1,138 @@ +"use client"; + +import { useRef, useState } from "react"; +import { useRouter } from "next/navigation"; +import { authClient } from "@/lib/auth-client"; +import { + Alert, + AlertTitle, + AlertDescription, + Button, + Card, + CardContent, + CardDescription, + CardHeader, + Input, + Label, +} from "@arcadeai/design-system"; +import { Info } from "lucide-react"; + +export function LoginForm() { + const router = useRouter(); + const [isRegister, setIsRegister] = useState(true); + const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); + const formRef = useRef(null); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + setError(""); + setLoading(true); + + const form = new FormData(e.currentTarget); + const email = form.get("email") as string; + const password = form.get("password") as string; + + if (!email || !password) { + setError("Please fill in all fields."); + setLoading(false); + return; + } + + try { + if (isRegister) { + const { error: err } = await authClient.signUp.email({ email, password, name: "" }); + if (err) { + setError(err.message ?? "Something went wrong"); + return; + } + } else { + const { error: err } = await authClient.signIn.email({ email, password }); + if (err) { + setError(err.message ?? "Invalid credentials"); + return; + } + } + router.push("/dashboard"); + } catch { + setError("Network error. Please try again."); + } finally { + setLoading(false); + } + } + + return ( +
+
+ + Arcade Agent +
+ + + Two separate sign-ins + + This creates a local account for your agent app — it's just for session management + and stays in your own database. You'll connect your Arcade account on the next screen + to give the agent access to your tools. + + + + +
+ + +
+ + {isRegister ? "Get started with Arcade Agent" : "Welcome back to Arcade Agent"} + +
+ +
+
+ + +
+
+ + +
+ {error &&

{error}

} + +
+
+
+
+ ); +} diff --git a/test-mastra/components/dashboard/auth-prompt.tsx b/test-mastra/components/dashboard/auth-prompt.tsx new file mode 100644 index 0000000..ddffe35 --- /dev/null +++ b/test-mastra/components/dashboard/auth-prompt.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { useState } from "react"; +import { ShieldAlert, Loader2 } from "lucide-react"; +import { Button } from "@arcadeai/design-system"; + +interface AuthPromptProps { + toolName: string; + authUrl: string; + onContinue: () => void; +} + +export function AuthPrompt({ toolName, authUrl, onContinue }: AuthPromptProps) { + const [clicked, setClicked] = useState(false); + + return ( +
+
+ +

Authorization required

+
+

{toolName} needs permission to continue.

+ {clicked ? ( +
+ + Waiting for authorization… the agent will resume automatically. +
+ ) : ( +
+ + +
+ )} +
+ ); +} diff --git a/test-mastra/components/dashboard/empty-state.tsx b/test-mastra/components/dashboard/empty-state.tsx new file mode 100644 index 0000000..e0da73f --- /dev/null +++ b/test-mastra/components/dashboard/empty-state.tsx @@ -0,0 +1,66 @@ +"use client"; + +import { useState } from "react"; +import { Inbox, Info, Loader2, X } from "lucide-react"; +import { Alert, AlertTitle, AlertDescription, Button } from "@arcadeai/design-system"; + +const CALLOUT_KEY = "callout-how-planning-works"; + +interface EmptyStateProps { + onPlan: () => void; + loading: boolean; +} + +export function EmptyState({ onPlan, loading }: EmptyStateProps) { + const [showCallout, setShowCallout] = useState(() => + typeof window !== "undefined" ? localStorage.getItem(CALLOUT_KEY) !== "1" : true + ); + const dismissCallout = () => { + localStorage.setItem(CALLOUT_KEY, "1"); + setShowCallout(false); + }; + + return ( +
+ +
+

Ready to triage?

+

+ Your agent will scan your inbox, calendar, tasks, and PRs, then prioritize everything and + build your action plan. +

+
+ {showCallout && ( + + + + How this works + + + + When you click “Plan my day”, the agent connects to your Slack, Gmail, + Google Calendar, Linear, and GitHub through Arcade's MCP Gateway. It will first + check if any of those need authentication — if so, you'll be prompted to authorize + before the run starts. Then it reads your recent messages, emails, events, issues, and + pull requests, and classifies each item by priority and suggests next steps. + + + )} + +
+ ); +} diff --git a/test-mastra/components/dashboard/source-auth-gate.tsx b/test-mastra/components/dashboard/source-auth-gate.tsx new file mode 100644 index 0000000..d573d97 --- /dev/null +++ b/test-mastra/components/dashboard/source-auth-gate.tsx @@ -0,0 +1,128 @@ +"use client"; + +import { useState } from "react"; +import { Check, ArrowUpRight, Info, X } from "lucide-react"; +import { Alert, AlertTitle, AlertDescription, Button } from "@arcadeai/design-system"; +import type { SourceStatus } from "@/types/inbox"; +import { getSource } from "@/lib/sources"; + +const CALLOUT_KEY = "callout-why-authorize"; + +interface SourceAuthGateProps { + sourceStatuses: Record; + authUrls: { url: string; toolName?: string }[]; + skippedSources: Set; + onSkip: (source: string) => void; + onContinue: () => void; +} + +export function SourceAuthGate({ + sourceStatuses, + authUrls, + skippedSources, + onSkip, + onContinue, +}: SourceAuthGateProps) { + const [showCallout, setShowCallout] = useState(() => + typeof window !== "undefined" ? localStorage.getItem(CALLOUT_KEY) !== "1" : true + ); + const dismissCallout = () => { + localStorage.setItem(CALLOUT_KEY, "1"); + setShowCallout(false); + }; + + const pendingCount = Object.entries(sourceStatuses).filter( + ([source, status]) => status === "auth_required" && !skippedSources.has(source) + ).length; + + const canContinue = pendingCount === 0; + + return ( +
+
+

Connect your tools

+

+ The agent uses these tools to build your daily plan. Authorize each one you'd like to + include, or skip any you want to leave out. +

+
+ + {showCallout && ( + + + + Why authorize? + + + + Each tool connects on your behalf using OAuth — the agent only gets read access to scan + for items to triage. You can skip any source you don't use, and revoke access + anytime from your Arcade dashboard. + + + )} + +
+ {Object.entries(sourceStatuses).map(([source, status]) => { + const config = getSource(source); + const Icon = config.icon; + const authUrl = authUrls.find((a) => a.toolName === source)?.url; + const isSkipped = skippedSources.has(source); + const effectiveStatus: SourceStatus = isSkipped ? "skipped" : status; + + return ( +
+
+ + {config.label} +
+ +
+ {effectiveStatus === "connected" && ( + + + + + Connected + + )} + + {effectiveStatus === "skipped" && ( + Skipped + )} + + {effectiveStatus === "auth_required" && authUrl && ( + <> + + + + )} +
+
+ ); + })} +
+ + +
+ ); +} diff --git a/test-mastra/components/dashboard/stats-bar.tsx b/test-mastra/components/dashboard/stats-bar.tsx new file mode 100644 index 0000000..ac26410 --- /dev/null +++ b/test-mastra/components/dashboard/stats-bar.tsx @@ -0,0 +1,98 @@ +import { LayoutDashboard } from "lucide-react"; +import { Card, CardHeader, CardTitle, Skeleton } from "@arcadeai/design-system"; +import { cn } from "@/lib/utils"; +import { getSource, type IconComponent } from "@/lib/sources"; + +interface StatsBarProps { + stats: { + total: number; + bySource: Record; + }; + activeSource: string | null; + onSourceClick: (source: string | null) => void; + isLoading?: boolean; +} + +const gridColsClass: Record = { + 1: "grid-cols-1", + 2: "grid-cols-1 sm:grid-cols-2", + 3: "grid-cols-1 sm:grid-cols-3", + 4: "grid-cols-2 sm:grid-cols-4", + 5: "grid-cols-2 sm:grid-cols-5", + 6: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-6", +}; + +function StatCard({ + label, + count, + icon: Icon, + active, + onClick, +}: { + label: string; + count: number; + icon: IconComponent; + active: boolean; + onClick: () => void; +}) { + return ( + + +
+ {label} + +
+

{count}

+
+
+ ); +} + +export function StatsBar({ stats, activeSource, onSourceClick, isLoading }: StatsBarProps) { + const activeSources = Object.entries(stats.bySource).filter(([, count]) => count > 0); + const cardCount = 1 + activeSources.length; + const gridClass = gridColsClass[Math.min(cardCount, 6)] || gridColsClass[6]; + + return ( +
+ {isLoading ? ( + + +
+ Total + +
+ +
+
+ ) : ( + onSourceClick(null)} + /> + )} + {activeSources.map(([source, count]) => { + const config = getSource(source); + return ( + onSourceClick(activeSource === source ? null : source)} + /> + ); + })} +
+ ); +} diff --git a/test-mastra/components/dashboard/task-card.tsx b/test-mastra/components/dashboard/task-card.tsx new file mode 100644 index 0000000..e717bd7 --- /dev/null +++ b/test-mastra/components/dashboard/task-card.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { Card, CardContent, Badge } from "@arcadeai/design-system"; +import type { InboxItem } from "@/types/inbox"; +import { getSource } from "@/lib/sources"; + +const priorityConfig: Record< + InboxItem["priority"], + { label: string; variant: "destructive" | "secondary" | "outline"; className?: string } +> = { + P0: { label: "P0", variant: "destructive" }, + P1: { + label: "P1", + variant: "secondary", + className: + "bg-amber-100 text-amber-800 hover:bg-amber-100 dark:bg-amber-950 dark:text-amber-200 dark:hover:bg-amber-950", + }, + P2: { label: "P2", variant: "secondary" }, + FYI: { label: "FYI", variant: "outline" }, +}; + +const categoryLabels: Record = { + NEEDS_REPLY: "Needs Reply", + NEEDS_FEEDBACK: "Needs Feedback", + NEEDS_DECISION: "Needs Decision", + NEEDS_REVIEW: "Needs Review", + ATTEND: "Attend", + FYI: "FYI", + IGNORE: "Ignore", +}; + +interface TaskCardProps { + item: InboxItem; +} + +export function TaskCard({ item }: TaskCardProps) { + const priority = priorityConfig[item.priority]; + const source = getSource(item.source); + const SourceIcon = source.icon; + const subtitle = item.sourceDetail || item.participants?.map((p) => p.name).join(", "); + + const formattedTime = item.scheduledTime + ? new Date(item.scheduledTime).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) + : null; + + return ( + + +
+ + + {source.label} + + + {priority.label} + + {categoryLabels[item.category] || item.category} + {formattedTime && ( + + {formattedTime} + + )} + {item.effort} +
+ + {item.url ? ( + + {item.summary} + + ) : ( +

{item.summary}

+ )} + +
+ {subtitle && {subtitle}} + + {item.suggestedNextStep} + +
+
+
+ ); +} diff --git a/test-mastra/components/dashboard/task-list.tsx b/test-mastra/components/dashboard/task-list.tsx new file mode 100644 index 0000000..eee181f --- /dev/null +++ b/test-mastra/components/dashboard/task-list.tsx @@ -0,0 +1,27 @@ +"use client"; + +import type { InboxItem } from "@/types/inbox"; +import { TaskCard } from "@/components/dashboard/task-card"; + +interface TaskListProps { + items: InboxItem[]; +} + +export function TaskList({ items }: TaskListProps) { + return ( +
+
+

Today's Tasks

+ + {items.length} {items.length === 1 ? "item" : "items"} + +
+ +
+ {items.map((item) => ( + + ))} +
+
+ ); +} diff --git a/test-mastra/components/layout/header.tsx b/test-mastra/components/layout/header.tsx new file mode 100644 index 0000000..39fae4d --- /dev/null +++ b/test-mastra/components/layout/header.tsx @@ -0,0 +1,76 @@ +"use client"; + +import { useSyncExternalStore } from "react"; +import { useTheme } from "next-themes"; +import { LogOut, Moon, Sun } from "lucide-react"; +import { Button } from "@arcadeai/design-system"; + +const emptySubscribe = () => () => {}; + +interface HeaderProps { + onLogout?: () => void; +} + +function ArcadeLogo() { + return ( +
+ + Arcade Agent +
+ ); +} + +function ThemeToggle() { + const { theme, setTheme } = useTheme(); + const mounted = useSyncExternalStore( + emptySubscribe, + () => true, + () => false + ); + + if (!mounted) { + return ( + + ); + } + + return ( + + ); +} + +export function Header({ onLogout }: HeaderProps) { + const today = new Date().toLocaleDateString("en-US", { + weekday: "long", + month: "short", + day: "numeric", + }); + + return ( +
+
+ + {today} +
+
+ + {onLogout && ( + + )} +
+
+ ); +} diff --git a/test-mastra/components/theme-provider.tsx b/test-mastra/components/theme-provider.tsx new file mode 100644 index 0000000..f2df7ce --- /dev/null +++ b/test-mastra/components/theme-provider.tsx @@ -0,0 +1,10 @@ +"use client"; + +import { ThemeProvider as NextThemesProvider } from "next-themes"; + +export function ThemeProvider({ + children, + ...props +}: React.ComponentProps) { + return {children}; +} diff --git a/test-mastra/drizzle.config.ts b/test-mastra/drizzle.config.ts new file mode 100644 index 0000000..2dc05ad --- /dev/null +++ b/test-mastra/drizzle.config.ts @@ -0,0 +1,10 @@ +import type { Config } from "drizzle-kit"; + +export default { + schema: "./lib/db/schema.ts", + out: "./drizzle/migrations", + dialect: "sqlite", + dbCredentials: { + url: process.env.DATABASE_URL || "local.db", + }, +} satisfies Config; diff --git a/test-mastra/eslint.config.mjs b/test-mastra/eslint.config.mjs new file mode 100644 index 0000000..05e726d --- /dev/null +++ b/test-mastra/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/test-mastra/hooks/use-arcade-connection.ts b/test-mastra/hooks/use-arcade-connection.ts new file mode 100644 index 0000000..829a309 --- /dev/null +++ b/test-mastra/hooks/use-arcade-connection.ts @@ -0,0 +1,79 @@ +"use client"; + +import { useCallback, useEffect, useRef, useState } from "react"; +import { useRouter } from "next/navigation"; +import type { ArcadeStatus } from "@/types/dashboard"; + +function parseArcadeResponse(data: { + connected?: boolean; + authUrl?: string; + error?: string; +}): ArcadeStatus { + if (data.connected) return { state: "connected" }; + if (data.authUrl) return { state: "needs_auth", authUrl: data.authUrl }; + return { + state: "error", + message: data.error || "Could not connect to Arcade Gateway.", + }; +} + +export function useArcadeConnection(): { + arcadeStatus: ArcadeStatus; + retryConnection: () => void; +} { + const router = useRouter(); + const [arcadeStatus, setArcadeStatus] = useState({ + state: "checking", + }); + const connectInFlight = useRef(false); + const authInProgress = useRef(false); + const lastCheckRef = useRef(0); + + const checkConnection = useCallback( + async (opts?: { isRetry?: boolean }) => { + if (connectInFlight.current) return; + if (opts?.isRetry) { + authInProgress.current = false; + setArcadeStatus({ state: "checking" }); + } else if (authInProgress.current) { + return; + } + lastCheckRef.current = Date.now(); + connectInFlight.current = true; + try { + const r = await fetch("/api/auth/arcade/connect", { method: "POST" }); + if (r.status === 401) { + router.push("/"); + return; + } + const data = await r.json(); + const status = parseArcadeResponse(data); + authInProgress.current = status.state === "needs_auth"; + setArcadeStatus(status); + } catch { + setArcadeStatus({ + state: "error", + message: "Failed to check Arcade connection.", + }); + } finally { + connectInFlight.current = false; + } + }, + [router] + ); + + useEffect(() => { + checkConnection(); + const onFocus = () => { + if (Date.now() - lastCheckRef.current < 2000) return; + authInProgress.current = false; // User returned from OAuth tab — re-check + checkConnection(); + }; + window.addEventListener("focus", onFocus); + return () => window.removeEventListener("focus", onFocus); + }, [checkConnection]); + + const retryConnection = useCallback(() => checkConnection({ isRetry: true }), [checkConnection]); + + return { arcadeStatus, retryConnection }; +} diff --git a/test-mastra/hooks/use-plan-stream.ts b/test-mastra/hooks/use-plan-stream.ts new file mode 100644 index 0000000..7761119 --- /dev/null +++ b/test-mastra/hooks/use-plan-stream.ts @@ -0,0 +1,143 @@ +"use client"; + +import { useCallback, useRef, useState } from "react"; +import type { InboxItem, PlanEvent } from "@/types/inbox"; + +interface PlanStreamCallbacks { + onAuthRequired?: (authUrl: string, toolName?: string) => void; + onElicitation?: (elicitationId: string, authUrl: string, message: string) => void; + onSourcesDone?: () => void; +} + +export function usePlanStream(callbacks?: PlanStreamCallbacks): { + items: InboxItem[]; + stats: { total: number; bySource: Record }; + loading: boolean; + error: string | null; + showError: (message: string) => void; + clearError: () => void; + activeSource: string | null; + setActiveSource: React.Dispatch>; + statusMessage: string | null; + planRan: boolean; + resetPlan: () => void; + handlePlan: () => Promise; +} { + const [items, setItems] = useState([]); + const [stats, setStats] = useState<{ total: number; bySource: Record }>({ + total: 0, + bySource: {}, + }); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [activeSource, setActiveSource] = useState(null); + const [statusMessage, setStatusMessage] = useState(null); + const abortRef = useRef(null); + const [planRan, setPlanRan] = useState(false); + + // Keep callbacks in a ref so handlePlan is stable even when callbacks change + const callbacksRef = useRef(callbacks); + callbacksRef.current = callbacks; + + const showError = useCallback((message: string) => setError(message), []); + const clearError = useCallback(() => setError(null), []); + const resetPlan = useCallback(() => setPlanRan(false), []); + + const handlePlan = useCallback(async () => { + setLoading(true); + setError(null); + setItems([]); + setStats({ total: 0, bySource: {} }); + setStatusMessage(null); + + abortRef.current?.abort(); + const controller = new AbortController(); + abortRef.current = controller; + + try { + const res = await fetch("/api/plan", { + method: "POST", + signal: controller.signal, + }); + + if (!res.ok || !res.body) { + throw new Error(`Plan request failed (${res.status})`); + } + + const reader = res.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split("\n"); + buffer = lines.pop() ?? ""; + + for (const line of lines) { + if (!line.trim()) continue; + try { + const event = JSON.parse(line) as PlanEvent; + switch (event.type) { + case "task": + setItems((prev) => [...prev, event.data]); + break; + case "summary": + setStats({ + total: event.data.total, + bySource: event.data.bySource, + }); + break; + case "auth_required": + callbacksRef.current?.onAuthRequired?.(event.authUrl, event.toolName); + break; + case "elicitation": + callbacksRef.current?.onElicitation?.( + event.elicitationId, + event.authUrl, + event.message + ); + break; + case "status": + setStatusMessage(event.message); + break; + case "error": + setError(event.message); + break; + case "done": + callbacksRef.current?.onSourcesDone?.(); + break; + } + } catch { + // skip malformed lines + } + } + } + } catch (err) { + if ((err as Error).name !== "AbortError") { + setError(err instanceof Error ? err.message : "Something went wrong"); + } + } finally { + setLoading(false); + setStatusMessage(null); + setPlanRan(true); + } + }, []); + + return { + items, + stats, + loading, + error, + showError, + clearError, + activeSource, + setActiveSource, + statusMessage, + planRan, + resetPlan, + handlePlan, + }; +} diff --git a/test-mastra/hooks/use-source-check.ts b/test-mastra/hooks/use-source-check.ts new file mode 100644 index 0000000..682a956 --- /dev/null +++ b/test-mastra/hooks/use-source-check.ts @@ -0,0 +1,127 @@ +"use client"; + +import { useCallback, useEffect, useRef, useState } from "react"; +import type { SourceStatus } from "@/types/inbox"; +import type { SourceCheckPhase } from "@/types/dashboard"; + +export function useSourceCheck(options: { enabled: boolean }): { + sourceCheckPhase: SourceCheckPhase; + authGateActive: boolean; + setAuthGateActive: (active: boolean) => void; + skippedSources: Set; + skipSource: (source: string) => void; + sourceStatuses: Record; + authUrls: { url: string; toolName?: string }[]; + markSourceAuthRequired: (toolName: string) => void; + markAllCheckingAsConnected: () => void; + addAuthUrl: (url: string, toolName?: string) => void; + dismissAuthUrl: (url: string) => void; + resetForNewPlan: () => void; +} { + const { enabled } = options; + + const [sourceCheckPhase, setSourceCheckPhase] = useState("idle"); + const [authGateActive, setAuthGateActive] = useState(false); + const [skippedSources, setSkippedSources] = useState>(new Set()); + const [sourceStatuses, setSourceStatuses] = useState>({}); + const [authUrls, setAuthUrls] = useState<{ url: string; toolName?: string }[]>([]); + const sourceCheckInFlight = useRef(false); + const initialCheckDone = useRef(false); + + // --- Check source auth status via WhoAmI tools --- + const checkSources = useCallback(async () => { + if (sourceCheckInFlight.current) return; + sourceCheckInFlight.current = true; + const isInitial = !initialCheckDone.current; + initialCheckDone.current = true; // mark immediately so re-checks don't re-enter initial path + if (isInitial) setSourceCheckPhase("checking"); + try { + const res = await fetch("/api/sources", { method: "POST" }); + if (!res.ok) { + if (isInitial) setSourceCheckPhase("done"); + return; + } + const data = await res.json(); + const statuses: Record = {}; + const urls: { url: string; toolName?: string }[] = []; + for (const [source, info] of Object.entries( + data.sources as Record + )) { + statuses[source] = info.status as SourceStatus; + if (info.authUrl) urls.push({ url: info.authUrl, toolName: source }); + } + setSourceStatuses(statuses); + setAuthUrls(urls); + if (isInitial) { + const hasAuthRequired = Object.values(statuses).some((s) => s === "auth_required"); + if (hasAuthRequired) setAuthGateActive(true); + setSourceCheckPhase("done"); + } + } catch { + if (isInitial) setSourceCheckPhase("done"); + } finally { + sourceCheckInFlight.current = false; + } + }, []); + + useEffect(() => { + if (!enabled) return; + checkSources(); + }, [enabled, checkSources]); + + // Re-check sources when user returns from an auth tab (while gate is active) + useEffect(() => { + if (!authGateActive) return; + const onFocus = () => { + checkSources(); + }; + window.addEventListener("focus", onFocus); + return () => window.removeEventListener("focus", onFocus); + }, [authGateActive, checkSources]); + + const skipSource = (source: string) => { + setSkippedSources((prev) => new Set([...prev, source])); + }; + + const markSourceAuthRequired = useCallback((toolName: string) => { + setSourceStatuses((prev) => ({ ...prev, [toolName]: "auth_required" })); + }, []); + + const markAllCheckingAsConnected = useCallback(() => { + setSourceStatuses((prev) => { + const next = { ...prev }; + for (const key of Object.keys(next)) { + if (next[key] === "checking") next[key] = "connected"; + } + return next; + }); + }, []); + + const addAuthUrl = useCallback((url: string, toolName?: string) => { + setAuthUrls((prev) => (prev.some((a) => a.url === url) ? prev : [...prev, { url, toolName }])); + }, []); + + const dismissAuthUrl = useCallback((url: string) => { + setAuthUrls((prev) => prev.filter((a) => a.url !== url)); + }, []); + + const resetForNewPlan = useCallback(() => { + setAuthUrls([]); + setSourceStatuses({}); + }, []); + + return { + sourceCheckPhase, + authGateActive, + setAuthGateActive, + skippedSources, + skipSource, + sourceStatuses, + authUrls, + markSourceAuthRequired, + markAllCheckingAsConnected, + addAuthUrl, + dismissAuthUrl, + resetForNewPlan, + }; +} diff --git a/test-mastra/lib/auth-client.ts b/test-mastra/lib/auth-client.ts new file mode 100644 index 0000000..5a5d28c --- /dev/null +++ b/test-mastra/lib/auth-client.ts @@ -0,0 +1,16 @@ +import { createAuthClient } from "better-auth/react"; + +/** + * Better Auth client — use in React client components for sign-in, sign-up, + * sign-out, and session state. + * + * Usage: + * import { authClient } from "@/lib/auth-client"; + * const { data: session } = authClient.useSession(); + * await authClient.signIn.email({ email, password }); + * await authClient.signUp.email({ email, password, name: "" }); + * await authClient.signOut(); + */ +export const authClient = createAuthClient({ + baseURL: process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:8765", +}); diff --git a/test-mastra/lib/auth.ts b/test-mastra/lib/auth.ts new file mode 100644 index 0000000..6d3e09d --- /dev/null +++ b/test-mastra/lib/auth.ts @@ -0,0 +1,29 @@ +import { betterAuth } from "better-auth"; +import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { headers } from "next/headers"; +import { db } from "@/lib/db"; +import * as schema from "@/lib/db/schema"; + +export const auth = betterAuth({ + baseURL: process.env.NEXT_PUBLIC_APP_URL ?? `http://localhost:${process.env.PORT ?? 8765}`, + database: drizzleAdapter(db, { + provider: "sqlite", + schema, + }), + emailAndPassword: { + enabled: true, + minPasswordLength: 8, + }, + trustedOrigins: [process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:8765"], +}); + +/** + * Retrieve the current session user (server-side, reads from request cookies). + * + * Drop-in replacement for the previous bcrypt+session getSession() helper. + * Returns the Better Auth user object, or null when not authenticated. + */ +export async function getSession() { + const session = await auth.api.getSession({ headers: await headers() }); + return session?.user ?? null; +} diff --git a/test-mastra/lib/db/index.ts b/test-mastra/lib/db/index.ts new file mode 100644 index 0000000..e562cd2 --- /dev/null +++ b/test-mastra/lib/db/index.ts @@ -0,0 +1,11 @@ +import { drizzle } from "drizzle-orm/better-sqlite3"; +import Database from "better-sqlite3"; +import * as schema from "./schema"; + +// Strip common URL prefixes so DATABASE_URL works with both +// plain paths ("local.db") and URL formats ("file:local.db") +let dbPath = process.env.DATABASE_URL || "local.db"; +dbPath = dbPath.replace(/^file:/, "").replace(/^sqlite[^:]*:\/\/\//, ""); + +const sqlite = new Database(dbPath); +export const db = drizzle(sqlite, { schema }); diff --git a/test-mastra/lib/db/schema.ts b/test-mastra/lib/db/schema.ts new file mode 100644 index 0000000..94d6d45 --- /dev/null +++ b/test-mastra/lib/db/schema.ts @@ -0,0 +1,59 @@ +import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; + +// Better Auth core tables — required for email/password authentication. +// Do not rename these tables or their columns. +// See: https://www.better-auth.com/docs/adapters/drizzle +export const user = sqliteTable("user", { + id: text("id").primaryKey(), + name: text("name").notNull(), + email: text("email").notNull().unique(), + emailVerified: integer("email_verified", { mode: "boolean" }).notNull(), + image: text("image"), + createdAt: integer("created_at", { mode: "timestamp" }).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(), +}); + +export const session = sqliteTable("session", { + id: text("id").primaryKey(), + expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(), + token: text("token").notNull().unique(), + createdAt: integer("created_at", { mode: "timestamp" }).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), +}); + +export const account = sqliteTable("account", { + id: text("id").primaryKey(), + accountId: text("account_id").notNull(), + providerId: text("provider_id").notNull(), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + idToken: text("id_token"), + accessTokenExpiresAt: integer("access_token_expires_at", { mode: "timestamp" }), + refreshTokenExpiresAt: integer("refresh_token_expires_at", { mode: "timestamp" }), + scope: text("scope"), + password: text("password"), + createdAt: integer("created_at", { mode: "timestamp" }).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(), +}); + +export const verification = sqliteTable("verification", { + id: text("id").primaryKey(), + identifier: text("identifier").notNull(), + value: text("value").notNull(), + expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(), + createdAt: integer("created_at", { mode: "timestamp" }), + updatedAt: integer("updated_at", { mode: "timestamp" }), +}); + +// AI-EDIT-SAFE: application schema extension +// --- CUSTOMIZATION POINT --- +// Add more tables here for your domain. +// See: https://orm.drizzle.team/docs/column-types/sqlite diff --git a/test-mastra/lib/sources.ts b/test-mastra/lib/sources.ts new file mode 100644 index 0000000..f87d80c --- /dev/null +++ b/test-mastra/lib/sources.ts @@ -0,0 +1,86 @@ +import type { ComponentType, SVGProps } from "react"; +import { + Slack, + Github, + GoogleCalendar, + Linear, + Gmail, +} from "@arcadeai/design-system/components/ui/atoms/icons"; +import { Globe } from "lucide-react"; + +export type IconComponent = ComponentType>; + +export interface SourceConfig { + icon: IconComponent; + label: string; + className: string; + /** Regex that matches tool names belonging to this source (e.g. "Slack_ListMessages") */ + pattern: RegExp; +} + +// ────────────────────────────────────────────── +// CUSTOMIZATION POINT — add new sources here +// ────────────────────────────────────────────── +export const sources: Record = { + slack: { + pattern: /^slack[._]/i, + icon: Slack, + label: "Slack", + className: + "bg-purple-100 border-purple-200 text-purple-900 dark:bg-purple-950 dark:border-purple-900 dark:text-purple-200", + }, + google_calendar: { + pattern: /^(google|googlecalendar|calendar)[._]/i, + icon: GoogleCalendar, + label: "Calendar", + className: + "bg-blue-100 border-blue-200 text-blue-900 dark:bg-blue-950 dark:border-blue-900 dark:text-blue-200", + }, + linear: { + pattern: /^linear[._]/i, + icon: Linear, + label: "Linear", + className: + "bg-indigo-100 border-indigo-200 text-indigo-900 dark:bg-indigo-950 dark:border-indigo-900 dark:text-indigo-200", + }, + github: { + pattern: /^git(hub)?[._]/i, + icon: Github, + label: "GitHub", + className: + "bg-gray-100 border-gray-300 text-gray-900 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200", + }, + gmail: { + pattern: /^gmail[._]/i, + icon: Gmail, + label: "Gmail", + className: + "bg-red-100 border-red-200 text-red-900 dark:bg-red-950 dark:border-red-900 dark:text-red-200", + }, +}; + +const defaultSource: Omit = { + icon: Globe, + label: "Other", + className: + "bg-gray-100 border-gray-200 text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-300", +}; + +/** Look up source config by key, with fallback for unknown sources. */ +export function getSource(key: string): Omit { + if (sources[key]) return sources[key]; + return { + ...defaultSource, + label: key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()), + }; +} + +/** Map a tool name (e.g. "Slack_ListConversations") to a source key. */ +export function mapToolToSource(toolName?: string): string { + if (!toolName) return "other"; + for (const [key, config] of Object.entries(sources)) { + if (config.pattern.test(toolName)) return key; + } + const namespace = toolName.split(/[._]/)[0]; + return namespace ? namespace.toLowerCase() : "other"; +} diff --git a/test-mastra/lib/utils.ts b/test-mastra/lib/utils.ts new file mode 100644 index 0000000..a5ef193 --- /dev/null +++ b/test-mastra/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/test-mastra/next.config.ts b/test-mastra/next.config.ts new file mode 100644 index 0000000..ebe12d7 --- /dev/null +++ b/test-mastra/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + serverExternalPackages: ["better-sqlite3", "@mastra/core"], +}; + +export default nextConfig; diff --git a/test-mastra/package.json b/test-mastra/package.json new file mode 100644 index 0000000..c428dd9 --- /dev/null +++ b/test-mastra/package.json @@ -0,0 +1,57 @@ +{ + "name": "test-mastra", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --port ${PORT:-8765}", + "build": "next build", + "start": "next start", + "doctor": "node scripts/doctor.mjs", + "lint": "eslint", + "typecheck": "tsc --noEmit", + "format": "prettier --write .", + "format:check": "prettier --check ." + }, + "dependencies": { + "@ai-sdk/anthropic": "^3.0.0", + "@ai-sdk/mcp": "^1.0.36", + "@ai-sdk/openai": "^3.0.29", + "@ai-sdk/react": "^3.0.92", + "@mastra/ai-sdk": "^1.3.3", + "@mastra/core": "^1.24.1", + "@mastra/mcp": "^1.4.2", + "@modelcontextprotocol/sdk": "^1.27.1", + "ai": "^6.0.90", + "@better-auth/drizzle-adapter": "^1.5.3", + "better-auth": "^1.5.3", + "better-sqlite3": "^12.6.2", + "@arcadeai/design-system": "^3.30.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "drizzle-orm": "^0.45.1", + "lucide-react": "^0.575.0", + "next": "16.1.6", + "next-themes": "^0.4.6", + "radix-ui": "^1.4.3", + "react": "19.2.3", + "react-dom": "19.2.3", + "react-markdown": "^10.1.0", + "remark-gfm": "^4.0.1", + "tailwind-merge": "^3.5.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@tailwindcss/typography": "^0.5.16", + "@types/better-sqlite3": "^7.6.13", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "drizzle-kit": "^0.31.9", + "eslint": "^9", + "eslint-config-next": "16.1.6", + "tailwindcss": "^4", + "tw-animate-css": "^1.4.0", + "prettier": "^3", + "typescript": "^5" + } +} diff --git a/test-mastra/postcss.config.mjs b/test-mastra/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/test-mastra/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/test-mastra/scripts/doctor.mjs b/test-mastra/scripts/doctor.mjs new file mode 100644 index 0000000..c244bd2 --- /dev/null +++ b/test-mastra/scripts/doctor.mjs @@ -0,0 +1,112 @@ +import { existsSync, readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const ENV_PATH = resolve(process.cwd(), ".env"); +const REQUIRED_TOOLKITS = ["Slack", "Google Calendar", "Linear", "GitHub", "Gmail"]; +const REQUIRED_TOOLS = [ + "Slack_ListConversations", + "Slack_GetMessages", + "Slack_GetConversationMetadata", + "Slack_WhoAmI", + "GoogleCalendar_ListEvents", + "GoogleCalendar_ListCalendars", + "GoogleCalendar_WhoAmI", + "Linear_GetNotifications", + "Linear_GetRecentActivity", + "Linear_ListIssues", + "Linear_GetIssue", + "Linear_ListProjects", + "Linear_GetProject", + "Linear_WhoAmI", + "Github_ListNotifications", + "Github_GetNotificationSummary", + "Github_ListPullRequests", + "Github_GetPullRequest", + "Github_GetUserOpenItems", + "Github_GetUserRecentActivity", + "Github_GetReviewWorkload", + "Github_GetIssue", + "Github_WhoAmI", + "Gmail_ListEmails", + "Gmail_ListThreads", + "Gmail_GetThread", + "Gmail_SearchThreads", + "Gmail_WhoAmI", +]; + +function parseEnvFile(path) { + if (!existsSync(path)) return {}; + const raw = readFileSync(path, "utf8"); + const out = {}; + for (const line of raw.split(/\r?\n/)) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) continue; + const idx = trimmed.indexOf("="); + if (idx === -1) continue; + const key = trimmed.slice(0, idx).trim(); + const value = trimmed.slice(idx + 1).trim(); + out[key] = value; + } + return out; +} + +async function checkGatewayReachability(url) { + try { + const res = await fetch(url, { method: "GET" }); + // 200/401/403 all indicate the endpoint is reachable. + return [200, 401, 403].includes(res.status); + } catch { + return false; + } +} + +async function main() { + const env = parseEnvFile(ENV_PATH); + const gatewayUrl = env.ARCADE_GATEWAY_URL || process.env.ARCADE_GATEWAY_URL || ""; + const openAi = env.OPENAI_API_KEY || process.env.OPENAI_API_KEY || ""; + const anthropic = env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY || ""; + + const errors = []; + + if (!existsSync(ENV_PATH)) { + errors.push("Missing .env file. Run: cp .env.example .env"); + } + + if (!gatewayUrl) { + errors.push( + "Missing ARCADE_GATEWAY_URL. Create a gateway at https://app.arcade.dev/mcp-gateways and set it in .env" + ); + } + + if (!openAi && !anthropic) { + errors.push("Missing LLM key. Set OPENAI_API_KEY or ANTHROPIC_API_KEY in .env"); + } + + if (errors.length === 0 && gatewayUrl) { + const reachable = await checkGatewayReachability(gatewayUrl); + if (!reachable) { + errors.push(`Gateway not reachable at ${gatewayUrl}`); + } + } + + if (errors.length > 0) { + console.error("\nDoctor found setup issues:\n"); + for (const err of errors) console.error(`- ${err}`); + console.error("\nRecommended minimum toolkits (enable only needed tools):"); + for (const toolkit of REQUIRED_TOOLKITS) console.error(`- ${toolkit}`); + console.error("\nRecommended minimum tools (exact names):"); + for (const tool of REQUIRED_TOOLS) console.error(`- ${tool}`); + process.exit(1); + } + + console.log("Doctor check passed."); + console.log("Recommended minimum toolkits (enable only needed tools):"); + for (const toolkit of REQUIRED_TOOLKITS) console.log(`- ${toolkit}`); + console.log("Recommended minimum tools (exact names):"); + for (const tool of REQUIRED_TOOLS) console.log(`- ${tool}`); +} + +main().catch((err) => { + console.error("Doctor failed:", err); + process.exit(1); +}); diff --git a/test-mastra/src/mastra/agents/index.ts b/test-mastra/src/mastra/agents/index.ts new file mode 100644 index 0000000..73911c4 --- /dev/null +++ b/test-mastra/src/mastra/agents/index.ts @@ -0,0 +1 @@ +export { triageAgent } from "./triage-agent"; diff --git a/test-mastra/src/mastra/agents/system-prompt.md b/test-mastra/src/mastra/agents/system-prompt.md new file mode 100644 index 0000000..959fdd7 --- /dev/null +++ b/test-mastra/src/mastra/agents/system-prompt.md @@ -0,0 +1,34 @@ +You are a daily planning and triage assistant. You help users quickly process items from their connected services — messages, calendar events, tasks, PRs, and emails. + +WORKFLOW: + +1. Check what tools are available and scan 3-5 items from each source. +2. Read recent activity from each item. +3. Classify each item and present results. +4. Ask the user to reply "continue" for the next batch. + +CLASSIFICATION: + +- Category: NEEDS_REPLY | NEEDS_FEEDBACK | NEEDS_DECISION | NEEDS_REVIEW | ATTEND | FYI | IGNORE +- Priority: P0 (urgent) | P1 (important) | P2 (can wait) | FYI +- Effort: XS (<5min) | S (5-15min) | M (15-30min) | L (>30min) + +OUTPUT FORMAT (one line per item): +**[Source] item-name** | CATEGORY | PRIORITY | EFFORT - Brief summary + +After each batch, show a summary and say: Reply "continue" to process the next batch. + +RULES: + +- Make at most 3-5 tool calls per response, then show results. +- Direct mentions or assignments to the user are P0/P1. Unread items get higher priority. +- Calendar events happening soon are P0/P1. Use ATTEND category for meetings. +- Code reviews (PRs) use NEEDS_REVIEW category. +- If a source has no recent activity, note it and move on. +- Handle errors gracefully — skip failed sources and continue. + +OAUTH HANDLING: +When a tool returns an authorization response with a URL, tell the user: +"Please visit this URL to grant access: [url]". Then wait for them to confirm. + +Start by checking what tools are available and fetching items from each source. diff --git a/test-mastra/src/mastra/agents/triage-agent.ts b/test-mastra/src/mastra/agents/triage-agent.ts new file mode 100644 index 0000000..f1efb50 --- /dev/null +++ b/test-mastra/src/mastra/agents/triage-agent.ts @@ -0,0 +1,39 @@ +import { Agent } from "@mastra/core/agent"; +import { readFileSync } from "fs"; +import { join } from "path"; +import { openai } from "@ai-sdk/openai"; +import { anthropic } from "@ai-sdk/anthropic"; +import { mcpClient } from "../tools/arcade"; + +// AI-EDIT-SAFE: model selection +// --- CUSTOMIZATION POINT --- +// The model is selected based on which API key you set in .env. +// Set ANTHROPIC_API_KEY to use Claude, or OPENAI_API_KEY to use GPT. +// If both are set, Anthropic takes priority. +export function getModel() { + if (process.env.ANTHROPIC_API_KEY) { + return anthropic("claude-sonnet-4-20250514"); + } + return openai("gpt-4.1"); +} + +// --- CUSTOMIZATION POINT --- +// Edit system-prompt.md (in this directory) to change the agent's purpose. +// For example, you could make a GitHub PR review agent, a calendar +// scheduling assistant, or a Gmail drafting bot — just update the +// system prompt and configure matching tools in your Arcade Gateway. +const instructions = readFileSync( + join(process.cwd(), "src/mastra/agents/system-prompt.md"), + "utf-8" +); + +// AI-EDIT-CAUTION: keep stable unless changing runtime wiring. +export const systemPrompt = instructions; + +export const triageAgent = new Agent({ + id: "slack-triage", + name: "Slack Triage Agent", + instructions, + model: getModel(), + tools: async () => mcpClient.listTools(), +}); diff --git a/test-mastra/src/mastra/index.ts b/test-mastra/src/mastra/index.ts new file mode 100644 index 0000000..7aa0db6 --- /dev/null +++ b/test-mastra/src/mastra/index.ts @@ -0,0 +1,10 @@ +import { Mastra } from "@mastra/core"; +import { triageAgent } from "./agents"; + +// --- CUSTOMIZATION POINT --- +// Register additional agents here. +// Example: import { reviewAgent } from "./agents/review-agent"; +// Then add it: agents: { "slack-triage": triageAgent, "pr-review": reviewAgent } +export const mastra = new Mastra({ + agents: { "slack-triage": triageAgent }, +}); diff --git a/test-mastra/src/mastra/tools/arcade.ts b/test-mastra/src/mastra/tools/arcade.ts new file mode 100644 index 0000000..96bd05b --- /dev/null +++ b/test-mastra/src/mastra/tools/arcade.ts @@ -0,0 +1,241 @@ +import { MCPClient } from "@mastra/mcp"; +import { createMCPClient, ElicitationRequestSchema } from "@ai-sdk/mcp"; +import { auth } from "@modelcontextprotocol/sdk/client/auth.js"; +import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; +import type { + OAuthClientMetadata, + OAuthClientInformationFull, + OAuthTokens, +} from "@modelcontextprotocol/sdk/shared/auth.js"; +import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs"; +import { join } from "path"; + +// PRODUCTION NOTE: This template uses app-level token storage (single +// .arcade-auth/ directory shared by all users). For multi-user production +// deployments, store tokens per-user in the database. See README for details. + +// --- CUSTOMIZATION POINT --- +// The MCP Gateway URL determines which tools are available. +// Create/modify your gateway at https://app.arcade.dev/mcp-gateways +// to add tools like Gmail, GitHub, Google Calendar, etc. + +function getGatewayUrl(): string { + const value = process.env.ARCADE_GATEWAY_URL?.trim(); + if (!value) { + throw new Error( + "ARCADE_GATEWAY_URL is missing. Create one at https://app.arcade.dev/mcp-gateways, add only the minimum required tools from Slack, Google Calendar, Linear, GitHub, and Gmail, then set ARCADE_GATEWAY_URL in .env." + ); + } + return value; +} +function ensureScheme(url: string): string { + const trimmed = url.replace(/\/+$/, ""); + return /^https?:\/\//.test(trimmed) ? trimmed : `http://${trimmed}`; +} +function getCallbackUrl(): string { + const base = ensureScheme( + process.env.NEXT_PUBLIC_APP_URL || `http://localhost:${process.env.PORT || 8765}` + ); + return base + "/api/auth/arcade/callback"; +} + +// --- File-based persistence (.arcade-auth/, gitignored) --- + +const AUTH_DIR = join(process.cwd(), ".arcade-auth"); +const CLIENT_FILE = join(AUTH_DIR, "client.json"); +const TOKENS_FILE = join(AUTH_DIR, "tokens.json"); +const VERIFIER_FILE = join(AUTH_DIR, "verifier.txt"); +const PENDING_AUTH_URL_FILE = join(AUTH_DIR, "pending-auth-url.txt"); + +function ensureDir() { + if (!existsSync(AUTH_DIR)) mkdirSync(AUTH_DIR, { recursive: true, mode: 0o700 }); +} + +function readJson(path: string): T | undefined { + try { + if (existsSync(path)) return JSON.parse(readFileSync(path, "utf-8")); + } catch { + // ignore JSON parse errors + } + return undefined; +} + +function writeJson(path: string, data: unknown) { + ensureDir(); + writeFileSync(path, JSON.stringify(data, null, 2), { mode: 0o600 }); +} + +// --- Pending auth URL (consumed by the connect endpoint) --- + +const PENDING_AUTH_TTL_MS = 5 * 60 * 1000; // 5 minutes + +function setPendingAuthUrl(url: string) { + ensureDir(); + writeFileSync( + PENDING_AUTH_URL_FILE, + JSON.stringify({ url, createdAt: Date.now() }), + { encoding: "utf-8", mode: 0o600 } + ); +} + +export function getPendingAuthUrl(): string | null { + if (!existsSync(PENDING_AUTH_URL_FILE)) return null; + + try { + const raw = readFileSync(PENDING_AUTH_URL_FILE, "utf-8").trim(); + const data = JSON.parse(raw) as { url: string; createdAt: number }; + if (Date.now() - data.createdAt > PENDING_AUTH_TTL_MS) { + clearPendingAuthUrl(); + return null; + } + return data.url; + } catch { + return null; + } +} + +export function clearPendingAuthUrl() { + try { + unlinkSync(PENDING_AUTH_URL_FILE); + } catch { + // Ignore cleanup errors; pending URL is best-effort state. + } +} + +// --- OAuth provider (implements OAuthClientProvider from MCP SDK) --- + +class ArcadeOAuthProvider implements OAuthClientProvider { + get redirectUrl() { + return getCallbackUrl(); + } + + get clientMetadata(): OAuthClientMetadata { + return { + redirect_uris: [getCallbackUrl()], + client_name: "Arcade Agent", + grant_types: ["authorization_code", "refresh_token"], + response_types: ["code"], + token_endpoint_auth_method: "none", + }; + } + + clientInformation(): OAuthClientInformationFull | undefined { + return readJson(CLIENT_FILE); + } + + saveClientInformation(info: OAuthClientInformationFull): void { + writeJson(CLIENT_FILE, info); + } + + tokens(): OAuthTokens | undefined { + return readJson(TOKENS_FILE); + } + + saveTokens(tokens: OAuthTokens): void { + writeJson(TOKENS_FILE, tokens); + } + + async redirectToAuthorization(authorizationUrl: URL): Promise { + setPendingAuthUrl(authorizationUrl.toString()); + console.log(`\n🔐 Arcade authorization required. Visit:\n${authorizationUrl.toString()}\n`); + } + + // NOTE: PKCE verifier is stored in a single file (.arcade-auth/verifier.txt), + // suitable for single-tenant use. In a multi-user deployment, associate + // verifiers with user sessions. The connectPromise serialization in the + // connect route prevents race conditions within a single process. + saveCodeVerifier(verifier: string): void { + ensureDir(); + writeFileSync(VERIFIER_FILE, verifier, { mode: 0o600 }); + } + + codeVerifier(): string { + return readFileSync(VERIFIER_FILE, "utf-8"); + } +} + +export const oauthProvider = new ArcadeOAuthProvider(); + +export { auth }; + +/** + * Trigger the MCP OAuth flow (discovery, registration, PKCE). + * Returns "REDIRECT" if the user needs to authorize, "AUTHORIZED" if tokens are already valid. + */ +export async function initiateOAuth(): Promise<"AUTHORIZED" | "REDIRECT"> { + return auth(oauthProvider, { serverUrl: getGatewayUrl() }); +} + +// Mastra MCP client (used by the Mastra agent for chat) +export const mcpClient = new MCPClient({ + servers: { + arcade: { + url: new URL(getGatewayUrl()), + authProvider: oauthProvider, + capabilities: { elicitation: {} }, + }, + }, +}); + +// Per-request elicitation bridge: the singleton MCPClient handles elicitation +// at the module level, but the NDJSON emit() function is per-request. +let _elicitationCallback: ((params: { + elicitationId: string; + message: string; + authUrl: string; +}) => void) | null = null; + +export function setElicitationCallback( + cb: typeof _elicitationCallback +) { + _elicitationCallback = cb; +} + +mcpClient.elicitation.onRequest("arcade", async (request) => { + const params = request as Record; + const message = (params.message as string) ?? "Authorization required"; + const authUrl = (params.url as string) ?? ""; + const elicitationId = (params.elicitationId as string) ?? ""; + console.log(`[MCP] Elicitation request: ${message}`); + _elicitationCallback?.({ elicitationId, message, authUrl }); + return { action: "accept" as const }; +}); + +/** + * Create an AI SDK MCP client for Arcade Gateway using stored OAuth tokens. + * Auto-detects transport: SSE for /sse URLs, Streamable HTTP otherwise. + * + * Pass `onElicitation` to receive URL-mode elicitation events (e.g. OAuth + * authorization prompts from the gateway). The gateway blocks the tool call + * until the user completes auth, then resumes automatically. + */ +export async function getArcadeMCPClient(options?: { + onElicitation?: (params: { + elicitationId: string; + message: string; + authUrl: string; + }) => void; +}) { + const gatewayUrl = getGatewayUrl(); + const tokens = oauthProvider.tokens(); + const headers = tokens?.access_token + ? { Authorization: `Bearer ${tokens.access_token}` } + : undefined; + const transportType = gatewayUrl.endsWith("/sse") ? "sse" : "http"; + const client = await createMCPClient({ + transport: { type: transportType, url: gatewayUrl, headers }, + capabilities: { elicitation: {} }, + }); + + client.onElicitationRequest(ElicitationRequestSchema, async (request) => { + const params = request.params as Record; + const message = (params.message as string) ?? "Authorization required"; + const authUrl = (params.url as string) ?? ""; + const elicitationId = (params.elicitationId as string) ?? ""; + console.log(`[MCP] Elicitation request: ${message}`); + options?.onElicitation?.({ elicitationId, message, authUrl }); + return { action: "accept" as const }; + }); + + return client; +} diff --git a/test-mastra/src/mastra/tools/index.ts b/test-mastra/src/mastra/tools/index.ts new file mode 100644 index 0000000..54cc8a7 --- /dev/null +++ b/test-mastra/src/mastra/tools/index.ts @@ -0,0 +1 @@ +export { mcpClient } from "./arcade"; diff --git a/test-mastra/tsconfig.json b/test-mastra/tsconfig.json new file mode 100644 index 0000000..3a13f90 --- /dev/null +++ b/test-mastra/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +} diff --git a/test-mastra/types/dashboard.ts b/test-mastra/types/dashboard.ts new file mode 100644 index 0000000..b002c95 --- /dev/null +++ b/test-mastra/types/dashboard.ts @@ -0,0 +1,14 @@ +export type ConfigWarning = { + id: string; + title: string; + message: string; + docsUrl: string; +}; + +export type ArcadeStatus = + | { state: "checking" } + | { state: "needs_auth"; authUrl: string } + | { state: "connected" } + | { state: "error"; message: string }; + +export type SourceCheckPhase = "idle" | "checking" | "done"; diff --git a/test-mastra/types/inbox.ts b/test-mastra/types/inbox.ts new file mode 100644 index 0000000..8cbe2f4 --- /dev/null +++ b/test-mastra/types/inbox.ts @@ -0,0 +1,37 @@ +export type KnownSource = "slack" | "google_calendar" | "linear" | "github" | "gmail"; +export type ItemSource = KnownSource | (string & {}); + +export type SourceStatus = "unknown" | "checking" | "connected" | "auth_required" | "skipped"; + +export interface InboxItem { + id: string; + source: ItemSource; + sourceDetail?: string; + summary: string; + category: + | "NEEDS_REPLY" + | "NEEDS_FEEDBACK" + | "NEEDS_DECISION" + | "NEEDS_REVIEW" + | "ATTEND" + | "FYI" + | "IGNORE"; + priority: "P0" | "P1" | "P2" | "FYI"; + effort: "XS" | "S" | "M" | "L"; + why: string; + suggestedNextStep: string; + confidence: number; + participants?: { id: string; name: string }[]; + url?: string; + scheduledTime?: string; +} + +export type PlanEvent = + | { type: "status"; message: string } + | { type: "task"; data: InboxItem } + | { type: "summary"; data: { total: number; bySource: Record } } + | { type: "auth_required"; authUrl: string; toolName?: string } + | { type: "elicitation"; elicitationId: string; authUrl: string; message: string } + | { type: "sources"; sources: string[] } + | { type: "error"; message: string } + | { type: "done" }; diff --git a/test-py/.env.example b/test-py/.env.example new file mode 100644 index 0000000..1cddc7f --- /dev/null +++ b/test-py/.env.example @@ -0,0 +1,23 @@ +# --- Arcade AI --- +# Your MCP Gateway URL from https://app.arcade.dev/mcp-gateways +ARCADE_GATEWAY_URL= + +# --- LLM Provider (set one or both) --- +# Get from https://platform.openai.com +OPENAI_API_KEY= +# Get from https://console.anthropic.com +ANTHROPIC_API_KEY= + +# --- App --- +PORT=8765 + +# --- App --- +APP_URL=http://localhost:8765 +APP_SECRET_KEY=change-me-to-a-random-string + +# --- Database --- +DATABASE_URL=sqlite+aiosqlite:///local.db + +# --- Custom User Verification (optional, recommended for production) --- +# ARCADE_CUSTOM_VERIFIER=true +# ARCADE_API_KEY= diff --git a/test-py/.gitignore b/test-py/.gitignore new file mode 100644 index 0000000..e6e501b --- /dev/null +++ b/test-py/.gitignore @@ -0,0 +1,25 @@ +# Python +__pycache__/ +*.py[cod] +*.egg-info/ +dist/ +build/ +.venv/ +venv/ + +# env files +.env* +!.env.example + +# database +*.db + +# Arcade OAuth tokens +.arcade-auth/ + +# IDE +.vscode/ +.idea/ + +# misc +.DS_Store diff --git a/test-py/AGENT_PLAYBOOK.md b/test-py/AGENT_PLAYBOOK.md new file mode 100644 index 0000000..375d17f --- /dev/null +++ b/test-py/AGENT_PLAYBOOK.md @@ -0,0 +1,36 @@ +# Agent Playbook + +This project is intentionally structured so coding agents can safely customize it. + +## Safe Edit Zones + +Look for these markers: + +- `CUSTOMIZATION POINT` — expected user customization area +- `AI-EDIT-SAFE` — safe for automated edits +- `AI-EDIT-CAUTION` — integration-sensitive; edit carefully + +## First Customization Steps + +1. Edit `app/system-prompt.md` to change agent behavior. +2. Change model choice in `app/agent.py`. +3. Extend schema in `app/models.py` if you need app-specific data. +4. Ensure `ARCADE_GATEWAY_URL` is set and the gateway has the expected tools. + +## Gateway Checklist + +Create/configure your gateway at `https://app.arcade.dev/mcp-gateways` and add: + +- Slack +- Google Calendar +- Linear +- GitHub +- Gmail + +## Verification Commands + +```bash +python -m app.doctor +ruff check app/ +ty check . +``` diff --git a/test-py/CLAUDE.md b/test-py/CLAUDE.md new file mode 100644 index 0000000..d1210e1 --- /dev/null +++ b/test-py/CLAUDE.md @@ -0,0 +1,65 @@ +# Arcade Agent — LangGraph + FastAPI + +## What This Is + +A Slack triage agent built with LangGraph (LangChain's agent framework), FastAPI, and Arcade's MCP Gateway. Users log in, the agent connects to Slack via Arcade tools, and triages unread messages. + +## Tech Stack + +- **Agent**: LangGraph `create_react_agent` with LangChain tool adapters +- **MCP**: `langchain-mcp-adapters` MultiServerMCPClient connecting to Arcade Gateway +- **Web**: FastAPI + Jinja2 templates + vanilla JS +- **DB**: SQLAlchemy async + aiosqlite (SQLite) +- **Auth**: [FastAPI Users](https://fastapi-users.github.io/fastapi-users/) + httpOnly JWT session cookies +- **Streaming**: SSE via sse-starlette + +## Key Commands + +```bash +source .venv/bin/activate +uvicorn app.main:app --reload # Dev server +python -m app.doctor # Environment + gateway setup checks +ruff check app/ # Lint +ruff format app/ # Format +ty check . # Type check +alembic revision --autogenerate -m "" # New migration +alembic upgrade head # Run migrations +``` + +## Key Files + +| File | Purpose | +| ---------------------- | ------------------------------------------------------------------------ | +| `app/agent.py` | Agent definition — model selection | +| `app/system-prompt.md` | System prompt (customization point) | +| `app/arcade_oauth.py` | MCP OAuth flow — discovery, PKCE, token exchange, file-based persistence | +| `app/routes/chat.py` | SSE streaming chat endpoint | +| `app/routes/arcade.py` | OAuth connect/callback + custom user verifier | +| `app/routes/auth.py` | Login, register, logout | +| `app/auth.py` | `get_current_user()` helper (FastAPI Users JWT-backed) | +| `app/auth_manager.py` | FastAPI Users setup (UserManager, JWT strategy, cookie transport) | +| `app/models.py` | User model (extends FastAPI Users base) | +| `app/static/chat.js` | Chat UI — SSE streaming, tool calls, auth URLs | + +## Auth Architecture + +Three layers: + +1. **App auth** — email/password via [FastAPI Users](https://fastapi-users.github.io/fastapi-users/), stateless JWT stored in an httpOnly `session_id` cookie, SQLite storage. Configure `APP_SECRET_KEY` in `.env`. +2. **Arcade Gateway OAuth** — MCP OAuth flow with file-based token persistence in `.arcade-auth/` (discovery → registration → PKCE → token exchange) +3. **Tool-level OAuth** — Arcade handles per-tool auth (Slack, GitHub, etc.); auth URLs surfaced in chat UI +4. **Custom verifier** (optional) — `/api/arcade/verify` confirms user identity for COAT protection. Enabling the custom verifier also requires: (a) setting up custom OAuth applications with each auth provider (Slack, GitHub, etc.) in the Arcade dashboard — Arcade's default shared OAuth apps cannot be used with a custom verifier, and (b) exposing the local dev server via ngrok (`ngrok http 8000`) so Arcade can reach the verifier endpoint, then configuring the ngrok URL in the Arcade dashboard + +## Constraints + +- `MultiServerMCPClient` is NOT a context manager in v0.2.x — call `await client.get_tools()` directly, do not use `async with` +- No `@arcadeai/arcadejs` — pure MCP protocol via `langchain-mcp-adapters` +- OAuth tokens stored in `.arcade-auth/` (gitignored) + +## Customization Points + +Marked with `# --- CUSTOMIZATION POINT ---` comments: + +- `app/system-prompt.md` — Agent purpose and behavior +- `app/agent.py` — Model selection +- `app/models.py` — Database schema diff --git a/test-py/README.md b/test-py/README.md new file mode 100644 index 0000000..e805fd8 --- /dev/null +++ b/test-py/README.md @@ -0,0 +1,214 @@ +# test-py + +A reference AI agent template that ships with a working Slack triage use case. Built with [LangGraph](https://langchain-ai.github.io/langgraph/), [FastAPI](https://fastapi.tiangolo.com), and [Arcade](https://arcade.dev). + +The agent connects to Arcade's MCP Gateway to call Slack tools (and any other tools you configure), with automatic OAuth handling and streaming responses. + +## What Is an MCP Gateway? + +An MCP Gateway is a managed tool endpoint in Arcade that your agent connects to via one URL. + +Benefits: + +- **One connection point** — use one `ARCADE_GATEWAY_URL` instead of wiring many tool servers +- **Tool curation** — choose exactly which tools your agent can see +- **Faster iteration** — update tool access in Arcade without changing integration code +- **Cleaner model context** — smaller, focused toolsets improve tool selection reliability +- **Portable setup** — same gateway pattern works across frameworks and MCP clients + +## Prerequisites + +- Python 3.11+ +- [Arcade account](https://app.arcade.dev) + MCP Gateway URL +- [OpenAI API key](https://platform.openai.com) or [Anthropic API key](https://console.anthropic.com) + +## Quick Start + +1. **Activate the virtual environment:** + ```bash + source .venv/bin/activate + ``` + +2. **Configure environment:** + ```bash + cp .env.example .env + ``` + Fill in your `ARCADE_GATEWAY_URL` and at least one LLM API key in `.env`. + +3. **Run setup doctor:** + ```bash + python -m app.doctor + ``` + +4. **Set up the database** (if not already done by the CLI): + ```bash + alembic upgrade head + ``` + +5. **Start the dev server:** + ```bash + uvicorn app.main:app --reload + ``` + +6. **Open [http://localhost:8765](http://localhost:8765)**, register an account, and start chatting. + +When the agent first tries to use an Arcade tool that requires OAuth (e.g., Slack), it will return an authorization URL. Click the link to authorize in a new tab, then click "Continue After Authorization" in the chat to retry. + +## Arcade Gateway Setup Checklist + +1. Create a gateway at [app.arcade.dev/mcp-gateways](https://app.arcade.dev/mcp-gateways). +2. Add only these minimum tools (exact names): + - Slack: `Slack_ListConversations`, `Slack_GetMessages`, `Slack_GetConversationMetadata`, `Slack_WhoAmI` + - Google Calendar: `GoogleCalendar_ListEvents`, `GoogleCalendar_ListCalendars`, `GoogleCalendar_WhoAmI` + - Linear: `Linear_GetNotifications`, `Linear_GetRecentActivity`, `Linear_ListIssues`, `Linear_GetIssue`, `Linear_ListProjects`, `Linear_GetProject`, `Linear_WhoAmI` + - GitHub: `Github_ListNotifications`, `Github_GetNotificationSummary`, `Github_ListPullRequests`, `Github_GetPullRequest`, `Github_GetUserOpenItems`, `Github_GetUserRecentActivity`, `Github_GetReviewWorkload`, `Github_GetIssue`, `Github_WhoAmI` + - Gmail: `Gmail_ListEmails`, `Gmail_ListThreads`, `Gmail_GetThread`, `Gmail_SearchThreads`, `Gmail_WhoAmI` +3. Avoid broad "all tools" access. Smaller toolsets improve tool selection quality. +4. Copy the gateway URL into `.env` as `ARCADE_GATEWAY_URL`. +5. Retry the connection check in the app. + +If you see `ARCADE_GATEWAY_URL is missing`, your app cannot connect to Arcade until this value is set. + +## Customization + +### Change the agent's purpose + +Edit `app/system-prompt.md` to change what the agent does (PR reviews, calendar management, email drafting, etc.). The agent configuration in `app/agent.py` has `# --- CUSTOMIZATION POINT ---` markers for model selection and other settings. + +### Add or change Arcade tools + +1. Go to [app.arcade.dev/mcp-gateways](https://app.arcade.dev/mcp-gateways) +2. Add tools to your gateway (Gmail, GitHub, Google Calendar, etc.) +3. Update `ARCADE_GATEWAY_URL` in `.env` if needed + +The agent automatically discovers all tools available on your gateway. + +### Switch LLM provider + +The template auto-detects which provider to use based on which API key is set in `.env`: +- Set `ANTHROPIC_API_KEY` → uses Claude +- Set `OPENAI_API_KEY` → uses GPT-4o +- If both are set, Anthropic takes priority + +You can also manually configure the model in `app/agent.py`. + +### Modify the database + +Edit `app/models.py` to add models, then create a new migration: + +```bash +alembic revision --autogenerate -m "description" +alembic upgrade head +``` + +### Production considerations + +This template uses app-level token storage — all users share the same Arcade Gateway connection via a single `.arcade-auth/` directory. For production deployments: + +- **Per-user tokens**: Store OAuth tokens in the database keyed by user ID +- **Per-session PKCE**: Associate PKCE verifiers with user sessions to prevent cross-session conflicts +- **Token refresh**: Implement automatic token refresh per-user + +## Project Structure + +``` +├── app/ +│ ├── main.py # FastAPI app entry point +│ ├── config.py # Settings loaded from .env +│ ├── agent.py # LangGraph agent + model selection (customization point) +│ ├── system-prompt.md # System prompt (customization point) +│ ├── arcade_oauth.py # MCP Gateway OAuth connection +│ ├── auth.py # get_current_user helper (FastAPI Users-backed) +│ ├── auth_manager.py # FastAPI Users setup (UserManager, JWT strategy) +│ ├── database.py # SQLAlchemy async engine +│ ├── models.py # User model (extends FastAPI Users base) +│ ├── routes/ +│ │ ├── auth.py # Login, register, logout endpoints (FastAPI Users) +│ │ ├── chat.py # Streaming chat endpoint (SSE) +│ │ └── arcade.py # Connection check + OAuth callback + custom verifier +│ ├── static/ +│ │ ├── chat.js # Chat UI logic +│ │ └── styles.css # Minimal custom styles +│ └── templates/ +│ ├── base.html # Layout with Tailwind CDN +│ ├── login.html # Login/register page +│ └── chat.html # Chat interface +├── alembic/ +│ ├── env.py # Async migration config +│ └── versions/ +│ └── 001_initial.py # Users table (FastAPI Users schema) +├── .env.example # Environment variable template +├── alembic.ini # Alembic configuration +└── pyproject.toml # Python project metadata +``` + +## How It Works + +1. **LangGraph agent** (`app/agent.py`) defines a ReAct agent with a system prompt and tools from Arcade +2. **OAuth client** (`app/arcade_oauth.py`) connects to Arcade's MCP Gateway using OAuth authentication with file-based token persistence +3. **Chat endpoint** (`app/routes/chat.py`) streams agent responses as SSE events +4. **Frontend** (`app/templates/chat.html` + `app/static/chat.js`) renders the chat with streaming, tool calls, and auth URL handling +5. **Auth layer** (`app/auth_manager.py`) uses [FastAPI Users](https://fastapi-users.github.io/fastapi-users/) for email/password authentication with stateless JWT sessions stored in httpOnly cookies + +When Arcade tools require OAuth authorization (e.g., Slack access), the tool response includes an authorization URL. The chat UI displays it as a clickable link. After the user authorizes in a new tab, they click "Continue After Authorization" to retry. + +## Production Security: Custom User Verification + +By default, anyone who has the Arcade authorization link can complete the OAuth flow. In production with multiple users, this opens a [COAT attack](https://www.arcade.dev/blog/arcade-proactively-addressed-coat-vulnerability-in-agentic-ai) vector — an attacker could send an auth link to a victim, and if the victim completes it, the attacker gains access to the victim's account. + +The **custom user verifier** solves this by confirming that the person completing the authorization is the same user who started it, using your app's session. + +### Setup + +1. **Enable in `.env`:** + ``` + ARCADE_CUSTOM_VERIFIER=true + ARCADE_API_KEY=your-arcade-api-key # From https://app.arcade.dev/settings + ``` + +2. **Configure in Arcade Dashboard:** + - Go to your gateway settings at [app.arcade.dev/mcp-gateways](https://app.arcade.dev/mcp-gateways) + - Under **Auth > Settings**, set the custom verifier URL to: + ``` + {your-app-url}/api/arcade/verify + ``` + +3. **Set up custom OAuth apps for each auth provider:** + When you enable a custom user verifier, Arcade's default shared OAuth applications can no longer be used. You must register your own OAuth application with each provider you want to use (e.g., Slack, GitHub, Google) and configure them in the Arcade dashboard under your gateway's auth provider settings. See the [Arcade Custom Auth Provider docs](https://docs.arcade.dev/en/guides/user-facing-agents/secure-auth-production) for details. + +4. **Expose your local server for development (ngrok):** + Arcade must be able to reach your verifier endpoint over the internet, so `localhost` URLs won't work in the dashboard. Use [ngrok](https://ngrok.com/) to create a public tunnel: + ```bash + # Install ngrok: https://ngrok.com/download + ngrok http 8765 + ``` + ngrok will print a forwarding URL like `https://abc123.ngrok-free.app`. Use that as your verifier URL in the Arcade dashboard: + ``` + https://abc123.ngrok-free.app/api/arcade/verify + ``` + > **Tip:** The free ngrok URL changes every time you restart it. Use `ngrok http 8765 --url=your-static-domain.ngrok-free.app` if you have a static domain configured in your ngrok account, so the dashboard URL stays stable. + +5. **Test it:** When a tool requires authorization, Arcade will redirect the user through your verifier endpoint. The endpoint checks the user's session, confirms their identity with Arcade, and redirects them back. + +### Troubleshooting + +**`verify_session_required` error or redirect loop back to login:** +The session cookie doesn't match the host the verify request came in on. Make sure you logged in via `NEXT_PUBLIC_APP_URL` (the ngrok URL), not `localhost`. + +**Repeated `400 Bad Request` from Arcade even after fixing the above:** +Arcade caches a pending auth flow server-side. After too many failed verify attempts, the same `flow_id` keeps being returned by WhoAmI but it's no longer valid. To get a fresh `flow_id`: delete `.arcade-auth/`, restart the dev server, re-authenticate the gateway, then try authorizing the tool again from the dashboard. + +For full details, see the [Arcade Secure Auth Guide](https://docs.arcade.dev/en/guides/user-facing-agents/secure-auth-production). + +## Environment Variables + +| Variable | Required | Description | +|---|---|---| +| `ARCADE_GATEWAY_URL` | Yes | MCP Gateway URL from [app.arcade.dev/mcp-gateways](https://app.arcade.dev/mcp-gateways) | +| `OPENAI_API_KEY` | One of these | OpenAI API key | +| `ANTHROPIC_API_KEY` | One of these | Anthropic API key | +| `APP_URL` | No | App URL for verifier callback (default: `http://localhost:8765`) | +| `APP_SECRET_KEY` | No | Secret key for session security (change in production!) | +| `DATABASE_URL` | No | SQLAlchemy async URL (default: `sqlite+aiosqlite:///local.db`) | +| `ARCADE_CUSTOM_VERIFIER` | No | Set to `true` to enable COAT protection (see below) | +| `ARCADE_API_KEY` | When verifier enabled | Arcade API key for user verification | diff --git a/test-py/alembic.ini b/test-py/alembic.ini new file mode 100644 index 0000000..1060730 --- /dev/null +++ b/test-py/alembic.ini @@ -0,0 +1,36 @@ +[alembic] +script_location = alembic +prepend_sys_path = . + +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/test-py/alembic/env.py b/test-py/alembic/env.py new file mode 100644 index 0000000..541b608 --- /dev/null +++ b/test-py/alembic/env.py @@ -0,0 +1,45 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy.ext.asyncio import create_async_engine + +from alembic import context +from app import models # noqa: F401 — registers models with Base.metadata +from app.config import settings +from app.database import Base + +config = context.config +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +target_metadata = Base.metadata + + +def run_migrations_offline() -> None: + context.configure( + url=settings.database_url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection): + context.configure(connection=connection, target_metadata=target_metadata) + with context.begin_transaction(): + context.run_migrations() + + +async def run_migrations_online() -> None: + connectable = create_async_engine(settings.database_url) + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + await connectable.dispose() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + asyncio.run(run_migrations_online()) diff --git a/test-py/alembic/versions/001_initial.py b/test-py/alembic/versions/001_initial.py new file mode 100644 index 0000000..b6b5fd2 --- /dev/null +++ b/test-py/alembic/versions/001_initial.py @@ -0,0 +1,38 @@ +"""Initial migration: users table (FastAPI Users schema). + +Revision ID: 001 +Revises: +Create Date: 2025-01-01 00:00:00.000000 +""" + +from collections.abc import Sequence + +import sqlalchemy as sa + +from alembic import op + +revision: str = "001" +down_revision: str | None = None +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + # FastAPI Users schema: id is a UUID string, no separate sessions table + # (sessions are encoded in stateless JWTs stored in httpOnly cookies). + op.create_table( + "users", + sa.Column("id", sa.String(), nullable=False), + sa.Column("email", sa.String(), nullable=False), + sa.Column("hashed_password", sa.String(), nullable=False), + sa.Column("is_active", sa.Boolean(), nullable=False, server_default="1"), + sa.Column("is_superuser", sa.Boolean(), nullable=False, server_default="0"), + sa.Column("is_verified", sa.Boolean(), nullable=False, server_default="0"), + sa.Column("name", sa.String(), nullable=True), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("email"), + ) + + +def downgrade() -> None: + op.drop_table("users") diff --git a/test-py/app/__init__.py b/test-py/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test-py/app/agent.py b/test-py/app/agent.py new file mode 100644 index 0000000..8ae651f --- /dev/null +++ b/test-py/app/agent.py @@ -0,0 +1,39 @@ +""" +LangGraph ReAct agent with Arcade MCP tools. + +--- CUSTOMIZATION POINT --- +Edit system-prompt.md (in this directory) to change the agent's purpose. +For example, you could make a GitHub PR review agent, a calendar +scheduling assistant, or a Gmail drafting bot — just update the +system prompt and configure matching tools in your Arcade Gateway. +""" + +from pathlib import Path + +from langchain_anthropic import ChatAnthropic +from langchain_openai import ChatOpenAI + +from app.config import settings + +# AI-EDIT-SAFE: system prompt behavior +# Load system prompt from file (single source of truth) +SYSTEM_PROMPT = (Path(__file__).parent / "system-prompt.md").read_text() + + +# --- CUSTOMIZATION POINT --- +# The model is selected based on which API key you set in .env. +# Set ANTHROPIC_API_KEY to use Claude, or OPENAI_API_KEY to use GPT. +# If both are set, Anthropic takes priority. +def get_llm(): + if settings.anthropic_api_key: + return ChatAnthropic( + model="claude-sonnet-4-20250514", + api_key=settings.anthropic_api_key, + max_tokens=16384, + ) + if settings.openai_api_key: + return ChatOpenAI( + model="gpt-4.1", + api_key=settings.openai_api_key, + ) + raise RuntimeError("No LLM API key configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY in .env") diff --git a/test-py/app/arcade_oauth.py b/test-py/app/arcade_oauth.py new file mode 100644 index 0000000..2b4f881 --- /dev/null +++ b/test-py/app/arcade_oauth.py @@ -0,0 +1,472 @@ +"""Arcade MCP Gateway OAuth2 authentication. + +Implements the same OAuth flow as the TypeScript/Mastra template: +1. Discovery (Protected Resource Metadata + OAuth Server Metadata) +2. Dynamic Client Registration +3. Authorization URL generation with PKCE +4. Code exchange for tokens +5. File-based token persistence in .arcade-auth/ + +This is app-level auth (not per-user) — all users of this app share +the same Arcade gateway connection once authenticated. +""" + +import contextlib +import json +import logging +import secrets +import time +from collections.abc import Callable +from pathlib import Path +from urllib.parse import urlencode, urlparse + +import httpx +from mcp.client.auth.oauth2 import PKCEParameters +from mcp.client.auth.utils import ( + build_oauth_authorization_server_metadata_discovery_urls, + build_protected_resource_metadata_discovery_urls, + create_client_registration_request, + create_oauth_metadata_request, + extract_resource_metadata_from_www_auth, + extract_scope_from_www_auth, + get_client_metadata_scopes, + handle_auth_metadata_response, + handle_protected_resource_response, + handle_registration_response, + handle_token_response_scopes, +) +from mcp.shared.auth import OAuthClientMetadata + +from app.config import settings + + +# Suppress false-positive "Session termination failed: 202" warnings from the +# MCP SDK. Arcade's gateway returns HTTP 202 (Accepted) for async session +# teardown, which is a success — but the SDK only treats 200 as success. +class _McpTermination202Filter(logging.Filter): + def filter(self, record: logging.LogRecord) -> bool: + msg = record.getMessage() + return not ("session termination failed" in msg.lower() and "202" in msg) + + +for _logger_name in (None, "mcp", "mcp.client", "mcp.client.streamable_http", "httpx"): + logging.getLogger(_logger_name).addFilter(_McpTermination202Filter()) + +# --- File-based persistence (.arcade-auth/, gitignored) --- + +AUTH_DIR = Path(".arcade-auth") +CLIENT_FILE = AUTH_DIR / "client.json" +TOKENS_FILE = AUTH_DIR / "tokens.json" +VERIFIER_FILE = AUTH_DIR / "verifier.txt" +STATE_FILE = AUTH_DIR / "state.txt" +METADATA_FILE = AUTH_DIR / "oauth_metadata.json" +PENDING_AUTH_URL_FILE = AUTH_DIR / "pending_auth_url.txt" + +_pending_auth_url: str | None = None +_pending_auth_url_time: float = 0 + +_PENDING_AUTH_TTL = 300 # 5 minutes + + +def _ensure_dir(): + # mkdir's mode arg is umask-affected, so we chmod afterward to guarantee 0o700. + AUTH_DIR.mkdir(exist_ok=True) + AUTH_DIR.chmod(0o700) + + +def _read_json(path: Path) -> dict | None: + try: + return json.loads(path.read_text()) + except (FileNotFoundError, json.JSONDecodeError): + return None + + +def _write_json(path: Path, data: dict): + _ensure_dir() + path.write_text(json.dumps(data, indent=2)) + path.chmod(0o600) + + +# --- Token / client persistence --- + + +def get_tokens() -> dict | None: + """Load stored OAuth tokens.""" + return _read_json(TOKENS_FILE) + + +def save_tokens(tokens: dict): + """Persist OAuth tokens.""" + _write_json(TOKENS_FILE, tokens) + + +def get_client_info() -> dict | None: + """Load stored dynamic client registration info.""" + return _read_json(CLIENT_FILE) + + +def save_client_info(info: dict): + """Persist dynamic client registration info.""" + _write_json(CLIENT_FILE, info) + + +def get_oauth_metadata() -> dict | None: + """Load cached OAuth server metadata.""" + return _read_json(METADATA_FILE) + + +def save_oauth_metadata(metadata: dict): + """Cache OAuth server metadata.""" + _write_json(METADATA_FILE, metadata) + + +# --- Pending auth URL (consumed by the connect endpoint) --- + + +def get_pending_auth_url() -> str | None: + """Return the pending authorization URL (without clearing it). Checks TTL.""" + global _pending_auth_url, _pending_auth_url_time + + if _pending_auth_url and (time.time() - _pending_auth_url_time <= _PENDING_AUTH_TTL): + return _pending_auth_url + + # Try file fallback + try: + data = json.loads(PENDING_AUTH_URL_FILE.read_text()) + if time.time() - data.get("createdAt", 0) <= _PENDING_AUTH_TTL: + return data["url"] + # Expired + clear_pending_auth_url() + return None + except (FileNotFoundError, json.JSONDecodeError, KeyError): + return None + + +def clear_pending_auth_url(): + """Explicitly clear the pending authorization URL.""" + global _pending_auth_url, _pending_auth_url_time + _pending_auth_url = None + _pending_auth_url_time = 0 + with contextlib.suppress(OSError): + PENDING_AUTH_URL_FILE.unlink(missing_ok=True) + + +def get_state() -> str | None: + """Read and delete the stored OAuth state parameter.""" + try: + state = STATE_FILE.read_text().strip() + STATE_FILE.unlink(missing_ok=True) + return state or None + except FileNotFoundError: + return None + + +def _callback_url() -> str: + """Build the OAuth callback URL.""" + return f"{settings.app_url}/api/auth/arcade/callback" + + +def _base_url(url: str) -> str: + """Extract scheme + host from a URL.""" + parsed = urlparse(url) + return f"{parsed.scheme}://{parsed.netloc}" + + +# --- OAuth flow steps --- + + +async def discover_and_authorize() -> str: + """Run full OAuth discovery + registration + PKCE. Returns the authorization URL. + + Uses MCP SDK utilities for spec-compliant discovery (SEP-985, RFC 8414), + PKCE generation, dynamic client registration, and scope selection. + + Flow: + 1. Hit gateway → get 401 → extract PRM hint from WWW-Authenticate + 2. Discover Protected Resource Metadata → auth server URL + 3. Discover OAuth Authorization Server Metadata → endpoints + 4. Dynamic client registration (if needed) + 5. Generate PKCE + state + 6. Build and return authorization URL + """ + global _pending_auth_url + gateway_url = settings.arcade_gateway_url + if not gateway_url: + raise RuntimeError("ARCADE_GATEWAY_URL is missing. Add it to your .env and retry.") + + async with httpx.AsyncClient(timeout=30) as http: + # Step 1: Probe gateway for 401 and WWW-Authenticate header + probe_resp = await http.get(gateway_url) + + # Step 2: Discover Protected Resource Metadata (SEP-985 with fallback) + # SDK builds ordered fallback list: WWW-Auth hint → path-based → root + www_auth_url = extract_resource_metadata_from_www_auth(probe_resp) + prm = None + auth_server_url = _base_url(gateway_url) + for url in build_protected_resource_metadata_discovery_urls(www_auth_url, gateway_url): + resp = await http.send(create_oauth_metadata_request(url)) + prm = await handle_protected_resource_response(resp) + if prm: + if prm.authorization_servers: + auth_server_url = str(prm.authorization_servers[0]) + break + + # Step 3: Discover OAuth Authorization Server Metadata (RFC 8414) + # SDK handles path-aware discovery + OIDC fallback + oauth_meta = None + cached_meta = get_oauth_metadata() + if cached_meta: + try: + from mcp.shared.auth import OAuthMetadata + + oauth_meta = OAuthMetadata.model_validate(cached_meta) + except Exception: + oauth_meta = None + + if not oauth_meta: + for url in build_oauth_authorization_server_metadata_discovery_urls( + auth_server_url, gateway_url + ): + resp = await http.send(create_oauth_metadata_request(url)) + ok, asm = await handle_auth_metadata_response(resp) + if not ok: + break # Non-4xx error, stop trying + if asm: + oauth_meta = asm + save_oauth_metadata( + asm.model_dump(mode="json", by_alias=True, exclude_none=True) + ) + break + + # Extract endpoints (with fallbacks) + auth_endpoint = ( + str(oauth_meta.authorization_endpoint) + if oauth_meta and oauth_meta.authorization_endpoint + else f"{auth_server_url}/authorize" + ) + token_endpoint = ( + str(oauth_meta.token_endpoint) + if oauth_meta and oauth_meta.token_endpoint + else f"{auth_server_url}/token" + ) + + # Step 4: Dynamic Client Registration (if no stored client) + client_info = get_client_info() + if not client_info: + client_metadata = OAuthClientMetadata( + redirect_uris=[_callback_url()], + client_name="Arcade Agent", + grant_types=["authorization_code", "refresh_token"], + response_types=["code"], + token_endpoint_auth_method="none", + ) + reg_req = create_client_registration_request( + oauth_meta, client_metadata, _base_url(auth_server_url) + ) + reg_resp = await http.send(reg_req) + client_info_model = await handle_registration_response(reg_resp) + client_info = client_info_model.model_dump( + mode="json", by_alias=True, exclude_none=True + ) + save_client_info(client_info) + + # Step 5: Generate PKCE + state + pkce = PKCEParameters.generate() + state = secrets.token_urlsafe(32) + + _ensure_dir() + VERIFIER_FILE.write_text(pkce.code_verifier) + VERIFIER_FILE.chmod(0o600) + STATE_FILE.write_text(state) + STATE_FILE.chmod(0o600) + + # Persist metadata for exchange_code() + if oauth_meta: + save_oauth_metadata( + oauth_meta.model_dump(mode="json", by_alias=True, exclude_none=True) + ) + else: + _write_json(METADATA_FILE, {"token_endpoint": token_endpoint}) + + # Step 6: Build authorization URL + params: dict[str, str] = { + "response_type": "code", + "client_id": client_info["client_id"], + "redirect_uri": _callback_url(), + "state": state, + "code_challenge": pkce.code_challenge, + "code_challenge_method": "S256", + } + + # Add resource from PRM (required to scope token to specific gateway) + if prm and prm.resource: + params["resource"] = str(prm.resource) + + # Scope selection using MCP spec priority order + scope = get_client_metadata_scopes(extract_scope_from_www_auth(probe_resp), prm, oauth_meta) + if scope: + params["scope"] = scope + + auth_url = f"{auth_endpoint}?{urlencode(params)}" + _pending_auth_url = auth_url + _pending_auth_url_time = time.time() + _ensure_dir() + PENDING_AUTH_URL_FILE.write_text(json.dumps({"url": auth_url, "createdAt": time.time()})) + PENDING_AUTH_URL_FILE.chmod(0o600) + return auth_url + + +async def exchange_code(code: str) -> dict: + """Exchange an authorization code for OAuth tokens. + + Uses the stored PKCE verifier and client registration info. + """ + client_info = get_client_info() + if not client_info: + raise RuntimeError("No client registration found — run connect first") + + try: + code_verifier = VERIFIER_FILE.read_text() + except FileNotFoundError as err: + raise RuntimeError("No PKCE verifier found — run connect first") from err + + # Get token endpoint from cached metadata + oauth_meta = get_oauth_metadata() + if oauth_meta and oauth_meta.get("token_endpoint"): + token_endpoint = oauth_meta["token_endpoint"] + else: + # Fallback + gateway_base = _base_url(settings.arcade_gateway_url) + token_endpoint = f"{gateway_base}/token" + + async with httpx.AsyncClient(timeout=30) as client: + resp = await client.post( + token_endpoint, + data={ + "grant_type": "authorization_code", + "code": code, + "redirect_uri": _callback_url(), + "client_id": client_info["client_id"], + "code_verifier": code_verifier, + }, + headers={"Content-Type": "application/x-www-form-urlencoded"}, + ) + resp.raise_for_status() + token_model = await handle_token_response_scopes(resp) + tokens = token_model.model_dump(mode="json", exclude_none=True) + save_tokens(tokens) + invalidate_tools_cache() + return tokens + + +# --- Per-request elicitation bridge --- +# The MCP client is a module-level singleton, but the NDJSON emitter is +# per-request. A module-level callback ref bridges the two. + +_elicitation_callback: Callable[[str, str, str], None] | None = None + + +def set_elicitation_callback(cb: Callable[[str, str, str], None] | None): + """Set (or clear) the per-request elicitation event callback.""" + global _elicitation_callback + _elicitation_callback = cb + + +async def _on_elicitation(mcp_context, params, context): + """Handle URL-mode elicitation requests from Arcade gateway.""" + from mcp.types import ElicitResult + + message = getattr(params, "message", "Authorization required") + auth_url = getattr(params, "url", "") + elicitation_id = getattr(params, "elicitation_id", "") or getattr(params, "elicitationId", "") + logging.getLogger(__name__).info("[MCP] Elicitation request: %s", message) + if _elicitation_callback: + _elicitation_callback(elicitation_id, message, auth_url) + return ElicitResult(action="accept") + + +# --- MCP Client factory --- + + +def create_mcp_client(): + """Create a MultiServerMCPClient using stored OAuth tokens. + + After OAuth is complete, the stored access_token is sent as a + Bearer token to authenticate with the Arcade MCP Gateway. + """ + from langchain_mcp_adapters.callbacks import Callbacks + from langchain_mcp_adapters.client import MultiServerMCPClient + + if not settings.arcade_gateway_url: + raise RuntimeError("ARCADE_GATEWAY_URL is missing. Add it to your .env and retry.") + + tokens = get_tokens() + headers = {} + if tokens and tokens.get("access_token"): + headers["Authorization"] = f"Bearer {tokens['access_token']}" + + # Auto-detect transport: /sse endpoints use SSE, others use Streamable HTTP + transport = "sse" if settings.arcade_gateway_url.rstrip("/").endswith("/sse") else "http" + + return MultiServerMCPClient( + { + "arcade": { + "transport": transport, + "url": settings.arcade_gateway_url, + "headers": headers, + } + }, + callbacks=Callbacks(on_elicitation=_on_elicitation), + ) + + +# --- Cached MCP client (avoids "Session termination failed" spam) --- + +_cached_mcp_client = None +_cached_token: str | None = None + + +def get_mcp_client(): + """Return a cached MultiServerMCPClient, recreating only when the token changes. + + Reusing the client avoids opening/closing an MCP session on every chat + request, which eliminates the "Session termination failed: 202" log spam. + """ + global _cached_mcp_client, _cached_token + + tokens = get_tokens() + current_token = tokens.get("access_token") if tokens else None + + if _cached_mcp_client is not None and current_token == _cached_token: + return _cached_mcp_client + + _cached_mcp_client = create_mcp_client() + _cached_token = current_token + return _cached_mcp_client + + +# --- Cached tools (avoids creating a new MCP session per request) --- + +_cached_tools: list | None = None +_cached_tools_time: float = 0 +_TOOLS_CACHE_TTL = 300 # 5 minutes + + +async def get_cached_tools(mcp_client=None): + """Return cached MCP tools, refreshing only when the cache expires.""" + global _cached_tools, _cached_tools_time + now = time.time() + if _cached_tools is not None and (now - _cached_tools_time) < _TOOLS_CACHE_TTL: + return _cached_tools + if mcp_client is None: + mcp_client = get_mcp_client() + _cached_tools = await mcp_client.get_tools() + _cached_tools_time = now + return _cached_tools + + +def invalidate_tools_cache(): + """Clear the tools cache (e.g. after token refresh).""" + global _cached_tools, _cached_tools_time + _cached_tools = None + _cached_tools_time = 0 diff --git a/test-py/app/auth.py b/test-py/app/auth.py new file mode 100644 index 0000000..9d0929a --- /dev/null +++ b/test-py/app/auth.py @@ -0,0 +1,38 @@ +"""Auth helpers using FastAPI Users. + +`get_current_user(request, db)` provides a backward-compatible wrapper that +decodes the FastAPI Users JWT session cookie and returns the authenticated user. +All other route files (chat, arcade, plan) continue to call this function +without modification. +""" + +from fastapi import Request +from fastapi_users.db import SQLAlchemyUserDatabase +from sqlalchemy.ext.asyncio import AsyncSession + +from app.models import User + + +async def get_current_user(request: Request, db: AsyncSession) -> User | None: + """Return the authenticated user from the session cookie, or None. + + Reads the `session_id` JWT cookie written by FastAPI Users' CookieTransport + and verifies it using the configured JWTStrategy. + """ + from app.auth_manager import UserManager, get_jwt_strategy + + token = request.cookies.get("session_id") + if not token: + return None + + user_db = SQLAlchemyUserDatabase(db, User) + user_manager = UserManager(user_db) + strategy = get_jwt_strategy() + + try: + user = await strategy.read_token(token, user_manager) + if user and user.is_active: + return user + return None + except Exception: + return None diff --git a/test-py/app/auth_manager.py b/test-py/app/auth_manager.py new file mode 100644 index 0000000..29183c5 --- /dev/null +++ b/test-py/app/auth_manager.py @@ -0,0 +1,61 @@ +"""FastAPI Users configuration. + +This module sets up FastAPI Users with a cookie-based JWT authentication backend. +See: https://fastapi-users.github.io/fastapi-users/ +""" + +import uuid + +from fastapi import Depends +from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin +from fastapi_users.authentication import ( + AuthenticationBackend, + CookieTransport, + JWTStrategy, +) +from fastapi_users.db import SQLAlchemyUserDatabase + +from app.config import settings +from app.database import get_db +from app.models import User + + +async def get_user_db(session=Depends(get_db)): + yield SQLAlchemyUserDatabase(session, User) + + +SECRET = settings.app_secret_key + + +class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): + """Manages user lifecycle events (registration, login, password reset).""" + + reset_password_token_secret = SECRET + verification_token_secret = SECRET + + +async def get_user_manager(user_db: SQLAlchemyUserDatabase = Depends(get_user_db)): + yield UserManager(user_db) + + +# Cookie transport — stores the JWT in an httpOnly session_id cookie +cookie_transport = CookieTransport( + cookie_name="session_id", + cookie_max_age=3600 * 24 * 7, # 7 days + cookie_secure=settings.app_url.startswith("https://"), + cookie_httponly=True, + cookie_samesite="lax", +) + + +def get_jwt_strategy() -> JWTStrategy: + return JWTStrategy(secret=SECRET, lifetime_seconds=3600 * 24 * 7) + + +auth_backend = AuthenticationBackend( + name="cookie", + transport=cookie_transport, + get_strategy=get_jwt_strategy, +) + +fastapi_users = FastAPIUsers[User, uuid.UUID](get_user_manager, [auth_backend]) diff --git a/test-py/app/config.py b/test-py/app/config.py new file mode 100644 index 0000000..770b860 --- /dev/null +++ b/test-py/app/config.py @@ -0,0 +1,58 @@ +from dotenv import load_dotenv +from pydantic import field_validator +from pydantic_settings import BaseSettings + +load_dotenv(dotenv_path=".env", override=False) + + +class Settings(BaseSettings): + # Arcade + arcade_gateway_url: str = "" + + # LLM + openai_api_key: str = "" + anthropic_api_key: str = "" + + # App + app_url: str = "http://localhost:8765" + app_secret_key: str = "change-me-to-a-random-string" + + # Database + database_url: str = "sqlite+aiosqlite:///local.db" + + # Custom verifier (optional — requires ARCADE_API_KEY) + arcade_custom_verifier: bool = False + arcade_api_key: str = "" + + @field_validator( + "arcade_gateway_url", "openai_api_key", "anthropic_api_key", "arcade_api_key", mode="before" + ) + @classmethod + def strip_env_comments(cls, v: str) -> str: + """Strip inline comments that python-dotenv may leave in values.""" + v = v.strip() + if v.startswith("#"): + return "" + return v + + @field_validator("database_url", mode="before") + @classmethod + def coerce_database_url(cls, v: str) -> str: + """Fall back to default SQLite URL if env var is unset, blank, or not a valid URL.""" + v = (v or "").strip() + if not v or "://" not in v: + return "sqlite+aiosqlite:///local.db" + return v + + @field_validator("app_url") + @classmethod + def ensure_app_url_has_scheme(cls, v: str) -> str: + v = v.rstrip("/") + if not v.startswith(("http://", "https://")): + v = f"http://{v}" + return v + + model_config = {"env_file": ".env", "extra": "ignore"} + + +settings = Settings() diff --git a/test-py/app/database.py b/test-py/app/database.py new file mode 100644 index 0000000..a9376ff --- /dev/null +++ b/test-py/app/database.py @@ -0,0 +1,16 @@ +from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine +from sqlalchemy.orm import DeclarativeBase + +from app.config import settings + +engine = create_async_engine(settings.database_url, echo=False) +async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) + + +class Base(DeclarativeBase): + pass + + +async def get_db(): + async with async_session() as session: + yield session diff --git a/test-py/app/doctor.py b/test-py/app/doctor.py new file mode 100644 index 0000000..bf67d67 --- /dev/null +++ b/test-py/app/doctor.py @@ -0,0 +1,96 @@ +""" +Basic setup checker for local development. + +Run: + python -m app.doctor +""" + +from __future__ import annotations + +import asyncio + +import httpx + +from app.config import settings + +RECOMMENDED_TOOLKITS = ["Slack", "Google Calendar", "Linear", "GitHub", "Gmail"] +RECOMMENDED_TOOLS = [ + "Slack_ListConversations", + "Slack_GetMessages", + "Slack_GetConversationMetadata", + "Slack_WhoAmI", + "GoogleCalendar_ListEvents", + "GoogleCalendar_ListCalendars", + "GoogleCalendar_WhoAmI", + "Linear_GetNotifications", + "Linear_GetRecentActivity", + "Linear_ListIssues", + "Linear_GetIssue", + "Linear_ListProjects", + "Linear_GetProject", + "Linear_WhoAmI", + "Github_ListNotifications", + "Github_GetNotificationSummary", + "Github_ListPullRequests", + "Github_GetPullRequest", + "Github_GetUserOpenItems", + "Github_GetUserRecentActivity", + "Github_GetReviewWorkload", + "Github_GetIssue", + "Github_WhoAmI", + "Gmail_ListEmails", + "Gmail_ListThreads", + "Gmail_GetThread", + "Gmail_SearchThreads", + "Gmail_WhoAmI", +] + + +async def _gateway_reachable(url: str) -> bool: + try: + async with httpx.AsyncClient(timeout=10) as client: + resp = await client.get(url) + return resp.status_code in {200, 401, 403} + except Exception: + return False + + +async def main() -> int: + errors: list[str] = [] + + if not settings.arcade_gateway_url: + errors.append( + "Missing ARCADE_GATEWAY_URL. Create one at https://app.arcade.dev/mcp-gateways " + "and set it in .env." + ) + + if not settings.openai_api_key and not settings.anthropic_api_key: + errors.append("Missing LLM key. Set OPENAI_API_KEY or ANTHROPIC_API_KEY in .env.") + + if settings.arcade_gateway_url and not await _gateway_reachable(settings.arcade_gateway_url): + errors.append(f"Gateway not reachable at {settings.arcade_gateway_url}") + + if errors: + print("\nDoctor found setup issues:\n") + for err in errors: + print(f"- {err}") + print("\nRecommended minimum toolkits (enable only needed tools):") + for toolkit in RECOMMENDED_TOOLKITS: + print(f"- {toolkit}") + print("\nRecommended minimum tools (exact names):") + for tool in RECOMMENDED_TOOLS: + print(f"- {tool}") + return 1 + + print("Doctor check passed.") + print("Recommended minimum toolkits (enable only needed tools):") + for toolkit in RECOMMENDED_TOOLKITS: + print(f"- {toolkit}") + print("Recommended minimum tools (exact names):") + for tool in RECOMMENDED_TOOLS: + print(f"- {tool}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(asyncio.run(main())) diff --git a/test-py/app/main.py b/test-py/app/main.py new file mode 100644 index 0000000..7e0bc09 --- /dev/null +++ b/test-py/app/main.py @@ -0,0 +1,75 @@ +from pathlib import Path +from urllib.parse import urlparse + +from fastapi import Depends, FastAPI, Request +from fastapi.responses import RedirectResponse +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates +from sqlalchemy.ext.asyncio import AsyncSession +from starlette.middleware.base import BaseHTTPMiddleware + +from app.auth import get_current_user +from app.config import settings +from app.database import get_db +from app.routes import arcade, auth, plan + +_parsed_app_url = urlparse(settings.app_url) +_canonical_host = _parsed_app_url.hostname +_canonical_port = _parsed_app_url.port + + +class CanonicalHostMiddleware(BaseHTTPMiddleware): + """Redirect requests to the canonical APP_URL host. + + This prevents cookie/session mismatch between 127.0.0.1 and localhost. + For example, if APP_URL is http://localhost:8765, requests to + http://127.0.0.1:8765 are redirected to http://localhost:8765. + """ + + async def dispatch(self, request: Request, call_next): + request_host = request.headers.get("host", "") + # Build expected host header (host:port or just host for default ports) + if _canonical_port and _canonical_port not in (80, 443): + expected_host = f"{_canonical_host}:{_canonical_port}" + else: + expected_host = _canonical_host + + if request_host != expected_host: + url = f"{settings.app_url}{request.url.path}" + if request.url.query: + url += f"?{request.url.query}" + return RedirectResponse(url, status_code=302) + + return await call_next(request) + + +app = FastAPI(title="Arcade Agent") +app.add_middleware(CanonicalHostMiddleware) + +BASE_DIR = Path(__file__).resolve().parent +app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static") +templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) + +# API routers +app.include_router(auth.router) +app.include_router(arcade.router) +app.include_router(plan.router) + + +@app.get("/") +async def index(request: Request, db: AsyncSession = Depends(get_db)): + """Login/register page, or redirect to dashboard if already logged in.""" + user = await get_current_user(request, db) + if user: + return RedirectResponse("/dashboard") + return templates.TemplateResponse("login.html", {"request": request}) + + +@app.get("/dashboard") +async def dashboard_page(request: Request, db: AsyncSession = Depends(get_db)): + """Dashboard page -- requires authentication.""" + user = await get_current_user(request, db) + if not user: + return RedirectResponse("/") + return templates.TemplateResponse("dashboard.html", {"request": request, "user": user}) + diff --git a/test-py/app/models.py b/test-py/app/models.py new file mode 100644 index 0000000..a943e5a --- /dev/null +++ b/test-py/app/models.py @@ -0,0 +1,22 @@ +from fastapi_users.db import SQLAlchemyBaseUserTableUUID +from sqlalchemy.orm import Mapped, mapped_column + +from app.database import Base + +# AI-EDIT-SAFE: application schema extension +# Add domain tables/columns as needed for your project. + + +class User(SQLAlchemyBaseUserTableUUID, Base): + """User model — extends FastAPI Users for email/password authentication. + + FastAPI Users provides: id (UUID), email, hashed_password, + is_active, is_superuser, is_verified. + + Add your own custom fields below. + """ + + __tablename__ = "users" + + # --- CUSTOMIZATION POINT --- + name: Mapped[str | None] = mapped_column(nullable=True) diff --git a/test-py/app/routes/__init__.py b/test-py/app/routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test-py/app/routes/arcade.py b/test-py/app/routes/arcade.py new file mode 100644 index 0000000..22d4ba3 --- /dev/null +++ b/test-py/app/routes/arcade.py @@ -0,0 +1,290 @@ +""" +Arcade MCP Gateway OAuth connection and custom user verifier. + +- POST /api/arcade/connect — check tokens or start OAuth flow +- POST /api/sources — check per-source auth via WhoAmI tools +- GET /api/auth/arcade/callback — OAuth callback (exchanges code for tokens) +- GET /api/arcade/verify — custom user verifier for COAT attack protection +""" + +import asyncio +import json +import re +from urllib.parse import urlparse + +import httpx +from fastapi import APIRouter, Depends, Request +from fastapi.responses import JSONResponse, RedirectResponse +from sqlalchemy.ext.asyncio import AsyncSession + +from app.arcade_oauth import ( + clear_pending_auth_url, + discover_and_authorize, + exchange_code, + get_cached_tools, + get_mcp_client, + get_state, + get_tokens, +) +from app.auth import get_current_user +from app.config import settings +from app.database import get_db + +router = APIRouter(tags=["arcade"]) + +ARCADE_CONFIRM_URL = "https://cloud.arcade.dev/api/v1/oauth/confirm_user" + + +@router.post("/api/arcade/connect") +async def connect(request: Request, db: AsyncSession = Depends(get_db)): + """Check if Arcade Gateway is connected, or start OAuth flow.""" + user = await get_current_user(request, db) + if not user: + return JSONResponse({"error": "Unauthorized"}, status_code=401) + if not settings.arcade_gateway_url: + return JSONResponse( + { + "connected": False, + "error": ( + "ARCADE_GATEWAY_URL is missing. Create one at " + "https://app.arcade.dev/mcp-gateways, add only the minimum required " + "tools from Slack, Google Calendar, Linear, GitHub, and Gmail, then " + "set ARCADE_GATEWAY_URL in .env." + ), + }, + status_code=400, + ) + + # Fast path: already have tokens + tokens = get_tokens() + if tokens and tokens.get("access_token"): + try: + mcp_client = get_mcp_client() + await get_cached_tools(mcp_client) + return {"connected": True} + except Exception as e: + # Distinguish auth failures (need re-auth) from connectivity errors. + # Don't trigger a new OAuth redirect for transient failures — that would + # force the user to re-authorize when the gateway is just briefly down. + err = str(e) + is_auth_error = ( + (isinstance(e, httpx.HTTPStatusError) and e.response.status_code in (401, 403)) + or "401" in err + or "403" in err + or "unauthorized" in err.lower() + or "forbidden" in err.lower() + ) + if not is_auth_error: + return JSONResponse( + { + "connected": False, + "error": ( + "Cannot reach Arcade Gateway. " + "Check ARCADE_GATEWAY_URL and try again." + ), + }, + status_code=502, + ) + # Auth error — tokens invalid/expired; fall through to re-auth + + # No tokens (or expired) — kick off OAuth discovery + registration + redirect + try: + auth_url = await discover_and_authorize() + clear_pending_auth_url() + return JSONResponse({"connected": False, "authUrl": auth_url}) + except Exception as e: + return JSONResponse( + { + "connected": False, + "error": f"Could not connect to Arcade Gateway: {e}", + }, + status_code=502, + ) + + +def _map_tool_to_source(tool_name: str | None) -> str: + if not tool_name: + return "other" + service = re.split(r"[._]", tool_name)[0].lower() + if service == "slack": + return "slack" + if service in ("google", "googlecalendar", "calendar"): + return "google_calendar" + if service == "linear": + return "linear" + if service in ("git", "github"): + return "github" + if service == "gmail": + return "gmail" + return service or "other" + + +def _extract_auth_url_from_result(content: str) -> str | None: + """Check if a tool result contains an Arcade authorization URL.""" + try: + parsed = json.loads(content) + url = ( + parsed.get("authorization_url") + or parsed.get("url") + or (parsed.get("structuredContent") or {}).get("authorization_url") + ) + if url: + return url + except (json.JSONDecodeError, AttributeError): + pass + # Fallback: regex + match = re.search( + r"https://[^\s\"'>\]]+/oauth/[^\s\"'>\]]+|https://[^\s\"'>\]]+authorize[^\s\"'>\]]*", + content or "", + re.IGNORECASE, + ) + return match.group(0) if match else None + + +@router.post("/api/sources") +async def check_sources(request: Request, db: AsyncSession = Depends(get_db)): + """Call WhoAmI tools in parallel to check per-source auth status.""" + user = await get_current_user(request, db) + if not user: + return JSONResponse({"error": "Unauthorized"}, status_code=401) + if not settings.arcade_gateway_url: + return JSONResponse( + { + "error": ( + "ARCADE_GATEWAY_URL is missing. Create one at " + "https://app.arcade.dev/mcp-gateways, add only the minimum required " + "tools from Slack, Google Calendar, Linear, GitHub, and Gmail, then " + "set ARCADE_GATEWAY_URL in .env." + ), + "sources": {}, + }, + status_code=400, + ) + + try: + mcp_client = get_mcp_client() + all_tools = await get_cached_tools(mcp_client) + whoami_tools = [t for t in all_tools if re.search(r"[._]WhoAmI$", t.name, re.IGNORECASE)] + + async def _call_whoami(tool): + try: + result = await tool.ainvoke({}) + content = result if isinstance(result, str) else str(result) + auth_url = _extract_auth_url_from_result(content) + source = _map_tool_to_source(tool.name) + return source, auth_url + except Exception: + source = _map_tool_to_source(tool.name) + return source, None + + results = await asyncio.gather( + *[_call_whoami(t) for t in whoami_tools], + return_exceptions=True, + ) + + sources = {} + for r in results: + if isinstance(r, Exception): + continue + source, auth_url = r + sources[source] = ( + {"status": "auth_required", "authUrl": auth_url} + if auth_url + else {"status": "connected"} + ) + + return {"sources": sources} + except Exception as e: + print(f"[sources] Error checking WhoAmI tools: {e}") + return {"sources": {}} + + +@router.get("/api/auth/arcade/callback") +async def callback(request: Request): + """OAuth callback — exchange authorization code for tokens.""" + code = request.query_params.get("code") + state = request.query_params.get("state") + if not code: + return JSONResponse({"error": "Missing authorization code"}, status_code=400) + + stored_state = get_state() + if not stored_state or state != stored_state: + return RedirectResponse("/dashboard?error=auth_failed", status_code=302) + + try: + await exchange_code(code) + # Use relative redirect so the browser stays on the same host + # (avoids cookie mismatch between 127.0.0.1 and localhost) + return RedirectResponse("/dashboard", status_code=302) + except Exception as e: + print(f"Arcade OAuth callback error: {e}") + return RedirectResponse("/dashboard?error=auth_failed", status_code=302) + + +@router.get("/api/arcade/verify") +async def verify(request: Request, db: AsyncSession = Depends(get_db)): + """Custom user verifier for COAT attack protection. + + When enabled (ARCADE_CUSTOM_VERIFIER=true), Arcade redirects users here + with a flow_id. This endpoint confirms the user's identity with Arcade, + preventing attackers from completing auth flows on behalf of victims. + + IMPORTANT: Enabling the custom verifier also requires registering custom + OAuth applications for each auth provider (Slack, GitHub, etc.) in the + Arcade dashboard — the default shared OAuth apps cannot be used with a + custom verifier. For local dev, use ngrok (`ngrok http 8000`) and set the + ngrok URL as the verifier URL in the dashboard. + + See: https://docs.arcade.dev/en/guides/user-facing-agents/secure-auth-production + """ + if not settings.arcade_custom_verifier: + return JSONResponse( + { + "error": "Custom user verification is not enabled. " + "Set ARCADE_CUSTOM_VERIFIER=true in your .env." + }, + status_code=404, + ) + + if not settings.arcade_api_key: + return RedirectResponse("/?error=verify_misconfigured", status_code=302) + + flow_id = request.query_params.get("flow_id") + if not flow_id: + return JSONResponse({"error": "Missing flow_id parameter"}, status_code=400) + + user = await get_current_user(request, db) + if not user: + return RedirectResponse("/") + + try: + async with httpx.AsyncClient() as http: + resp = await http.post( + ARCADE_CONFIRM_URL, + headers={ + "Authorization": f"Bearer {settings.arcade_api_key}", + "Content-Type": "application/json", + }, + json={"flow_id": flow_id, "user_id": user.email}, + ) + + if not resp.is_success: + return RedirectResponse("/dashboard?error=verify_failed", status_code=302) + + data = resp.json() + next_uri = data.get("next_uri", "") + # Validate next_uri to prevent open redirect: only allow relative paths + # or same-origin absolute URLs + redirect_to = "/dashboard" + if next_uri: + parsed = urlparse(next_uri) + if not parsed.netloc: + # Relative path — safe + redirect_to = next_uri + elif parsed.netloc == urlparse(settings.app_url).netloc: + # Same origin — safe + redirect_to = next_uri + return RedirectResponse(redirect_to) + + except Exception: + return RedirectResponse("/dashboard?error=verify_failed", status_code=302) diff --git a/test-py/app/routes/auth.py b/test-py/app/routes/auth.py new file mode 100644 index 0000000..baad4a7 --- /dev/null +++ b/test-py/app/routes/auth.py @@ -0,0 +1,88 @@ +"""Auth endpoints backed by FastAPI Users. + +These routes maintain the same URLs and JSON interface as before, but delegate +all password hashing and user management to FastAPI Users' UserManager. +""" + +from dataclasses import dataclass + +from fastapi import APIRouter, Depends, Response +from fastapi.responses import JSONResponse +from fastapi_users import exceptions +from fastapi_users.schemas import BaseUserCreate +from pydantic import BaseModel, EmailStr + +from app.auth_manager import UserManager, get_jwt_strategy, get_user_manager +from app.config import settings + +router = APIRouter(prefix="/api/auth", tags=["auth"]) + + +class AuthRequest(BaseModel): + email: EmailStr + password: str + + +class _UserCreate(BaseUserCreate): + """Minimal schema for FastAPI Users user creation.""" + + pass + + +@dataclass +class _Credentials: + """OAuth2-compatible credentials container for FastAPI Users authenticate().""" + + username: str + password: str + + +def _set_session_cookie(response: Response, token: str) -> None: + response.set_cookie( + "session_id", + token, + max_age=3600 * 24 * 7, + httponly=True, + samesite="lax", + secure=settings.app_url.startswith("https://"), + ) + + +@router.post("/register") +async def register( + body: AuthRequest, + response: Response, + user_manager: UserManager = Depends(get_user_manager), +): + try: + user = await user_manager.create(_UserCreate(email=body.email, password=body.password)) + except exceptions.UserAlreadyExists: + return JSONResponse({"error": "Email already registered"}, status_code=409) + except exceptions.InvalidPasswordException as e: + return JSONResponse({"error": str(e.reason)}, status_code=400) + + token = await get_jwt_strategy().write_token(user) + _set_session_cookie(response, token) + return {"success": True} + + +@router.post("/login") +async def login( + body: AuthRequest, + response: Response, + user_manager: UserManager = Depends(get_user_manager), +): + credentials = _Credentials(username=body.email, password=body.password) + user = await user_manager.authenticate(credentials) + if user is None or not user.is_active: + return JSONResponse({"error": "Invalid credentials"}, status_code=401) + + token = await get_jwt_strategy().write_token(user) + _set_session_cookie(response, token) + return {"success": True} + + +@router.post("/logout") +async def logout(response: Response): + response.delete_cookie("session_id", path="/") + return {"success": True} diff --git a/test-py/app/routes/plan.py b/test-py/app/routes/plan.py new file mode 100644 index 0000000..132b638 --- /dev/null +++ b/test-py/app/routes/plan.py @@ -0,0 +1,443 @@ +""" +Plan API Route — Daily Planning Agent + +POST /api/plan + +Triggers the triage agent to scan connected services (Slack, Calendar, +Linear, GitHub, Gmail, etc.), classify each item, and stream back +structured data as NDJSON. +""" + +import json +import re + +from fastapi import APIRouter, Depends, Request +from fastapi.responses import JSONResponse, StreamingResponse +from langchain_core.messages import HumanMessage +from sqlalchemy.ext.asyncio import AsyncSession + +from app.agent import get_llm +from app.arcade_oauth import get_cached_tools, get_mcp_client, set_elicitation_callback +from app.auth import get_current_user +from app.database import get_db + +router = APIRouter(tags=["plan"]) + + +def _map_tool_to_source(tool_name: str | None) -> str: + if not tool_name: + return "other" + service = tool_name.split(".")[0].lower() + if service == "slack": + return "slack" + if service in ("google", "googlecalendar", "calendar"): + return "google_calendar" + if service == "linear": + return "linear" + if service in ("git", "github"): + return "github" + if service == "gmail": + return "gmail" + return service or "other" + + +def _build_plan_prompt(): + return ( + "You are a daily planning and triage agent. You have access to tools " + "that connect to the user's services (e.g. Slack, Google Calendar, " + "Linear, GitHub, Gmail). Your job is to thoroughly scan all available " + "sources, read recent items AND currently assigned work, and classify " + "each one.\n\n" + "WORKFLOW:\n" + "1. In your FIRST step, call every available non-WhoAmI tool in " + "parallel — one per source.\n" + "2. After getting results, if any tool offers parameters for deeper " + "queries (e.g. filtering, pagination, or fetching specific items), " + "make follow-up calls to get more data.\n" + "3. Classify each item and output a structured JSON block.\n" + "4. After processing ALL sources, output a summary.\n" + "5. Do NOT call *_WhoAmI tools — those are for auth checking, " + "not data fetching.\n" + "6. If a tool returns truncated results, work with what you have " + "— do not retry the same call.\n\n" + "IMPORTANT RULES FOR TOOL RESULTS:\n" + "- Do NOT create items for empty results. If a source returns " + "0 items, skip it silently.\n" + "- Do NOT create items for metadata like 'you are a member of " + "N channels' — that is not actionable.\n" + "- Only create items for ACTUAL content: messages, notifications, " + "events, issues, emails, PRs, etc.\n" + "- If a tool returns a list of channels/conversations, do NOT " + "classify the list itself. Only classify individual messages or " + "items with actual content.\n" + "- If a tool returns an authorization error, skip it and move on " + "— do not create an item for the error.\n\n" + "CLASSIFICATION:\n" + "- category: NEEDS_REPLY | NEEDS_FEEDBACK | NEEDS_DECISION " + "| NEEDS_REVIEW | ATTEND | FYI | IGNORE\n" + "- priority: P0 (urgent) | P1 (important) | P2 (can wait) | FYI\n" + "- effort: XS (<5min) | S (5-15min) | M (15-30min) | L (>30min)\n" + "- confidence: 0.0 to 1.0\n\n" + "SOURCE MAPPING:\n" + '- Tools starting with "Slack" -> source: "slack"\n' + '- Tools starting with "Google", "GoogleCalendar", or "Calendar"' + ' -> source: "google_calendar"\n' + '- Tools starting with "Linear" -> source: "linear"\n' + '- Tools starting with "Git" or "GitHub" -> source: "github"\n' + '- Tools starting with "Gmail" -> source: "gmail"\n' + '- Anything else -> source: lowercase service name (e.g. "notion", "dropbox")\n\n' + "OUTPUT: For EACH item, output EXACTLY this on its own line:\n\n" + "```json:task\n" + "{\n" + ' "id": "",\n' + ' "source": "slack",\n' + ' "sourceDetail": "DM with Alice",\n' + ' "summary": "<1-2 sentences>",\n' + ' "category": "NEEDS_REPLY",\n' + ' "priority": "P1",\n' + ' "effort": "S",\n' + ' "why": "",\n' + ' "suggestedNextStep": "",\n' + ' "confidence": 0.85,\n' + ' "participants": [{"id": "", "name": ""}],\n' + ' "url": "",\n' + ' "scheduledTime": ""\n' + "}\n" + "```\n\n" + "After all items from all sources, output:\n" + "```json:summary\n" + '{"total": , "bySource": {"slack": 5, "google_calendar": 3, "linear": 2}}\n' + "```\n\n" + "URL RULES:\n" + "Prefer a direct deep link to the item itself:\n" + '- Slack: use the "permalink" field if present' + " (https://.slack.com/archives//p)\n" + "- GitHub: use the issue or PR URL on github.com\n" + "- Linear: use the Linear issue URL\n" + "- Gmail: use the Gmail thread URL (https://mail.google.com/mail/u/0/#inbox/)\n" + '- Google Calendar: use the "htmlLink" field if present\n' + "If no direct deep link is available, fall back to the most relevant URL" + " found anywhere in the tool response.\n" + 'Only omit "url" if there is truly no URL available in the response at all.\n\n' + "Rules:\n" + "- One json:task block per ACTIONABLE item (skip empty results, metadata, and errors)\n" + "- Brief status text between blocks is fine\n" + "- Process ALL available sources before the summary\n" + "- If a tool requires authorization, skip it and move on to other sources\n" + "- If errors occur reading a source, skip it silently\n" + "- Use ATTEND category for calendar events you need to join\n" + "- Use NEEDS_REVIEW for code reviews (PRs, etc.)\n\n" + ) + + +def _patch_tool_schemas(tools): + """Ensure all tool schemas have a 'properties' field.""" + for tool in tools: + schema = getattr(tool, "args_schema", None) + if isinstance(schema, dict) and "properties" not in schema: + schema["properties"] = {} + return tools + + +def _extract_auth_url(content) -> str | None: + """Fallback: extract auth URL from tool output for gateways that don't support elicitation.""" + if not isinstance(content, str): + return None + try: + parsed = json.loads(content) + return ( + parsed.get("authorization_url") + or parsed.get("url") + or (parsed.get("structuredContent") or {}).get("authorization_url") + ) + except (json.JSONDecodeError, AttributeError): + return None + + +def _extract_json_blocks(text: str): + """Extract json:task and json:summary blocks from accumulated text.""" + tasks = [] + summary = None + last_consumed = 0 + + for match in re.finditer(r"```json:task\s*\n([\s\S]*?)```", text): + try: + tasks.append(json.loads(match.group(1).strip())) + end = match.start() + len(match.group(0)) + if end > last_consumed: + last_consumed = end + except json.JSONDecodeError: + pass + + for match in re.finditer(r"```json:summary\s*\n([\s\S]*?)```", text): + try: + raw = json.loads(match.group(1).strip()) + # Normalize: handle both old and new summary shapes + if "total" in raw and "bySource" in raw: + summary = raw + elif "tasks" in raw: + summary = {"total": raw["tasks"], "bySource": {}} + else: + summary = {"total": 0, "bySource": {}} + end = match.start() + len(match.group(0)) + if end > last_consumed: + last_consumed = end + except json.JSONDecodeError: + pass + + remaining = text[last_consumed:] if last_consumed > 0 else text + return tasks, summary, remaining + + +def _encode_event(event: dict) -> str: + return json.dumps(event) + "\n" + + +@router.post("/api/plan") +async def plan(request: Request, db: AsyncSession = Depends(get_db)): + user = await get_current_user(request, db) + if not user: + return JSONResponse({"error": "Unauthorized"}, status_code=401) + + plan_prompt = _build_plan_prompt() + + async def stream(): + from langgraph.prebuilt import create_react_agent + + yield _encode_event({"type": "status", "message": "Connecting to Arcade Gateway..."}) + + # Wire per-request elicitation events to the NDJSON stream. + # The elicitation callback fires during tool execution; we buffer events + # and drain them on each iteration of the stream loop. + elicitation_events: list[dict] = [] + set_elicitation_callback( + lambda eid, msg, url: elicitation_events.append( + {"type": "elicitation", "elicitationId": eid, "authUrl": url, "message": msg} + ) + ) + + mcp_client = get_mcp_client() + + try: + all_tools = await get_cached_tools(mcp_client) + all_tools = _patch_tool_schemas(all_tools) + + # Only keep tools useful for triage — tools from known services, no mutations. + # Uses pattern matching so it works regardless of Arcade's exact naming + # convention (underscores, dots, or mixed casing are all handled). + _KNOWN_SERVICE = re.compile( + r"^(github|gmail|google|calendar|linear|slack)", + re.IGNORECASE, + ) + _MUTATION = re.compile( + r"create|update|delete|send|reply|post|archive|remove" + r"|add|invite|merge|close|assign|edit|publish|comment", + re.IGNORECASE, + ) + + def _is_triage_tool(name: str) -> bool: + if not _KNOWN_SERVICE.search(name): + return False + return not _MUTATION.search(name) + + MAX_TOOL_RESULT_CHARS = 4000 + + def _wrap_tool(tool): + """Wrap a tool to truncate large results and prevent context overflow.""" + original_coroutine = tool.coroutine + original_func = tool.func + + async def _truncated_ainvoke(*args, **kwargs): + result = await original_coroutine(*args, **kwargs) + if isinstance(result, str) and len(result) > MAX_TOOL_RESULT_CHARS: + return ( + result[:MAX_TOOL_RESULT_CHARS] + + f"\n...[truncated {len(result) - MAX_TOOL_RESULT_CHARS} chars]" + ) + return result + + updates = {"coroutine": _truncated_ainvoke} + if original_func is not None: + + def _truncated_invoke(*args, **kwargs): + result = original_func(*args, **kwargs) + s = ( + result + if isinstance(result, str) + else (json.dumps(result) if result else "") + ) + if len(s) > MAX_TOOL_RESULT_CHARS: + return ( + s[:MAX_TOOL_RESULT_CHARS] + + f"\n...[truncated {len(s) - MAX_TOOL_RESULT_CHARS} chars]" + ) + return result + + updates["func"] = _truncated_invoke + + return tool.model_copy(update=updates) + + tools = [ + _wrap_tool(t) + for t in all_tools + if _is_triage_tool(t.name) and not re.search(r"[._]WhoAmI$", t.name, re.IGNORECASE) + ] + + tool_names = [t.name for t in tools] + sources = list({_map_tool_to_source(n) for n in tool_names if n}) + print( + f"[plan] {len(tool_names)} triage tools " + f"(of {len(all_tools)} total) " + f"from sources: {', '.join(sources)}" + ) + + yield _encode_event( + { + "type": "status", + "message": ( + f"Found {len(tool_names)} tools across " + f"{len(sources)} sources. Starting triage..." + ), + } + ) + + yield _encode_event({"type": "sources", "sources": sources}) + + # Build a per-source tool inventory so the model knows exactly what to call + tools_by_source: dict[str, list[str]] = {} + for name in tool_names: + src = _map_tool_to_source(name) + tools_by_source.setdefault(src, []).append(name) + tool_inventory = "\n".join( + f"{src}: {', '.join(names)}" for src, names in tools_by_source.items() + ) + + llm = get_llm() + agent = create_react_agent(llm, tools=tools, prompt=plan_prompt) + + accumulated_text = "" + emitted_task_count = 0 + emitted_summary = False + + from datetime import date + + today = date.today().strftime("%A, %B %d, %Y") + user_message = ( + f"Plan my day. Today is {today}.\n\n" + f"Here are the tools available by source:\n\n" + f"{tool_inventory}\n\n" + "In your FIRST step, call ALL non-WhoAmI tools in " + "parallel — one call per tool. Do NOT skip any source.\n" + "For calendar tools, make sure to pass today's date " + "range so you get today's events.\n" + "After getting results, classify every item. If any " + "source returned a list you can drill into, make " + "follow-up calls.\n" + "I need a COMPLETE picture: notifications, assigned " + "work, upcoming events, and unread messages." + ) + + async for event in agent.astream( + {"messages": [HumanMessage(content=user_message)]}, + stream_mode="updates", + ): + # Drain any elicitation events that arrived during tool execution + while elicitation_events: + yield _encode_event(elicitation_events.pop(0)) + + for node_name, update in event.items(): + # Check for tool results with auth URLs + if node_name == "tools" and "messages" in update: + for msg in update["messages"]: + content = msg.content if hasattr(msg, "content") else str(msg) + auth_url = _extract_auth_url(content) + if auth_url: + tool_name = getattr(msg, "name", "") + source = _map_tool_to_source(tool_name) + yield _encode_event( + { + "type": "auth_required", + "authUrl": auth_url, + "toolName": source, + } + ) + + name = getattr(msg, "name", "") + if name: + source = _map_tool_to_source(name) + yield _encode_event( + { + "type": "status", + "message": f"Calling {source}: {name}...", + } + ) + + # Check for agent text output + if node_name == "agent" and "messages" in update: + for msg in update["messages"]: + if ( + hasattr(msg, "content") + and msg.content + and isinstance(msg.content, str) + ): + accumulated_text += msg.content + + tasks, summary, remaining = _extract_json_blocks(accumulated_text) + accumulated_text = remaining + + for task in tasks: + emitted_task_count += 1 + yield _encode_event({"type": "task", "data": task}) + suffix = "s" if emitted_task_count > 1 else "" + yield _encode_event( + { + "type": "status", + "message": ( + f"Classified {emitted_task_count} item{suffix}..." + ), + } + ) + + if summary: + yield _encode_event({"type": "summary", "data": summary}) + emitted_summary = True + + # Final pass + if accumulated_text: + tasks, summary, _ = _extract_json_blocks(accumulated_text) + for task in tasks: + emitted_task_count += 1 + yield _encode_event({"type": "task", "data": task}) + if summary: + yield _encode_event({"type": "summary", "data": summary}) + emitted_summary = True + + if not emitted_summary and emitted_task_count > 0: + yield _encode_event( + { + "type": "summary", + "data": {"total": emitted_task_count, "bySource": {}}, + } + ) + + yield _encode_event({"type": "done"}) + + except Exception as e: + import traceback + + traceback.print_exc() + yield _encode_event( + { + "type": "error", + "message": str(e), + } + ) + yield _encode_event({"type": "done"}) + finally: + set_elicitation_callback(None) + + return StreamingResponse( + stream(), + media_type="application/x-ndjson", + headers={"Cache-Control": "no-cache"}, + ) diff --git a/test-py/app/static/dashboard.js b/test-py/app/static/dashboard.js new file mode 100644 index 0000000..6be914e --- /dev/null +++ b/test-py/app/static/dashboard.js @@ -0,0 +1,440 @@ +// Dashboard client — vanilla JS for the triage dashboard. +// Handles: Arcade connection gate, plan streaming (NDJSON), task rendering. + +// --- DOM refs --- +const gate = document.getElementById("gate"); +const gateChecking = document.getElementById("gate-checking"); +const gateAuth = document.getElementById("gate-auth"); +const gateAuthLink = document.getElementById("gate-auth-link"); +const gateAuthCta = document.getElementById("gate-auth-cta"); +const gateAuthWaiting = document.getElementById("gate-auth-waiting"); +const gateAuthRetry = document.getElementById("gate-auth-retry"); +const gateAuthWaitingRetry = document.getElementById("gate-auth-waiting-retry"); +const gateError = document.getElementById("gate-error"); +const gateErrorMsg = document.getElementById("gate-error-msg"); +const dashboardArea = document.getElementById("dashboard-area"); +const emptyState = document.getElementById("empty-state"); +const loadingSkeletons = document.getElementById("loading-skeletons"); +const statsBar = document.getElementById("stats-bar"); +const taskList = document.getElementById("task-list"); +const taskContainer = document.getElementById("task-container"); +const taskCount = document.getElementById("task-count"); +const authPrompts = document.getElementById("auth-prompts"); +const statusBar = document.getElementById("status-bar"); +const statusText = document.getElementById("status-text"); +const errorBar = document.getElementById("error-bar"); +const planBtn = document.getElementById("plan-btn"); +const replanBtn = document.getElementById("replan-btn"); +const logoutBtn = document.getElementById("logout-btn"); + +// --- State --- +let items = []; +let sourceStatuses = {}; // { github: 'checking' | 'connected' | 'auth_required', ... } +let authUrlsBySource = {}; // { github: 'https://...', ... } + +// --- Date display --- +const dateEl = document.getElementById("header-date"); +if (dateEl) { + dateEl.textContent = new Date().toLocaleDateString("en-US", { + weekday: "long", + month: "short", + day: "numeric", + }); +} + +// --- Arcade connection gate --- +let connectInFlight = false; +let authInProgress = false; + +function showGateState(state) { + gateChecking.classList.add("hidden"); + gateAuth.classList.add("hidden"); + gateError.classList.add("hidden"); + gate.classList.remove("hidden"); + dashboardArea.classList.add("hidden"); + + if (state === "checking") { + gateChecking.classList.remove("hidden"); + } else if (state === "auth") { + resetGateAuthWaiting(); + gateAuth.classList.remove("hidden"); + } else if (state === "error") { + gateError.classList.remove("hidden"); + } else if (state === "connected") { + gate.classList.add("hidden"); + dashboardArea.classList.remove("hidden"); + dashboardArea.classList.add("flex"); + } +} + +async function checkArcadeConnection() { + if (connectInFlight) return; + if (authInProgress) return; + connectInFlight = true; + showGateState("checking"); + + try { + const res = await fetch("/api/arcade/connect", { method: "POST" }); + if (res.status === 401) { + window.location.href = "/"; + return; + } + const data = await res.json(); + + if (data.connected) { + authInProgress = false; + showGateState("connected"); + checkSourceStatuses(); + } else if (data.authUrl) { + authInProgress = true; + gateAuthLink.href = data.authUrl; + showGateState("auth"); + } else { + authInProgress = false; + gateErrorMsg.textContent = data.error || "Could not connect to Arcade Gateway."; + showGateState("error"); + } + } catch { + authInProgress = false; + gateErrorMsg.textContent = "Failed to check Arcade connection."; + showGateState("error"); + } finally { + connectInFlight = false; + } +} + +function showGateAuthWaiting() { + gateAuthCta.classList.add("hidden"); + gateAuthWaiting.classList.remove("hidden"); +} + +function resetGateAuthWaiting() { + gateAuthCta.classList.remove("hidden"); + gateAuthWaiting.classList.add("hidden"); +} + +function retryArcadeConnection() { + resetGateAuthWaiting(); + authInProgress = false; + checkArcadeConnection(); +} + +gateAuthLink.addEventListener("click", showGateAuthWaiting); +gateAuthRetry.addEventListener("click", retryArcadeConnection); +gateAuthWaitingRetry.addEventListener("click", retryArcadeConnection); +checkArcadeConnection(); +window.addEventListener("focus", () => { + if (!dashboardArea.classList.contains("hidden")) return; + if (!authInProgress) return; + retryArcadeConnection(); +}); + +// --- Logout --- +logoutBtn.addEventListener("click", async () => { + await fetch("/api/auth/logout", { method: "POST" }); + window.location.href = "/"; +}); + +// --- Check source auth status via WhoAmI tools --- +async function checkSourceStatuses() { + try { + const res = await fetch("/api/sources", { method: "POST" }); + if (!res.ok) return; + const data = await res.json(); + for (const [source, info] of Object.entries(data.sources || {})) { + sourceStatuses[source] = info.status; + if (info.authUrl) authUrlsBySource[source] = info.authUrl; + } + } catch { + /* silent */ + } +} + +// --- Utility --- +function escapeHtml(text) { + const div = document.createElement("div"); + div.textContent = text; + return div.innerHTML; +} + +function sanitizeUrl(url) { + try { + const parsed = new URL(url); + if (parsed.protocol === "https:" || parsed.protocol === "http:") return url; + } catch { + /* invalid URL */ + } + return "#"; +} + +// --- Source config --- +const SOURCE_CONFIG = { + slack: { + label: "Slack", + color: + "bg-purple-100 text-purple-900 border-purple-200 dark:bg-purple-950 dark:text-purple-200 dark:border-purple-900", + }, + google_calendar: { + label: "Calendar", + color: + "bg-blue-100 text-blue-900 border-blue-200 dark:bg-blue-950 dark:text-blue-200 dark:border-blue-900", + }, + linear: { + label: "Linear", + color: + "bg-indigo-100 text-indigo-900 border-indigo-200 dark:bg-indigo-950 dark:text-indigo-200 dark:border-indigo-900", + }, + github: { + label: "GitHub", + color: "bg-muted text-muted-foreground border-border", + }, + gmail: { + label: "Gmail", + color: + "bg-red-100 text-red-900 border-red-200 dark:bg-red-950 dark:text-red-200 dark:border-red-900", + }, + other: { + label: "Other", + color: "bg-muted text-muted-foreground border-border", + }, +}; + +// --- Priority / Category badges --- +const PRIORITY_COLORS = { + P0: "bg-red-100 text-red-900 border-red-200 dark:bg-red-950 dark:text-red-200 dark:border-red-900", + P1: "bg-amber-100 text-amber-800 border-amber-200 dark:bg-amber-950 dark:text-amber-200 dark:border-amber-900", + P2: "bg-gray-100 text-gray-700 border-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700", + FYI: "bg-muted text-muted-foreground border-border", +}; + +const CATEGORY_LABELS = { + NEEDS_REPLY: "Needs Reply", + NEEDS_FEEDBACK: "Needs Feedback", + NEEDS_DECISION: "Needs Decision", + NEEDS_REVIEW: "Needs Review", + ATTEND: "Attend", + FYI: "FYI", + IGNORE: "Ignore", +}; + +function createTaskCard(task, index) { + const card = document.createElement("div"); + const priorityClass = PRIORITY_COLORS[task.priority] || PRIORITY_COLORS.FYI; + const categoryLabel = CATEGORY_LABELS[task.category] || task.category; + const source = SOURCE_CONFIG[task.source] || SOURCE_CONFIG.other; + + card.className = + "task-card bg-card rounded-xl border border-border p-4 hover:shadow-md transition-shadow space-y-3"; + card.style.animation = `fadeSlideIn 0.3s ease-out ${index * 0.05}s both`; + + const subtitle = + task.sourceDetail || + (task.participants || []).map((p) => escapeHtml(p.name || p.id)).join(", "); + const summaryHtml = task.url + ? `${escapeHtml(task.summary || "")}` + : `

${escapeHtml(task.summary || "")}

`; + + const timeHtml = task.scheduledTime + ? `${new Date(task.scheduledTime).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" })}` + : ""; + + const categoryBadge = `${escapeHtml(categoryLabel)}`; + + card.innerHTML = ` +
+ ${escapeHtml(source.label)} + ${escapeHtml(task.priority)} + ${categoryBadge} + ${timeHtml} + ${task.effort ? `${escapeHtml(task.effort)}` : ""} +
+ ${summaryHtml} +
+ ${subtitle ? `${escapeHtml(subtitle)}` : ""} + ${task.suggestedNextStep ? `${escapeHtml(task.suggestedNextStep)}` : ""} +
+ `; + + return card; +} + +function renderTasks() { + taskContainer.innerHTML = ""; + items.forEach((task, i) => taskContainer.appendChild(createTaskCard(task, i))); + taskCount.textContent = `(${items.length})`; +} + +function renderStats(data) { + const container = document.getElementById("stats-container"); + if (!container) return; + container.innerHTML = ""; + + const statCardClass = "stats-card bg-card rounded-xl border border-border p-4"; + + // Total card + const totalCard = document.createElement("div"); + totalCard.className = statCardClass; + totalCard.innerHTML = ` +
+ Total +
+
${data.total || 0}
+ `; + container.appendChild(totalCard); + + // Per-source cards + const bySource = data.bySource || {}; + for (const [source, count] of Object.entries(bySource)) { + if (count <= 0) continue; + const config = SOURCE_CONFIG[source] || SOURCE_CONFIG.other; + const card = document.createElement("div"); + card.className = statCardClass; + card.innerHTML = ` +
+ ${escapeHtml(config.label)} +
+
${count}
+ `; + container.appendChild(card); + } +} + +function showState() { + const hasItems = items.length > 0; + const isLoading = planBtn.disabled; + const hasAuthPrompts = !authPrompts.classList.contains("hidden"); + + emptyState.classList.toggle("hidden", hasItems || isLoading || hasAuthPrompts); + loadingSkeletons.classList.toggle("hidden", !isLoading || hasItems || hasAuthPrompts); + statsBar.classList.toggle("hidden", !hasItems); + taskList.classList.toggle("hidden", !hasItems); + if (replanBtn) { + replanBtn.classList.toggle("hidden", !hasItems); + } +} + +// --- Auth prompts --- +function addAuthPrompt(url, toolName) { + if (authPrompts.querySelector(`[data-url="${CSS.escape(url)}"]`)) return; + authPrompts.classList.remove("hidden"); + + const label = toolName || "Service"; + const card = document.createElement("div"); + card.className = "auth-prompt-card bg-warning/10 border border-warning/30 rounded-lg p-4"; + card.dataset.url = url; + card.innerHTML = ` +
+ +

${escapeHtml(label)} authorization required

+
+

${escapeHtml(label)} needs permission to continue.

+
+ Authorize + +
+ `; + + card.querySelector(".dismiss-auth-btn").addEventListener("click", () => { + card.remove(); + if (!authPrompts.children.length) authPrompts.classList.add("hidden"); + showState(); + // Re-check sources — user may have just authorized + checkSourceStatuses(); + }); + + authPrompts.appendChild(card); + showState(); +} + +// --- Plan execution --- +planBtn.addEventListener("click", handlePlan); +if (replanBtn) { + replanBtn.addEventListener("click", handlePlan); +} + +async function handlePlan() { + planBtn.disabled = true; + planBtn.textContent = "Planning..."; + if (replanBtn) { + replanBtn.disabled = true; + replanBtn.textContent = "Replanning..."; + } + items = []; + sourceStatuses = {}; + authUrlsBySource = {}; + errorBar.classList.add("hidden"); + authPrompts.innerHTML = ""; + authPrompts.classList.add("hidden"); + showState(); + + try { + const res = await fetch("/api/plan", { method: "POST" }); + if (!res.ok || !res.body) throw new Error(`Plan request failed (${res.status})`); + + const reader = res.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split("\n"); + buffer = lines.pop() || ""; + + for (const line of lines) { + if (!line.trim()) continue; + try { + const event = JSON.parse(line); + switch (event.type) { + case "task": + items.push(event.data); + renderTasks(); + showState(); + break; + case "summary": + renderStats(event.data); + break; + case "sources": + sourceStatuses = Object.fromEntries(event.sources.map((s) => [s, "checking"])); + break; + case "auth_required": + addAuthPrompt(event.authUrl || event.auth_url, event.toolName); + if (event.toolName) { + sourceStatuses[event.toolName] = "auth_required"; + authUrlsBySource[event.toolName] = event.authUrl || event.auth_url; + } + break; + case "status": + statusBar.classList.remove("hidden"); + statusText.textContent = event.message; + break; + case "error": + errorBar.textContent = event.message; + errorBar.classList.remove("hidden"); + break; + } + } catch { + /* skip malformed */ + } + } + } + } catch (err) { + errorBar.textContent = err.message || "Something went wrong"; + errorBar.classList.remove("hidden"); + } finally { + planBtn.disabled = false; + planBtn.textContent = "Plan my day"; + if (replanBtn) { + replanBtn.disabled = false; + replanBtn.textContent = "Replan my day"; + } + statusBar.classList.add("hidden"); + for (const key of Object.keys(sourceStatuses)) { + if (sourceStatuses[key] === "checking") sourceStatuses[key] = "connected"; + } + showState(); + } +} diff --git a/test-py/app/static/favicon.svg b/test-py/app/static/favicon.svg new file mode 100644 index 0000000..51986e4 --- /dev/null +++ b/test-py/app/static/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/test-py/app/static/styles.css b/test-py/app/static/styles.css new file mode 100644 index 0000000..f693a5f --- /dev/null +++ b/test-py/app/static/styles.css @@ -0,0 +1,150 @@ +/* Minimal additions beyond Tailwind CDN */ + +@keyframes spin { + to { + transform: rotate(360deg); + } +} +.animate-spin { + animation: spin 1s linear infinite; +} + +@keyframes pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} +.animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +@keyframes fadeSlideIn { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Custom color utilities — Tailwind CDN is v3 and ignores @theme, so we define these manually */ +.bg-background { + background-color: var(--background); +} +.bg-card { + background-color: var(--card); +} +.bg-muted { + background-color: var(--muted); +} +.bg-primary { + background-color: var(--primary); +} +.bg-primary\/90 { + background-color: color-mix(in oklch, var(--primary) 90%, transparent); +} +.bg-destructive\/10 { + background-color: color-mix(in oklch, var(--destructive) 10%, transparent); +} +.bg-warning\/10 { + background-color: color-mix(in oklch, var(--warning) 10%, transparent); +} + +.text-foreground { + color: var(--foreground); +} +.text-muted-foreground { + color: var(--muted-foreground); +} +.text-primary { + color: var(--primary); +} +.text-primary-foreground { + color: var(--primary-foreground); +} +.text-card-foreground { + color: var(--card-foreground); +} +.text-destructive { + color: var(--destructive); +} +.text-warning { + color: var(--warning); +} + +.border-border { + border-color: var(--border); +} +.border-input { + border-color: var(--input); +} +.border-destructive\/30 { + border-color: color-mix(in oklch, var(--destructive) 30%, transparent); +} +.border-warning\/30 { + border-color: color-mix(in oklch, var(--warning) 30%, transparent); +} +.border-t-primary { + border-top-color: var(--primary); +} +.border-t-foreground { + border-top-color: var(--foreground); +} + +.placeholder-muted-foreground::placeholder { + color: var(--muted-foreground); +} +.ring-ring { + --tw-ring-color: var(--ring); +} + +.hover\:bg-primary\/90:hover { + background-color: color-mix(in oklch, var(--primary) 90%, transparent); +} +.hover\:bg-muted:hover { + background-color: var(--muted); +} +.hover\:text-foreground:hover { + color: var(--foreground); +} + +/* Dark mode: dynamically generated task and stats cards */ +.dark .task-card, +.dark .stats-card { + background-color: var(--card); + border-color: var(--border); + color: var(--card-foreground); +} +.dark .task-card .text-gray-400, +.dark .task-card .text-gray-500, +.dark .stats-card .text-gray-500 { + color: var(--muted-foreground); +} +.dark .next-step-hint { + background-color: var(--muted); + color: var(--muted-foreground); +} + +/* Dark mode: auth prompt cards */ +.dark .auth-prompt-card { + background-color: color-mix(in oklch, var(--warning) 10%, transparent); + border-color: color-mix(in oklch, var(--warning) 30%, transparent); + color: var(--warning-foreground); +} +.dark .auth-prompt-card .text-gray-500 { + color: var(--muted-foreground); +} +.dark .dismiss-auth-btn { + border-color: var(--border); + color: var(--muted-foreground); + background: transparent; +} +.dark .dismiss-auth-btn:hover { + background-color: var(--muted); +} diff --git a/test-py/app/system-prompt.md b/test-py/app/system-prompt.md new file mode 100644 index 0000000..959fdd7 --- /dev/null +++ b/test-py/app/system-prompt.md @@ -0,0 +1,34 @@ +You are a daily planning and triage assistant. You help users quickly process items from their connected services — messages, calendar events, tasks, PRs, and emails. + +WORKFLOW: + +1. Check what tools are available and scan 3-5 items from each source. +2. Read recent activity from each item. +3. Classify each item and present results. +4. Ask the user to reply "continue" for the next batch. + +CLASSIFICATION: + +- Category: NEEDS_REPLY | NEEDS_FEEDBACK | NEEDS_DECISION | NEEDS_REVIEW | ATTEND | FYI | IGNORE +- Priority: P0 (urgent) | P1 (important) | P2 (can wait) | FYI +- Effort: XS (<5min) | S (5-15min) | M (15-30min) | L (>30min) + +OUTPUT FORMAT (one line per item): +**[Source] item-name** | CATEGORY | PRIORITY | EFFORT - Brief summary + +After each batch, show a summary and say: Reply "continue" to process the next batch. + +RULES: + +- Make at most 3-5 tool calls per response, then show results. +- Direct mentions or assignments to the user are P0/P1. Unread items get higher priority. +- Calendar events happening soon are P0/P1. Use ATTEND category for meetings. +- Code reviews (PRs) use NEEDS_REVIEW category. +- If a source has no recent activity, note it and move on. +- Handle errors gracefully — skip failed sources and continue. + +OAUTH HANDLING: +When a tool returns an authorization response with a URL, tell the user: +"Please visit this URL to grant access: [url]". Then wait for them to confirm. + +Start by checking what tools are available and fetching items from each source. diff --git a/test-py/app/templates/base.html b/test-py/app/templates/base.html new file mode 100644 index 0000000..43ccacf --- /dev/null +++ b/test-py/app/templates/base.html @@ -0,0 +1,100 @@ + + + + + + {% block title %}Arcade Agent{% endblock %} + + + + + + + + + + + + + {% block content %}{% endblock %} {% block scripts %}{% endblock %} + + + diff --git a/test-py/app/templates/dashboard.html b/test-py/app/templates/dashboard.html new file mode 100644 index 0000000..9400855 --- /dev/null +++ b/test-py/app/templates/dashboard.html @@ -0,0 +1,324 @@ +{% extends "base.html" %} {% block title %}Dashboard - Arcade Agent{% endblock %} {% block content +%} +
+ +
+
+
+ + Arcade Agent +
+ +
+
+ + +
+
+ +
+ +
+
+
+

Connecting to Arcade...

+
+ + +
+ + + +
+
+{% endblock %} {% block scripts %} + + +{% endblock %} diff --git a/test-py/app/templates/login.html b/test-py/app/templates/login.html new file mode 100644 index 0000000..23b9654 --- /dev/null +++ b/test-py/app/templates/login.html @@ -0,0 +1,154 @@ +{% extends "base.html" %} {% block title %}Login - Arcade Agent{% endblock %} {% block content %} +
+
+
+ + Arcade Agent +
+ +
+
+ +
+
+

Two separate sign-ins

+ +
+

This creates a local account for your agent app — it's just for session management and stays in your own database. You'll connect your Arcade account on the next screen to give the agent access to your tools.

+
+
+
+ +
+ +
+ + +
+ +

+ Get started with Arcade Agent +

+ +
+ + + + + + +
+
+
+
+{% endblock %} {% block scripts %} + + +{% endblock %} diff --git a/test-py/pyproject.toml b/test-py/pyproject.toml new file mode 100644 index 0000000..a5d2896 --- /dev/null +++ b/test-py/pyproject.toml @@ -0,0 +1,5 @@ +[project] +name = "test-py" +version = "0.1.0" +description = "AI agent powered by LangGraph + Arcade" +requires-python = ">=3.10" diff --git a/test-py/requirements.txt b/test-py/requirements.txt new file mode 100644 index 0000000..5ed0983 --- /dev/null +++ b/test-py/requirements.txt @@ -0,0 +1,17 @@ +langchain-openai>=0.3 +langchain-anthropic>=0.3 +langchain-mcp-adapters>=0.2.2 +mcp>=1.27 +langgraph>=0.3 +fastapi>=0.115 +fastapi-users[sqlalchemy]>=14.0 +uvicorn[standard]>=0.34 +sqlalchemy[asyncio]>=2.0 +aiosqlite>=0.20 +python-dotenv>=1.0 +jinja2>=3.1 +sse-starlette>=2.0 +pydantic-settings>=2.0 +httpx>=0.27 +alembic>=1.14 +email-validator>=2.0 diff --git a/test-py/ruff.toml b/test-py/ruff.toml new file mode 100644 index 0000000..d33e0dc --- /dev/null +++ b/test-py/ruff.toml @@ -0,0 +1,25 @@ +line-length = 100 +target-version = "py312" + +[lint] +select = [ + "E", "W", # pycodestyle + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "S", # flake8-bandit (security) + "C4", # flake8-comprehensions + "SIM", # flake8-simplify + "UP", # pyupgrade + "RUF", # ruff-specific rules +] +ignore = [ + "S105", "S106", "S107", # hardcoded password — too many false positives + "B008", # Depends() in args is standard FastAPI +] + +[lint.per-file-ignores] +"alembic/**/*.py" = ["E501", "UP"] + +[format] +quote-style = "double" diff --git a/test-py/server.py b/test-py/server.py new file mode 100644 index 0000000..2636381 --- /dev/null +++ b/test-py/server.py @@ -0,0 +1,4 @@ +import uvicorn + +if __name__ == "__main__": + uvicorn.run("app.main:app", host="localhost", port=8765) diff --git a/test-py/ty.toml b/test-py/ty.toml new file mode 100644 index 0000000..d88018c --- /dev/null +++ b/test-py/ty.toml @@ -0,0 +1,12 @@ +[environment] +python-version = "3.12" + +[rules] +# Pydantic models use dynamic constructors with aliases (e.g. `model` vs `model_name`) +# that ty doesn't resolve yet. SQLAlchemy Column types also cause false positives. +unknown-argument = "ignore" +missing-argument = "ignore" +invalid-argument-type = "ignore" +# ty doesn't narrow types through hasattr() or isinstance+continue patterns +no-matching-overload = "ignore" +not-iterable = "ignore"