From a864258cb8319a4c1f09848078265357f7382374 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Mon, 30 Mar 2026 12:58:45 -0300 Subject: [PATCH 01/18] feat(ui): integrate FSM, adaptive routing, and provider diversity --- docs/openapi.yaml | 2 +- .../services/__tests__/volumeDetector.test.ts | 40 ++- open-sse/services/volumeDetector.ts | 16 +- src/app/(dashboard)/dashboard/a2a/page.tsx | 99 ++++--- .../dashboard/audit/ConfigAuditViewer.tsx | 272 ++++++++++++++++++ src/app/(dashboard)/dashboard/audit/page.tsx | 44 +++ src/app/(dashboard)/dashboard/health/page.tsx | 77 +++++ .../(dashboard)/dashboard/providers/page.tsx | 71 ++++- .../settings/components/RoutingTab.tsx | 34 +++ src/app/api/audit/route.ts | 38 +++ src/app/api/health/degradation/route.ts | 30 ++ src/app/api/providers/expiration/route.ts | 17 ++ src/shared/components/DegradationBadge.tsx | 41 +++ src/shared/components/Header.tsx | 4 +- src/shared/components/UsageAnalytics.tsx | 26 +- src/shared/constants/sidebarVisibility.ts | 2 + src/shared/validation/settingsSchemas.ts | 2 + 17 files changed, 752 insertions(+), 63 deletions(-) create mode 100644 src/app/(dashboard)/dashboard/audit/ConfigAuditViewer.tsx create mode 100644 src/app/(dashboard)/dashboard/audit/page.tsx create mode 100644 src/app/api/audit/route.ts create mode 100644 src/app/api/health/degradation/route.ts create mode 100644 src/app/api/providers/expiration/route.ts create mode 100644 src/shared/components/DegradationBadge.tsx diff --git a/docs/openapi.yaml b/docs/openapi.yaml index e637d8451d..11d7f50a89 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: OmniRoute API - version: 3.3.3 + version: 3.3.4 description: | OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible endpoint that routes requests to multiple AI providers with load balancing, diff --git a/open-sse/services/__tests__/volumeDetector.test.ts b/open-sse/services/__tests__/volumeDetector.test.ts index e01607399a..e29684f912 100644 --- a/open-sse/services/__tests__/volumeDetector.test.ts +++ b/open-sse/services/__tests__/volumeDetector.test.ts @@ -2,9 +2,9 @@ import { describe, it } from "node:test"; import assert from "node:assert/strict"; import { detectVolumeSignals, recommendStrategyOverride } from "../volumeDetector"; -describe("volumeDetector", () => { - describe("detectVolumeSignals", () => { - it("detects simple single-message request", () => { +describe("volumeDetector", async () => { + describe("detectVolumeSignals", async () => { + it("detects simple single-message request", async () => { const body = { messages: [{ role: "user", content: "Hello" }], }; @@ -16,7 +16,7 @@ describe("volumeDetector", () => { assert.equal(signals.complexity, "trivial"); }); - it("detects tool-heavy request as high complexity", () => { + it("detects tool-heavy request as high complexity", async () => { const body = { messages: [{ role: "user", content: "Deploy the app to production" }], tools: [ @@ -31,17 +31,15 @@ describe("volumeDetector", () => { assert.equal(signals.complexity, "critical"); }); - it("detects browser keywords", () => { + it("detects browser keywords", async () => { const body = { - messages: [ - { role: "user", content: "Navigate to the page and take a screenshot" }, - ], + messages: [{ role: "user", content: "Navigate to the page and take a screenshot" }], }; const signals = detectVolumeSignals(body); assert.equal(signals.hasBrowser, true); }); - it("detects batch from multi-part content", () => { + it("detects batch from multi-part content", async () => { const parts = Array.from({ length: 20 }, (_, i) => ({ type: "text", text: `Item ${i}`, @@ -53,11 +51,9 @@ describe("volumeDetector", () => { assert.equal(signals.batchSize, 20); }); - it("detects security keywords as high complexity", () => { + it("detects security keywords as high complexity", async () => { const body = { - messages: [ - { role: "user", content: "Refactor the authentication module for production" }, - ], + messages: [{ role: "user", content: "Refactor the authentication module for production" }], }; const signals = detectVolumeSignals(body); assert.ok( @@ -67,16 +63,16 @@ describe("volumeDetector", () => { }); }); - describe("recommendStrategyOverride", () => { - it("recommends round-robin for large batches", () => { + describe("recommendStrategyOverride", async () => { + it("recommends round-robin for large batches", async () => { const signals = detectVolumeSignals({ input: Array(60).fill("item") }); - const override = recommendStrategyOverride(signals, "priority"); + const override = await recommendStrategyOverride(signals, "priority"); assert.equal(override.shouldOverride, true); assert.equal(override.strategy, "round-robin"); assert.equal(override.preferEconomy, true); }); - it("recommends premium-first for browser tasks", () => { + it("recommends premium-first for browser tasks", async () => { const signals = { batchSize: 1, estimatedTokens: 500, @@ -85,13 +81,13 @@ describe("volumeDetector", () => { hasImages: false, complexity: "high" as const, }; - const override = recommendStrategyOverride(signals, "round-robin"); + const override = await recommendStrategyOverride(signals, "round-robin"); assert.equal(override.shouldOverride, true); assert.equal(override.strategy, "priority"); assert.equal(override.forcePremium, true); }); - it("flags economy for tiny requests without changing strategy", () => { + it("flags economy for tiny requests without changing strategy", async () => { const signals = { batchSize: 1, estimatedTokens: 100, @@ -100,12 +96,12 @@ describe("volumeDetector", () => { hasImages: false, complexity: "trivial" as const, }; - const override = recommendStrategyOverride(signals, "priority"); + const override = await recommendStrategyOverride(signals, "priority"); assert.equal(override.shouldOverride, false); assert.equal(override.preferEconomy, true); }); - it("no override for normal medium requests", () => { + it("no override for normal medium requests", async () => { const signals = { batchSize: 1, estimatedTokens: 1000, @@ -114,7 +110,7 @@ describe("volumeDetector", () => { hasImages: false, complexity: "low" as const, }; - const override = recommendStrategyOverride(signals, "priority"); + const override = await recommendStrategyOverride(signals, "priority"); assert.equal(override.shouldOverride, false); assert.equal(override.preferEconomy, false); }); diff --git a/open-sse/services/volumeDetector.ts b/open-sse/services/volumeDetector.ts index 1bdd806783..56a1e78450 100644 --- a/open-sse/services/volumeDetector.ts +++ b/open-sse/services/volumeDetector.ts @@ -141,10 +141,10 @@ export function detectVolumeSignals(body: Record): VolumeSignal * @param currentStrategy - The combo's configured strategy * @returns Override recommendation */ -export function recommendStrategyOverride( +export async function recommendStrategyOverride( signals: VolumeSignals, currentStrategy: string -): StrategyOverride { +): Promise { const noOverride: StrategyOverride = { shouldOverride: false, strategy: null, @@ -153,6 +153,18 @@ export function recommendStrategyOverride( reason: "no override needed", }; + // Check if adaptive routing is enabled globally + try { + const { getSettings } = await import("@/lib/localDb"); + const settings = await getSettings(); + if (!settings.adaptiveVolumeRouting) { + return noOverride; + } + } catch (error) { + console.error("Failed to check adaptiveVolumeRouting setting:", error); + return noOverride; + } + // Rule 1: Large batch → round-robin to distribute load if (signals.batchSize >= 50) { return { diff --git a/src/app/(dashboard)/dashboard/a2a/page.tsx b/src/app/(dashboard)/dashboard/a2a/page.tsx index d0853252a9..521bb922ae 100644 --- a/src/app/(dashboard)/dashboard/a2a/page.tsx +++ b/src/app/(dashboard)/dashboard/a2a/page.tsx @@ -433,43 +433,78 @@ export default function A2ADashboardPage() { {t("tableTask")} {t("tableSkill")} {t("tableState")} + {t("tablePhase") || "FSM Status"} {t("tableUpdated")} {t("tableActions")} - {tasksData.tasks.map((task) => ( - - {task.id} - {task.skill} - - - {t(`state.${task.state}`)} - - - - {new Date(task.updatedAt).toLocaleString()} - - - - - - - ))} + {tasksData.tasks.map((task) => { + const fsmPhase = + task.metadata?.fsmPhase || (task.metadata?.workflowFSM as any)?.currentPhase; + let fsmBadgeColor = "bg-gray-500/15 text-gray-500"; + if (fsmPhase === "plan" || fsmPhase === "plan_review") + fsmBadgeColor = "bg-purple-500/15 text-purple-500"; + else if (fsmPhase === "execute") fsmBadgeColor = "bg-blue-500/15 text-blue-500"; + else if ( + ["code_review", "quality_review", "security", "test", "output_review"].includes( + fsmPhase + ) + ) + fsmBadgeColor = "bg-amber-500/15 text-amber-500"; + else if (fsmPhase === "done") fsmBadgeColor = "bg-green-500/15 text-green-500"; + else if (fsmPhase === "failed") fsmBadgeColor = "bg-red-500/15 text-red-500"; + + return ( + + {task.id} + {task.skill} + + + {t(`state.${task.state}`)} + + + + {fsmPhase ? ( + + {fsmPhase} + + ) : ( + + )} + + + {new Date(task.updatedAt).toLocaleString()} + + + + + + + ); + })} diff --git a/src/app/(dashboard)/dashboard/audit/ConfigAuditViewer.tsx b/src/app/(dashboard)/dashboard/audit/ConfigAuditViewer.tsx new file mode 100644 index 0000000000..728897e1a7 --- /dev/null +++ b/src/app/(dashboard)/dashboard/audit/ConfigAuditViewer.tsx @@ -0,0 +1,272 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { useTranslations } from "next-intl"; + +interface ConfigDiff { + added: string[]; + removed: string[]; + changed: Array<{ key: string; from: any; to: any }>; + isEmpty: boolean; +} + +interface AuditEntry { + id: string; + timestamp: string; + action: string; + target: string; + targetId: string; + targetName: string; + source: string; + before: any; + after: any; + diff: ConfigDiff; + note: string | null; +} + +export default function ConfigAuditViewer() { + const t = useTranslations("logs"); + const [entries, setEntries] = useState([]); + const [loading, setLoading] = useState(true); + const [selectedEntry, setSelectedEntry] = useState(null); + + useEffect(() => { + fetchLogs(); + }, []); + + const fetchLogs = async () => { + setLoading(true); + try { + const res = await fetch("/api/audit"); + const data = await res.json(); + setEntries(data.entries || []); + } catch (e) { + console.error(e); + } finally { + setLoading(false); + } + }; + + const getActionColor = (action: string) => { + switch (action) { + case "create": + return "text-green-400 bg-green-400/10 border-green-500/20"; + case "update": + return "text-blue-400 bg-blue-400/10 border-blue-500/20"; + case "delete": + return "text-red-400 bg-red-400/10 border-red-500/20"; + default: + return "text-gray-400 bg-gray-400/10 border-gray-500/20"; + } + }; + + if (loading) { + return ( +
+
+
+ ); + } + + if (entries.length === 0) { + return ( +
+ + + +

No Configuration Audit Logs found.

+
+ ); + } + + return ( +
+
+ + + + + + + + + + + + + {entries.map((entry) => ( + + + + + + + + + ))} + +
TimestampActionTargetResourceSourceDetails
+ {new Date(entry.timestamp).toLocaleString()} + + + {entry.action} + + + {entry.target} + + {entry.targetName} + + {entry.source} + + +
+
+ + {selectedEntry && ( +
+
+ {/* Modal Header */} +
+
+

+ {selectedEntry.action} {selectedEntry.target} +

+

+ ID: {selectedEntry.targetId} • {selectedEntry.targetName} +

+
+ +
+ + {/* Modal Content */} +
+ {selectedEntry.note && ( +
+ 📝 {selectedEntry.note} +
+ )} + + {selectedEntry.diff?.isEmpty ? ( +
+ No changes detected in Diff +
+ ) : ( +
+ {/* Added Keys */} + {selectedEntry.diff?.added?.length > 0 && ( +
+

+ ++ Added Properties +

+
+                        {JSON.stringify(
+                          selectedEntry.diff.added.reduce(
+                            (acc, key) => ({ ...acc, [key]: selectedEntry.after?.[key] }),
+                            {}
+                          ),
+                          null,
+                          2
+                        )}
+                      
+
+ )} + + {/* Removed Keys */} + {selectedEntry.diff?.removed?.length > 0 && ( +
+

+ -- Removed Properties +

+
+                        {JSON.stringify(
+                          selectedEntry.diff.removed.reduce(
+                            (acc, key) => ({ ...acc, [key]: selectedEntry.before?.[key] }),
+                            {}
+                          ),
+                          null,
+                          2
+                        )}
+                      
+
+ )} + + {/* Changed Keys */} + {selectedEntry.diff?.changed?.length > 0 && ( +
+

+ ~ Modified Properties +

+
+ {selectedEntry.diff.changed.map((change, idx) => ( +
+
+ {change.key} +
+
+
+
+ Before +
+
+                                  {JSON.stringify(change.from, null, 2)}
+                                
+
+
+
+ After +
+
+                                  {JSON.stringify(change.to, null, 2)}
+                                
+
+
+
+ ))} +
+
+ )} +
+ )} +
+
+
+ )} +
+ ); +} diff --git a/src/app/(dashboard)/dashboard/audit/page.tsx b/src/app/(dashboard)/dashboard/audit/page.tsx new file mode 100644 index 0000000000..1c52420069 --- /dev/null +++ b/src/app/(dashboard)/dashboard/audit/page.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { useState, useEffect } from "react"; +import ConfigAuditViewer from "./ConfigAuditViewer"; + +export default function ConfigAuditPage() { + const [summary, setSummary] = useState(null); + + useEffect(() => { + fetch("/api/audit?summary=true") + .then((res) => res.json()) + .then((data) => setSummary(data)) + .catch((err) => console.error(err)); + }, []); + + return ( +
+
+
+

+ Configuration Audit +

+

+ Track and diff changes made to routing policies, combos, and connections. +

+
+ {summary && ( +
+
+ Total Audits + + {summary.totalEntries} + +
+
+ )} +
+ +
+ +
+
+ ); +} diff --git a/src/app/(dashboard)/dashboard/health/page.tsx b/src/app/(dashboard)/dashboard/health/page.tsx index bf25185f62..804d0c9214 100644 --- a/src/app/(dashboard)/dashboard/health/page.tsx +++ b/src/app/(dashboard)/dashboard/health/page.tsx @@ -48,6 +48,7 @@ export default function HealthPage() { const [telemetry, setTelemetry] = useState(null); const [cache, setCache] = useState(null); const [signatureCache, setSignatureCache] = useState(null); + const [degradation, setDegradation] = useState(null); const [resetting, setResetting] = useState(false); const fetchHealth = useCallback(async () => { @@ -69,12 +70,14 @@ export default function HealthPage() { fetch("/api/telemetry/summary").then((r) => r.json()), fetch("/api/cache/stats").then((r) => r.json()), fetch("/api/rate-limits").then((r) => r.json()), + fetch("/api/health/degradation").then((r) => r.json()), ]); if (results[0].status === "fulfilled") setTelemetry(results[0].value); if (results[1].status === "fulfilled") setCache(results[1].value); if (results[2].status === "fulfilled" && results[2].value.cacheStats) { setSignatureCache(results[2].value.cacheStats); } + if (results[3].status === "fulfilled") setDegradation(results[3].value); }, []); useEffect(() => { @@ -270,6 +273,80 @@ export default function HealthPage() { + {/* Graceful Degradation Status */} + {degradation && degradation.features && degradation.features.length > 0 && ( + +
+

+ healing + Graceful Degradation Status +

+
+ + Full: {degradation.summary.full} + + + Reduced: {degradation.summary.reduced} + + + Minimal: {degradation.summary.minimal} + + + Default: {degradation.summary.default} + +
+
+
+ {degradation.features.map((feat: any) => { + const bg = + feat.level === "full" + ? "bg-green-500/5 border-green-500/10" + : feat.level === "reduced" + ? "bg-amber-500/5 border-amber-500/20" + : feat.level === "minimal" + ? "bg-orange-500/5 border-orange-500/20" + : "bg-red-500/5 border-red-500/20"; + const dot = + feat.level === "full" + ? "bg-green-500" + : feat.level === "reduced" + ? "bg-amber-500" + : feat.level === "minimal" + ? "bg-orange-500" + : "bg-red-500"; + return ( +
+
+ + + {feat.feature} + + + {feat.level} + +
+
{feat.capability}
+ {feat.reason && ( +
+ {feat.reason.length > 80 ? feat.reason.substring(0, 80) + "..." : feat.reason} +
+ )} +
+ Since {new Date(feat.since).toLocaleTimeString()} +
+
+ ); + })} +
+
+ )} + {/* Telemetry Cards — Latency & Prompt Cache */}
{/* Latency Card */} diff --git a/src/app/(dashboard)/dashboard/providers/page.tsx b/src/app/(dashboard)/dashboard/providers/page.tsx index ee53158198..7ea88f92ef 100644 --- a/src/app/(dashboard)/dashboard/providers/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/page.tsx @@ -95,6 +95,7 @@ function getConnectionErrorTag(connection) { export default function ProvidersPage() { const [connections, setConnections] = useState([]); const [providerNodes, setProviderNodes] = useState([]); + const [expirations, setExpirations] = useState(null); const [loading, setLoading] = useState(true); const [showAddCompatibleModal, setShowAddCompatibleModal] = useState(false); const [showAddAnthropicCompatibleModal, setShowAddAnthropicCompatibleModal] = useState(false); @@ -108,14 +109,17 @@ export default function ProvidersPage() { useEffect(() => { const fetchData = async () => { try { - const [connectionsRes, nodesRes] = await Promise.all([ + const [connectionsRes, nodesRes, expirationsRes] = await Promise.all([ fetch("/api/providers"), fetch("/api/provider-nodes"), + fetch("/api/providers/expiration"), ]); const connectionsData = await connectionsRes.json(); const nodesData = await nodesRes.json(); + const expirationsData = await expirationsRes.json(); if (connectionsRes.ok) setConnections(connectionsData.connections || []); if (nodesRes.ok) setProviderNodes(nodesData.nodes || []); + if (expirationsRes.ok && expirationsData) setExpirations(expirationsData); } catch (error) { console.log("Error fetching data:", error); } finally { @@ -188,7 +192,16 @@ export default function ProvidersPage() { const errorCode = latestError ? getConnectionErrorTag(latestError) : null; const errorTime = latestError?.lastErrorAt ? getRelativeTime(latestError.lastErrorAt) : null; - return { connected, error, total, errorCode, errorTime, allDisabled }; + // Check expirations + const providerExpirations = + expirations?.list?.filter((e: any) => e.provider === providerId) || []; + const hasExpired = providerExpirations.some((e: any) => e.status === "expired"); + const hasExpiringSoon = providerExpirations.some((e: any) => e.status === "expiring_soon"); + let expiryStatus = null; + if (hasExpired) expiryStatus = "expired"; + else if (hasExpiringSoon) expiryStatus = "expiring_soon"; + + return { connected, error, total, errorCode, errorTime, allDisabled, expiryStatus }; }; // Toggle all connections for a provider on/off @@ -289,6 +302,40 @@ export default function ProvidersPage() { return (
+ {/* Expiration Banner */} + {expirations?.summary && + (expirations.summary.expired > 0 || expirations.summary.expiringSoon > 0) && ( +
0 + ? "bg-red-500/10 border-red-500/20" + : "bg-amber-500/10 border-amber-500/20" + }`} + > + 0 ? "text-red-500" : "text-amber-500" + }`} + > + {expirations.summary.expired > 0 ? "error" : "warning"} + +
+

0 ? "text-red-500" : "text-amber-500"}`} + > + {expirations.summary.expired > 0 + ? `${expirations.summary.expired} Provider connection(s) expired` + : `${expirations.summary.expiringSoon} Provider connection(s) expiring soon`} +

+

+ {expirations.summary.expired > 0 + ? "Immediate action required. Expired connections will permanently fail." + : "Please review and renew expiring connections to avoid disruption."} +

+
+
+ )} + {/* OAuth Providers */}
@@ -582,6 +629,16 @@ function ProviderCard({ providerId, provider, stats, authType, onToggle }) { ) : ( <> {getStatusDisplay(connected, error, errorCode, t)} + {stats.expiryStatus === "expired" && ( + + Expired + + )} + {stats.expiryStatus === "expiring_soon" && ( + + Expiring Soon + + )} {errorTime && • {errorTime}} )} @@ -709,6 +766,16 @@ function ApiKeyProviderCard({ providerId, provider, stats, authType, onToggle }) ) : ( <> {getStatusDisplay(connected, error, errorCode, t)} + {stats.expiryStatus === "expired" && ( + + Expired + + )} + {stats.expiryStatus === "expiring_soon" && ( + + Expiring Soon + + )} {isCompatible && ( {provider.apiType === "responses" ? t("responses") : t("chat")} diff --git a/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx b/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx index 697a69eb06..774552da93 100644 --- a/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx @@ -152,6 +152,40 @@ export default function RoutingTab() {

+ {/* Adaptive Volume Routing */} + +
+
+
+ +
+
+

+ {t("adaptiveVolumeRouting") || "Adaptive Volume Routing"} +

+

+ {t("adaptiveVolumeRoutingDesc") || + "Automatically adjusts traffic volume between providers based on real-time latency and error rates."} +

+
+
+
+ +
+
+
+ {/* Wildcard Aliases */}
diff --git a/src/app/api/audit/route.ts b/src/app/api/audit/route.ts new file mode 100644 index 0000000000..5819553ffe --- /dev/null +++ b/src/app/api/audit/route.ts @@ -0,0 +1,38 @@ +import { NextResponse } from "next/server"; +import { + getAuditLog, + getAuditSummary, + AuditTarget, + AuditAction, + AuditSource, +} from "@/domain/configAudit"; + +export async function GET(req: Request) { + try { + const url = new URL(req.url); + const summary = url.searchParams.get("summary"); + + if (summary === "true") { + return NextResponse.json(getAuditSummary()); + } + + const limit = parseInt(url.searchParams.get("limit") || "50", 10); + const offset = parseInt(url.searchParams.get("offset") || "0", 10); + const target = url.searchParams.get("target") as AuditTarget | null; + const action = url.searchParams.get("action") as AuditAction | null; + const source = url.searchParams.get("source") as AuditSource | null; + const since = url.searchParams.get("since"); + + const options: any = { limit, offset }; + if (target) options.target = target; + if (action) options.action = action; + if (source) options.source = source; + if (since) options.since = since; + + const result = getAuditLog(options); + return NextResponse.json(result); + } catch (error) { + console.error("[API ERROR] /api/audit GET:", error); + return NextResponse.json({ error: "Failed to fetch audit log." }, { status: 500 }); + } +} diff --git a/src/app/api/health/degradation/route.ts b/src/app/api/health/degradation/route.ts new file mode 100644 index 0000000000..1a9bf0d0c7 --- /dev/null +++ b/src/app/api/health/degradation/route.ts @@ -0,0 +1,30 @@ +import { NextResponse } from "next/server"; +import { + getDegradationReport, + getDegradationSummary, + hasAnyDegradation, +} from "@/domain/degradation"; + +export async function GET(req: Request) { + try { + const url = new URL(req.url); + const summaryStr = url.searchParams.get("summary"); + + if (summaryStr === "true") { + return NextResponse.json({ + summary: getDegradationSummary(), + isDegraded: hasAnyDegradation(), + }); + } + + const report = getDegradationReport(); + return NextResponse.json({ + active: hasAnyDegradation(), + summary: getDegradationSummary(), + features: report, + }); + } catch (error) { + console.error("[API ERROR] /api/health/degradation GET:", error); + return NextResponse.json({ error: "Failed to fetch degradation report." }, { status: 500 }); + } +} diff --git a/src/app/api/providers/expiration/route.ts b/src/app/api/providers/expiration/route.ts new file mode 100644 index 0000000000..c9d4f24f5d --- /dev/null +++ b/src/app/api/providers/expiration/route.ts @@ -0,0 +1,17 @@ +import { NextResponse } from "next/server"; +import { getAllExpirations, getExpirationSummary } from "@/domain/providerExpiration"; + +export async function GET() { + try { + const list = getAllExpirations(); + const summary = getExpirationSummary(); + + return NextResponse.json({ + summary, + list, + }); + } catch (error) { + console.error("[API ERROR] /api/providers/expiration GET:", error); + return NextResponse.json({ error: "Failed to fetch expiration metadata." }, { status: 500 }); + } +} diff --git a/src/shared/components/DegradationBadge.tsx b/src/shared/components/DegradationBadge.tsx new file mode 100644 index 0000000000..031cc92e1c --- /dev/null +++ b/src/shared/components/DegradationBadge.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { useState, useEffect } from "react"; +import Link from "next/link"; +import { useTranslations } from "next-intl"; + +export default function DegradationBadge() { + const [isDegraded, setDegraded] = useState(false); + const t = useTranslations("common"); // Or a specific namespace if needed + + useEffect(() => { + const checkDegradation = async () => { + try { + const res = await fetch("/api/health/degradation?summary=true"); + if (res.ok) { + const data = await res.json(); + setDegraded(data.isDegraded); + } + } catch (err) { + // Ignore error + } + }; + + checkDegradation(); + const interval = setInterval(checkDegradation, 60000); + return () => clearInterval(interval); + }, []); + + if (!isDegraded) return null; + + return ( + + healing + Degraded + + ); +} diff --git a/src/shared/components/Header.tsx b/src/shared/components/Header.tsx index aaf2223148..9ffa39bbd6 100644 --- a/src/shared/components/Header.tsx +++ b/src/shared/components/Header.tsx @@ -6,6 +6,7 @@ import Image from "next/image"; import PropTypes from "prop-types"; import ThemeToggle from "./ThemeToggle"; import TokenHealthBadge from "./TokenHealthBadge"; +import DegradationBadge from "./DegradationBadge"; import LanguageSelector from "./LanguageSelector"; import ProviderIcon from "./ProviderIcon"; import { useTranslations } from "next-intl"; @@ -199,7 +200,8 @@ export default function Header({ onMenuClick, showMenuButton = true }) { {/* Theme toggle */} - {/* Token health */} + {/* Degradation & Token health */} + {/* Logout button */} diff --git a/src/shared/components/UsageAnalytics.tsx b/src/shared/components/UsageAnalytics.tsx index 34d55b825f..96f0103745 100644 --- a/src/shared/components/UsageAnalytics.tsx +++ b/src/shared/components/UsageAnalytics.tsx @@ -78,6 +78,26 @@ export default function UsageAnalytics() { return (analytics?.byProvider || []).length; }, [analytics]); + const providerDiversity = useMemo(() => { + const providers = analytics?.byProvider || []; + if (providers.length <= 1) return 0; + + let totalCalls = 0; + for (const p of providers) { + totalCalls += p.totalRequests || p.apiCalls || 0; + } + if (totalCalls === 0) return 0; + + let h = 0; + for (const p of providers) { + const p_i = (p.totalRequests || p.apiCalls || 0) / totalCalls; + if (p_i > 0) h -= p_i * Math.log2(p_i); + } + + const maxH = Math.log2(providers.length); + return maxH > 0 ? (h / maxH) * 100 : 0; + }, [analytics]); + if (loading && !analytics) return ; if (error) return Error: {error}; @@ -176,9 +196,9 @@ export default function UsageAnalytics() {
diff --git a/src/shared/constants/sidebarVisibility.ts b/src/shared/constants/sidebarVisibility.ts index fa73e228d7..b3dd15904f 100644 --- a/src/shared/constants/sidebarVisibility.ts +++ b/src/shared/constants/sidebarVisibility.ts @@ -16,6 +16,7 @@ export const HIDEABLE_SIDEBAR_ITEM_IDS = [ "search-tools", "health", "logs", + "audit", "settings", "docs", "issues", @@ -74,6 +75,7 @@ const DEBUG_SIDEBAR_ITEMS: readonly SidebarItemDefinition[] = [ const SYSTEM_SIDEBAR_ITEMS: readonly SidebarItemDefinition[] = [ { id: "health", href: "/dashboard/health", i18nKey: "health", icon: "health_and_safety" }, { id: "logs", href: "/dashboard/logs", i18nKey: "logs", icon: "description" }, + { id: "audit", href: "/dashboard/audit", i18nKey: "auditLog", icon: "history" }, { id: "settings", href: "/dashboard/settings", i18nKey: "settings", icon: "settings" }, ]; diff --git a/src/shared/validation/settingsSchemas.ts b/src/shared/validation/settingsSchemas.ts index 4517b33d5a..9d56f2b394 100644 --- a/src/shared/validation/settingsSchemas.ts +++ b/src/shared/validation/settingsSchemas.ts @@ -50,6 +50,8 @@ export const updateSettingsSchema = z.object({ stripModelPrefix: z.boolean().optional(), // Cache control preservation mode alwaysPreserveClientCache: z.enum(["auto", "always", "never"]).optional(), + // Adaptive Volume Routing + adaptiveVolumeRouting: z.boolean().optional(), // Custom CLI agent definitions for ACP customAgents: z .array( From 11dfdbb7a3afa580cb0071ee2d6bffa15a46f5d7 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Mon, 30 Mar 2026 16:37:49 -0300 Subject: [PATCH 02/18] feat(analytics): add diversity score card UI and diversity API route Implement DiversityScoreCard component to fetch and display provider diversity score with loading state and conditional styling, integrate it into AnalyticsPage overview, and add a new API route at src/app/api/analytics/diversity/route.ts to return the diversity report using getDiversityReport --- .../components/DiversityScoreCard.tsx | 136 ++++++++++++++++++ .../(dashboard)/dashboard/analytics/page.tsx | 12 +- src/app/api/analytics/diversity/route.ts | 13 ++ 3 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 src/app/(dashboard)/dashboard/analytics/components/DiversityScoreCard.tsx create mode 100644 src/app/api/analytics/diversity/route.ts diff --git a/src/app/(dashboard)/dashboard/analytics/components/DiversityScoreCard.tsx b/src/app/(dashboard)/dashboard/analytics/components/DiversityScoreCard.tsx new file mode 100644 index 0000000000..de4aa724c6 --- /dev/null +++ b/src/app/(dashboard)/dashboard/analytics/components/DiversityScoreCard.tsx @@ -0,0 +1,136 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Card } from "@/shared/components"; +import { useTranslations } from "next-intl"; + +export default function DiversityScoreCard() { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const t = useTranslations("analytics"); + + useEffect(() => { + fetch("/api/analytics/diversity") + .then((res) => res.json()) + .then((json) => { + setData(json); + setLoading(false); + }) + .catch((err) => { + console.error(err); + setLoading(false); + }); + }, []); + + if (loading || !data) { + return ( + +
+
+ ); + } + + const scorePercentage = Math.round((data.score || 0) * 100); + + let riskColor = "text-green-500"; + let gaugeColor = "bg-green-500"; + let riskLabel = "Healthy Distribution"; + + if (scorePercentage < 40) { + riskColor = "text-red-500"; + gaugeColor = "bg-red-500"; + riskLabel = "High Vendor Lock-in Risk"; + } else if (scorePercentage < 70) { + riskColor = "text-amber-500"; + gaugeColor = "bg-amber-500"; + riskLabel = "Moderate Distribution"; + } + + return ( + +
+ pie_chart +

+ Provider Diversity Score +

+ + Shannon Entropy + +
+ +
+
+ + {scorePercentage}% + + {riskLabel} +
+ + {/* Simple CSS Donut */} +
+ + + + +
+
+ +
+

+ Provider Share +

+ + {Object.keys(data.providers || {}).length === 0 ? ( +
+ No recent usage data available. +
+ ) : ( +
+ {Object.entries(data.providers) + .sort(([, a]: any, [, b]: any) => b.share - a.share) + .slice(0, 4) // Top 4 providers + .map(([provider, stat]: [string, any]) => ( +
+
+ + {provider} + + + {Math.round(stat.share * 100)}% + +
+
+
+
+
+ ))} +
+ )} +
+ +
+ Window: {data.windowSize} reqs + Based on Last {Math.round(data.ttlMs / 60000)} mins +
+ + ); +} diff --git a/src/app/(dashboard)/dashboard/analytics/page.tsx b/src/app/(dashboard)/dashboard/analytics/page.tsx index 3f97518466..87aa0f69e6 100644 --- a/src/app/(dashboard)/dashboard/analytics/page.tsx +++ b/src/app/(dashboard)/dashboard/analytics/page.tsx @@ -4,6 +4,7 @@ import { useState, Suspense } from "react"; import { UsageAnalytics, CardSkeleton, SegmentedControl } from "@/shared/components"; import EvalsTab from "../usage/components/EvalsTab"; import SearchAnalyticsTab from "./SearchAnalyticsTab"; +import DiversityScoreCard from "./components/DiversityScoreCard"; import { useTranslations } from "next-intl"; export default function AnalyticsPage() { @@ -38,9 +39,14 @@ export default function AnalyticsPage() { /> {activeTab === "overview" && ( - }> - - +
+
+ +
+ }> + + +
)} {activeTab === "evals" && } {activeTab === "search" && } diff --git a/src/app/api/analytics/diversity/route.ts b/src/app/api/analytics/diversity/route.ts new file mode 100644 index 0000000000..6e00929eec --- /dev/null +++ b/src/app/api/analytics/diversity/route.ts @@ -0,0 +1,13 @@ +import { NextResponse } from "next/server"; +import { getDiversityReport } from "../../../../../open-sse/services/autoCombo/providerDiversity"; + +export const dynamic = "force-dynamic"; + +export async function GET() { + try { + const report = getDiversityReport(); + return NextResponse.json(report); + } catch (error: any) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} From 03a860dd6f907d58d0fa537fa58c93c73a07a2e6 Mon Sep 17 00:00:00 2001 From: "R.D." Date: Mon, 30 Mar 2026 16:23:53 -0400 Subject: [PATCH 03/18] Fix combo smoke tests for reasoning responses --- src/lib/combos/testHealth.ts | 99 ++++++++++++++++++++++++++- tests/unit/combo-test-health.test.mjs | 59 +++++++++++++++- tests/unit/combo-test-route.test.mjs | 41 +++++++++++ 3 files changed, 195 insertions(+), 4 deletions(-) diff --git a/src/lib/combos/testHealth.ts b/src/lib/combos/testHealth.ts index 15959e6913..f7ac5f66cf 100644 --- a/src/lib/combos/testHealth.ts +++ b/src/lib/combos/testHealth.ts @@ -4,6 +4,10 @@ function asRecord(value: unknown): JsonRecord { return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : {}; } +function joinNonEmpty(parts: string[]) { + return parts.filter(Boolean).join("\n").trim(); +} + function extractTextFromContent(content: unknown): string { if (typeof content === "string") return content.trim(); @@ -28,12 +32,85 @@ function extractTextFromContent(content: unknown): string { .trim(); } +function extractReasoningText(record: JsonRecord): string { + const reasoningDetails = Array.isArray(record.reasoning_details) ? record.reasoning_details : []; + const detailText = reasoningDetails + .map((detail) => { + const detailRecord = asRecord(detail); + const detailType = typeof detailRecord.type === "string" ? detailRecord.type : ""; + const text = + typeof detailRecord.text === "string" + ? detailRecord.text.trim() + : typeof detailRecord.content === "string" + ? detailRecord.content.trim() + : ""; + + if ( + text && + (detailType === "" || + detailType === "reasoning" || + detailType === "reasoning.text" || + detailType === "thinking") + ) { + return text; + } + + return ""; + }) + .filter(Boolean); + + return joinNonEmpty([ + typeof record.reasoning_content === "string" ? record.reasoning_content.trim() : "", + typeof record.reasoning === "string" ? record.reasoning.trim() : "", + typeof record.reasoning_text === "string" ? record.reasoning_text.trim() : "", + joinNonEmpty(detailText), + ]); +} + +function getUsageReasoningTokens(body: JsonRecord): number { + const usage = asRecord(body.usage); + if (!usage) return 0; + + const completionDetails = asRecord(usage.completion_tokens_details); + const topLevelReasoning = + typeof usage.reasoning_tokens === "number" && Number.isFinite(usage.reasoning_tokens) + ? usage.reasoning_tokens + : 0; + const detailedReasoning = + typeof completionDetails.reasoning_tokens === "number" && + Number.isFinite(completionDetails.reasoning_tokens) + ? completionDetails.reasoning_tokens + : 0; + + return Math.max(topLevelReasoning, detailedReasoning); +} + +function hasReasoningOnlyCompletion(body: JsonRecord): boolean { + if (!Array.isArray(body.choices) || body.choices.length === 0) return false; + if (getUsageReasoningTokens(body) <= 0) return false; + + return body.choices.some((choice) => { + const choiceRecord = asRecord(choice); + const message = asRecord(choiceRecord.message); + const finishReason = + typeof choiceRecord.finish_reason === "string" ? choiceRecord.finish_reason : ""; + + if (!message || message.role !== "assistant") return false; + if (!finishReason) return false; + if (extractTextFromContent(message.content)) return false; + if (extractReasoningText(message)) return false; + return true; + }); +} + export function buildComboTestRequestBody(modelStr: string) { return { model: modelStr, messages: [{ role: "user", content: "Reply with OK only." }], - // Keep this close to a real client request without inflating cost. - max_tokens: 16, + // Give reasoning-heavy models enough headroom to emit a tiny visible answer + // without turning the smoke test into a full-cost real request. + max_tokens: 64, + temperature: 0, stream: false, }; } @@ -52,6 +129,9 @@ export function extractComboTestResponseText(responseBody: unknown): string { const messageText = extractTextFromContent(message.content); if (messageText) return messageText; + const reasoningText = extractReasoningText(message); + if (reasoningText) return reasoningText; + if (typeof choiceRecord.text === "string" && choiceRecord.text.trim()) { return choiceRecord.text.trim(); } @@ -63,8 +143,21 @@ export function extractComboTestResponseText(responseBody: unknown): string { const itemRecord = asRecord(item); const contentText = extractTextFromContent(itemRecord.content); if (contentText) return contentText; + + const reasoningText = extractReasoningText(itemRecord); + if (reasoningText) return reasoningText; } } - return extractTextFromContent(body.content); + const topLevelText = extractTextFromContent(body.content); + if (topLevelText) return topLevelText; + + const topLevelReasoning = extractReasoningText(body); + if (topLevelReasoning) return topLevelReasoning; + + if (hasReasoningOnlyCompletion(body)) { + return "[reasoning-only completion]"; + } + + return ""; } diff --git a/tests/unit/combo-test-health.test.mjs b/tests/unit/combo-test-health.test.mjs index 2471656d46..19de579271 100644 --- a/tests/unit/combo-test-health.test.mjs +++ b/tests/unit/combo-test-health.test.mjs @@ -9,7 +9,8 @@ test("combo test helper builds a realistic smoke payload", () => { assert.equal(body.model, "openrouter/openai/gpt-5.4"); assert.equal(body.messages[0].content, "Reply with OK only."); - assert.equal(body.max_tokens, 16); + assert.equal(body.max_tokens, 64); + assert.equal(body.temperature, 0); assert.equal(body.stream, false); }); @@ -46,6 +47,62 @@ test("combo test helper extracts text from block-based responses", () => { assert.equal(text, "OK\nConfirmed."); }); +test("combo test helper extracts reasoning content when visible text is absent", () => { + const text = extractComboTestResponseText({ + choices: [ + { + message: { + role: "assistant", + content: null, + reasoning_content: "Working through the request.\nOK", + }, + }, + ], + }); + + assert.equal(text, "Working through the request.\nOK"); +}); + +test("combo test helper extracts reasoning_text aliases from GitHub-style responses", () => { + const text = extractComboTestResponseText({ + choices: [ + { + message: { + role: "assistant", + content: "", + reasoning_text: "Reasoning trace", + }, + }, + ], + }); + + assert.equal(text, "Reasoning trace"); +}); + +test("combo test helper treats reasoning-only completions as a healthy signal", () => { + const text = extractComboTestResponseText({ + choices: [ + { + finish_reason: "length", + message: { + role: "assistant", + content: "", + }, + }, + ], + usage: { + prompt_tokens: 6, + completion_tokens: 12, + total_tokens: 18, + completion_tokens_details: { + reasoning_tokens: 12, + }, + }, + }); + + assert.equal(text, "[reasoning-only completion]"); +}); + test("combo test helper returns empty string when no text content exists", () => { const text = extractComboTestResponseText({ choices: [ diff --git a/tests/unit/combo-test-route.test.mjs b/tests/unit/combo-test-route.test.mjs index 2cadf2ec1c..3f09834152 100644 --- a/tests/unit/combo-test-route.test.mjs +++ b/tests/unit/combo-test-route.test.mjs @@ -86,6 +86,8 @@ test("combo test route marks a model healthy only when it returns assistant text assert.match(fetchCalls[0].init.headers["X-Request-Id"], /^combo-test-/); assert.equal(forwardedBody.model, "openrouter/openai/gpt-5.4"); assert.equal(forwardedBody.messages[0].content, "Reply with OK only."); + assert.equal(forwardedBody.max_tokens, 64); + assert.equal(forwardedBody.temperature, 0); assert.equal(body.resolvedBy, "openrouter/openai/gpt-5.4"); assert.equal(body.results[0].status, "ok"); assert.equal(body.results[0].responseText, "OK"); @@ -122,6 +124,45 @@ test("combo test route treats empty successful responses as failures", async () assert.match(body.results[0].error, /no text content/i); }); +test("combo test route accepts reasoning-only completions as healthy smoke-test responses", async () => { + await createTestCombo(); + + globalThis.fetch = async () => + new Response( + JSON.stringify({ + choices: [ + { + finish_reason: "length", + message: { + role: "assistant", + content: "", + }, + }, + ], + usage: { + prompt_tokens: 6, + completion_tokens: 12, + total_tokens: 18, + completion_tokens_details: { + reasoning_tokens: 12, + }, + }, + }), + { + status: 200, + headers: { "content-type": "application/json" }, + } + ); + + const response = await route.POST(makeRequest()); + const body = await response.json(); + + assert.equal(response.status, 200); + assert.equal(body.resolvedBy, "openrouter/openai/gpt-5.4"); + assert.equal(body.results[0].status, "ok"); + assert.equal(body.results[0].responseText, "[reasoning-only completion]"); +}); + test("combo test route surfaces provider errors instead of downgrading them to reachability", async () => { await createTestCombo(); From b492c5ac1afc315dd9358c9bced75cad5c050516 Mon Sep 17 00:00:00 2001 From: "R.D." Date: Mon, 30 Mar 2026 16:31:07 -0400 Subject: [PATCH 04/18] Fix cloudflared startup TLS handling --- Dockerfile | 2 +- README.md | 1 + src/lib/cloudflaredTunnel.ts | 101 +++++++++++++++++++++++--- tests/unit/cloudflaredTunnel.test.mjs | 84 +++++++++++++++++---- 4 files changed, 159 insertions(+), 29 deletions(-) diff --git a/Dockerfile b/Dockerfile index f7ad521d4b..cb0868cf8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM node:22-bookworm-slim AS builder WORKDIR /app RUN apt-get update \ - && apt-get install -y --no-install-recommends libsecret-1-0 \ + && apt-get install -y --no-install-recommends libsecret-1-0 ca-certificates \ && rm -rf /var/lib/apt/lists/* COPY package*.json ./ diff --git a/README.md b/README.md index 3b2fe3a90d..86d15526b5 100644 --- a/README.md +++ b/README.md @@ -882,6 +882,7 @@ Notes: - Quick Tunnel URLs are temporary and change after every restart. - Managed install currently supports Linux, macOS, and Windows on `x64` / `arm64`. +- Docker images bundle system CA roots and pass them to managed `cloudflared`, which avoids TLS trust failures when the tunnel bootstraps inside the container. - Set `CLOUDFLARED_BIN=/absolute/path/to/cloudflared` if you want OmniRoute to use an existing binary instead of downloading one. **Using Docker Compose with Caddy (HTTPS Auto-TLS):** diff --git a/src/lib/cloudflaredTunnel.ts b/src/lib/cloudflaredTunnel.ts index afb582a3bb..e3b457501e 100644 --- a/src/lib/cloudflaredTunnel.ts +++ b/src/lib/cloudflaredTunnel.ts @@ -13,6 +13,18 @@ const CLOUDFLARED_RELEASE_BASE = "https://github.com/cloudflare/cloudflared/releases/latest/download"; const START_TIMEOUT_MS = 30000; const STOP_TIMEOUT_MS = 5000; +const GENERIC_EXIT_ERROR_PREFIX = "cloudflared exited"; +const DEFAULT_CERT_FILE_CANDIDATES = [ + "/etc/ssl/certs/ca-certificates.crt", + "/etc/pki/tls/certs/ca-bundle.crt", + "/etc/ssl/cert.pem", + "/private/etc/ssl/cert.pem", +] as const; +const DEFAULT_CERT_DIR_CANDIDATES = [ + "/etc/ssl/certs", + "/etc/pki/tls/certs", + "/system/etc/security/cacerts", +] as const; type CloudflaredInstallSource = "managed" | "path" | "env"; type TunnelPhase = "unsupported" | "not_installed" | "stopped" | "starting" | "running" | "error"; @@ -238,9 +250,55 @@ export function extractTryCloudflareUrl(text: string) { return match ? match[0] : null; } +function normalizeCloudflaredLogLine(line: string) { + return line + .trim() + .replace(/^\d{4}-\d{2}-\d{2}T\S+\s+(?:INF|WRN|ERR)\s+/i, "") + .trim(); +} + +export function extractCloudflaredErrorMessage(text: string) { + const lines = String(text || "") + .split(/\r?\n/) + .map(normalizeCloudflaredLogLine) + .filter(Boolean); + + for (let i = lines.length - 1; i >= 0; i--) { + if (/(?:\berror\b|\bfailed\b|\btls:\b|\bx509\b|\bcertificate\b)/i.test(lines[i])) { + return lines[i]; + } + } + + return null; +} + +function isSpecificCloudflaredError(error: string | null | undefined) { + return !!error && !error.startsWith(GENERIC_EXIT_ERROR_PREFIX); +} + +function getGenericExitError(code: number | null, signal: NodeJS.Signals | null) { + return `cloudflared exited unexpectedly (${code ?? "signal"}${signal ? `/${signal}` : ""})`; +} + +export function getDefaultCloudflaredCertEnv( + existsSync: (candidate: string) => boolean = fsSync.existsSync, + certFileCandidates: readonly string[] = DEFAULT_CERT_FILE_CANDIDATES, + certDirCandidates: readonly string[] = DEFAULT_CERT_DIR_CANDIDATES +) { + const certEnv: NodeJS.ProcessEnv = {}; + const certFile = certFileCandidates.find((candidate) => existsSync(candidate)); + const certDir = certDirCandidates.find((candidate) => existsSync(candidate)); + + if (certFile) certEnv.SSL_CERT_FILE = certFile; + if (certDir) certEnv.SSL_CERT_DIR = certDir; + + return certEnv; +} + export function buildCloudflaredChildEnv( sourceEnv: NodeJS.ProcessEnv = process.env, - runtimeDirs: CloudflaredRuntimeDirs = getCloudflaredRuntimeDirs() + runtimeDirs: CloudflaredRuntimeDirs = getCloudflaredRuntimeDirs(), + defaultCertEnv: NodeJS.ProcessEnv = getDefaultCloudflaredCertEnv() ): NodeJS.ProcessEnv { const childEnv: NodeJS.ProcessEnv = {}; @@ -262,6 +320,12 @@ export function buildCloudflaredChildEnv( if (!childEnv.TMPDIR) childEnv.TMPDIR = runtimeDirs.tempDir; if (!childEnv.TMP) childEnv.TMP = runtimeDirs.tempDir; if (!childEnv.TEMP) childEnv.TEMP = runtimeDirs.tempDir; + if (!childEnv.SSL_CERT_FILE && defaultCertEnv.SSL_CERT_FILE) { + childEnv.SSL_CERT_FILE = defaultCertEnv.SSL_CERT_FILE; + } + if (!childEnv.SSL_CERT_DIR && defaultCertEnv.SSL_CERT_DIR) { + childEnv.SSL_CERT_DIR = defaultCertEnv.SSL_CERT_DIR; + } return childEnv; } @@ -447,7 +511,9 @@ async function finalizeProcessExit(code: number | null, signal: NodeJS.Signals | const lastError = code === 0 || signal === "SIGTERM" || signal === "SIGINT" ? null - : `cloudflared exited unexpectedly (${code ?? "signal"}${signal ? `/${signal}` : ""})`; + : isSpecificCloudflaredError(currentState.lastError) + ? currentState.lastError + : getGenericExitError(code, signal); tunnelProcess = null; tunnelPid = null; @@ -562,14 +628,10 @@ export async function startCloudflaredTunnel(): Promise startedAt: new Date().toISOString(), }); - const child = spawn( - binary.binaryPath as string, - getCloudflaredStartArgs(targetUrl), - { - stdio: ["ignore", "pipe", "pipe"], - env: buildCloudflaredChildEnv(), - } - ); + const child = spawn(binary.binaryPath as string, getCloudflaredStartArgs(targetUrl), { + stdio: ["ignore", "pipe", "pipe"], + env: buildCloudflaredChildEnv(), + }); tunnelProcess = child; tunnelPid = child.pid ?? null; @@ -597,6 +659,14 @@ export async function startCloudflaredTunnel(): Promise if (!text) return; await appendTunnelLog(source, text); + const errorMessage = source === "stderr" ? extractCloudflaredErrorMessage(text) : null; + if (errorMessage) { + await updateStateFile({ + pid: child.pid, + status: "error", + lastError: errorMessage, + }); + } const url = extractTryCloudflareUrl(text); if (!url) return; @@ -643,11 +713,18 @@ export async function startCloudflaredTunnel(): Promise try { return await startPromise; } catch (error) { + const currentState = await readStateFile(); + const message = isSpecificCloudflaredError(currentState.lastError) + ? currentState.lastError + : error instanceof Error + ? error.message + : "Failed to start cloudflared tunnel"; + await updateStateFile({ status: "error", - lastError: error instanceof Error ? error.message : "Failed to start cloudflared tunnel", + lastError: message, }); - throw error; + throw new Error(message); } finally { startPromise = null; } diff --git a/tests/unit/cloudflaredTunnel.test.mjs b/tests/unit/cloudflaredTunnel.test.mjs index 30650f23a8..62a51c4096 100644 --- a/tests/unit/cloudflaredTunnel.test.mjs +++ b/tests/unit/cloudflaredTunnel.test.mjs @@ -3,7 +3,9 @@ import assert from "node:assert/strict"; import { buildCloudflaredChildEnv, + extractCloudflaredErrorMessage, extractTryCloudflareUrl, + getDefaultCloudflaredCertEnv, getCloudflaredStartArgs, getCloudflaredAssetSpec, } from "../../src/lib/cloudflaredTunnel.ts"; @@ -20,6 +22,17 @@ test("extractTryCloudflareUrl returns null when no tunnel URL is present", () => assert.equal(extractTryCloudflareUrl("cloudflared starting without assigned URL"), null); }); +test("extractCloudflaredErrorMessage keeps the actionable stderr line", () => { + const error = extractCloudflaredErrorMessage( + '2026-03-30T19:56:12Z INF Requesting new quick Tunnel on trycloudflare.com...\n2026-03-30T19:56:12Z ERR failed to request quick Tunnel: Post "https://api.trycloudflare.com/tunnel": tls: failed to verify certificate: x509: certificate signed by unknown authority' + ); + + assert.equal( + error, + 'failed to request quick Tunnel: Post "https://api.trycloudflare.com/tunnel": tls: failed to verify certificate: x509: certificate signed by unknown authority' + ); +}); + test("getCloudflaredAssetSpec resolves linux amd64 binary", () => { const spec = getCloudflaredAssetSpec("linux", "x64"); @@ -49,22 +62,26 @@ test("getCloudflaredAssetSpec returns null for unsupported platforms", () => { }); test("buildCloudflaredChildEnv keeps runtime essentials, isolates runtime dirs, and drops secrets", () => { - const env = buildCloudflaredChildEnv({ - PATH: "/usr/bin", - HTTPS_PROXY: "http://proxy.internal:8080", - JWT_SECRET: "top-secret", - API_KEY_SECRET: "another-secret", - }, { - runtimeRoot: "/managed/runtime", - homeDir: "/managed/runtime/home", - configDir: "/managed/runtime/config", - cacheDir: "/managed/runtime/cache", - dataDir: "/managed/runtime/data", - tempDir: "/managed/runtime/tmp", - userProfileDir: "/managed/runtime/userprofile", - appDataDir: "/managed/runtime/userprofile/AppData/Roaming", - localAppDataDir: "/managed/runtime/userprofile/AppData/Local", - }); + const env = buildCloudflaredChildEnv( + { + PATH: "/usr/bin", + HTTPS_PROXY: "http://proxy.internal:8080", + JWT_SECRET: "top-secret", + API_KEY_SECRET: "another-secret", + }, + { + runtimeRoot: "/managed/runtime", + homeDir: "/managed/runtime/home", + configDir: "/managed/runtime/config", + cacheDir: "/managed/runtime/cache", + dataDir: "/managed/runtime/data", + tempDir: "/managed/runtime/tmp", + userProfileDir: "/managed/runtime/userprofile", + appDataDir: "/managed/runtime/userprofile/AppData/Roaming", + localAppDataDir: "/managed/runtime/userprofile/AppData/Local", + }, + {} + ); assert.deepEqual(env, { PATH: "/usr/bin", @@ -82,6 +99,41 @@ test("buildCloudflaredChildEnv keeps runtime essentials, isolates runtime dirs, }); }); +test("getDefaultCloudflaredCertEnv detects common CA bundle paths", () => { + const env = getDefaultCloudflaredCertEnv((candidate) => + ["/etc/ssl/certs/ca-certificates.crt", "/etc/ssl/certs"].includes(candidate) + ); + + assert.deepEqual(env, { + SSL_CERT_FILE: "/etc/ssl/certs/ca-certificates.crt", + SSL_CERT_DIR: "/etc/ssl/certs", + }); +}); + +test("buildCloudflaredChildEnv injects discovered CA paths when the parent env omits them", () => { + const env = buildCloudflaredChildEnv( + { PATH: "/usr/bin" }, + { + runtimeRoot: "/managed/runtime", + homeDir: "/managed/runtime/home", + configDir: "/managed/runtime/config", + cacheDir: "/managed/runtime/cache", + dataDir: "/managed/runtime/data", + tempDir: "/managed/runtime/tmp", + userProfileDir: "/managed/runtime/userprofile", + appDataDir: "/managed/runtime/userprofile/AppData/Roaming", + localAppDataDir: "/managed/runtime/userprofile/AppData/Local", + }, + { + SSL_CERT_FILE: "/etc/ssl/certs/ca-certificates.crt", + SSL_CERT_DIR: "/etc/ssl/certs", + } + ); + + assert.equal(env.SSL_CERT_FILE, "/etc/ssl/certs/ca-certificates.crt"); + assert.equal(env.SSL_CERT_DIR, "/etc/ssl/certs"); +}); + test("getCloudflaredStartArgs relies on cloudflared protocol auto-negotiation", () => { assert.deepEqual(getCloudflaredStartArgs("http://127.0.0.1:20128"), [ "tunnel", From 35e2892b982b3d8585ddbe8a91267d3e446832b1 Mon Sep 17 00:00:00 2001 From: Chris Staley Date: Mon, 30 Mar 2026 13:22:40 -0600 Subject: [PATCH 05/18] feat: add real Gemini CLI quota tracking via retrieveUserQuota API Replace stub getGeminiUsage() with per-model quota fetching from Google Cloud Code Assist's retrieveUserQuota endpoint (same API the official Gemini CLI /stats command uses). Fixes OAuth env var name, aligns model list with official Gemini CLI VALID_GEMINI_MODELS, and makes "Import from /models" discover new models via the quota endpoint. --- open-sse/config/providerRegistry.ts | 13 +- open-sse/services/usage.ts | 194 +++++++++++++++--- .../usage/components/ProviderLimits/index.tsx | 15 +- .../usage/components/ProviderLimits/utils.tsx | 17 +- src/app/api/providers/[id]/models/route.ts | 79 +++++-- src/shared/constants/providers.ts | 1 + 6 files changed, 255 insertions(+), 64 deletions(-) diff --git a/open-sse/config/providerRegistry.ts b/open-sse/config/providerRegistry.ts index 7be61c8f6c..0228412b9f 100644 --- a/open-sse/config/providerRegistry.ts +++ b/open-sse/config/providerRegistry.ts @@ -226,23 +226,18 @@ export const REGISTRY: Record = { oauth: { clientIdEnv: "GEMINI_CLI_OAUTH_CLIENT_ID", clientIdDefault: "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com", - clientSecretEnv: "GEMINI_CLI_OAUTH_CLIENT_SECRET", + clientSecretEnv: "GEMINI_OAUTH_CLIENT_SECRET", clientSecretDefault: "", }, models: [ - { id: "gemini-3.1-pro-high", name: "Gemini 3.1 Pro High" }, - { id: "gemini-3.1-pro-low", name: "Gemini 3.1 Pro Low" }, - { id: "gemini-3.1-pro", name: "Gemini 3.1 Pro" }, - { id: "gemini-3-1-pro", name: "Gemini 3.1 Pro (Alt ID)" }, + { id: "gemini-3-pro-preview", name: "Gemini 3 Pro Preview" }, { id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro Preview" }, - { id: "gemini-3.1-flash-lite-preview", name: "Gemini 3.1 Flash Lite Preview" }, + { id: "gemini-3.1-pro-preview-customtools", name: "Gemini 3.1 Pro Preview Custom Tools" }, { id: "gemini-3-flash-preview", name: "Gemini 3 Flash Preview" }, + { id: "gemini-3.1-flash-lite-preview", name: "Gemini 3.1 Flash Lite Preview" }, { id: "gemini-2.5-pro", name: "Gemini 2.5 Pro" }, { id: "gemini-2.5-flash", name: "Gemini 2.5 Flash" }, { id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash Lite" }, - { id: "gemini-2.0-flash", name: "Gemini 2.0 Flash" }, - { id: "gemini-1.5-pro", name: "Gemini 1.5 Pro" }, - { id: "gemini-1.5-flash", name: "Gemini 1.5 Flash" }, ], }, diff --git a/open-sse/services/usage.ts b/open-sse/services/usage.ts index c1f1114a2e..fcd547e47b 100644 --- a/open-sse/services/usage.ts +++ b/open-sse/services/usage.ts @@ -159,13 +159,13 @@ async function getGlmUsage(apiKey: string, providerSpecificData?: Record} Usage data with quotas */ export async function getUsageForProvider(connection) { - const { provider, accessToken, apiKey, providerSpecificData } = connection; + const { provider, accessToken, apiKey, providerSpecificData, projectId } = connection; switch (provider) { case "github": return await getGitHubUsage(accessToken, providerSpecificData); case "gemini-cli": - return await getGeminiUsage(accessToken); + return await getGeminiUsage(accessToken, providerSpecificData, projectId); case "antigravity": return await getAntigravityUsage(accessToken, undefined); case "claude": @@ -195,24 +195,22 @@ function parseResetTime(resetValue) { if (!resetValue) return null; try { - // If it's already a Date object + let date; if (resetValue instanceof Date) { - return resetValue.toISOString(); + date = resetValue; + } else if (typeof resetValue === "number") { + date = new Date(resetValue); + } else if (typeof resetValue === "string") { + date = new Date(resetValue); + } else { + return null; } - // If it's a number (Unix timestamp in milliseconds) - if (typeof resetValue === "number") { - return new Date(resetValue).toISOString(); - } + // Epoch-zero (1970-01-01) means no scheduled reset — treat as null + if (date.getTime() <= 0) return null; - // If it's a string (ISO date or parseable date string) - if (typeof resetValue === "string") { - return new Date(resetValue).toISOString(); - } - - return null; + return date.toISOString(); } catch (error) { - console.warn(`Failed to parse reset time: ${resetValue}`, error); return null; } } @@ -417,36 +415,178 @@ function inferGitHubPlanName(data: JsonRecord, premiumQuota: UsageQuota | null): return "GitHub Copilot"; } +// ── Gemini CLI subscription info cache ────────────────────────────────────── +// Prevents duplicate loadCodeAssist calls within the same quota cycle. +// Key: truncated accessToken → { data, fetchedAt } +const _geminiCliSubCache = new Map(); +const GEMINI_CLI_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes + /** - * Gemini CLI Usage (Google Cloud) + * Gemini CLI Usage — fetch per-model quota from Cloud Code Assist API. + * Gemini CLI and Antigravity share the same upstream (cloudcode-pa.googleapis.com), + * so this follows the same pattern as getAntigravityUsage(). */ -async function getGeminiUsage(accessToken) { +async function getGeminiUsage(accessToken, providerSpecificData?, connectionProjectId?) { try { - // Gemini CLI uses Google Cloud quotas - // Try to get quota info from Cloud Resource Manager + const subscriptionInfo = await getGeminiCliSubscriptionInfoCached(accessToken); + const projectId = + connectionProjectId || + providerSpecificData?.projectId || + subscriptionInfo?.cloudaicompanionProject || + null; + + const plan = getGeminiCliPlanLabel(subscriptionInfo); + + if (!projectId) { + return { plan, message: "Gemini CLI project ID not available." }; + } + + // Use retrieveUserQuota (same endpoint as Gemini CLI /stats command). + // Returns per-model buckets with remainingFraction and resetTime. const response = await fetch( - "https://cloudresourcemanager.googleapis.com/v1/projects?filter=lifecycleState:ACTIVE", + "https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota", { + method: "POST", headers: { Authorization: `Bearer ${accessToken}`, - Accept: "application/json", + "Content-Type": "application/json", }, + body: JSON.stringify({ project: projectId }), } ); if (!response.ok) { - // Quota API may not be accessible, return generic message - return { - message: "Gemini CLI uses Google Cloud quotas. Check Google Cloud Console for details.", - }; + return { plan, message: `Gemini CLI quota error (${response.status}).` }; } - return { message: "Gemini CLI connected. Usage tracked via Google Cloud Console." }; + const data = await response.json(); + const quotas: Record = {}; + + if (Array.isArray(data.buckets)) { + for (const bucket of data.buckets) { + if (!bucket.modelId || bucket.remainingFraction == null) continue; + + const remainingFraction = toNumber(bucket.remainingFraction, 0); + const remainingPercentage = remainingFraction * 100; + const QUOTA_NORMALIZED_BASE = 1000; + const total = QUOTA_NORMALIZED_BASE; + const remaining = Math.round(total * remainingFraction); + const used = total - remaining; + + quotas[bucket.modelId] = { + used, + total, + resetAt: parseResetTime(bucket.resetTime), + remainingPercentage, + unlimited: false, + }; + } + } + + return { plan, quotas }; } catch (error) { - return { message: "Unable to fetch Gemini usage. Check Google Cloud Console." }; + return { message: `Gemini CLI error: ${(error as Error).message}` }; } } +/** + * Get Gemini CLI subscription info (cached, 5 min TTL) + */ +async function getGeminiCliSubscriptionInfoCached(accessToken) { + const cacheKey = accessToken.substring(0, 16); + const cached = _geminiCliSubCache.get(cacheKey); + + if (cached && Date.now() - cached.fetchedAt < GEMINI_CLI_CACHE_TTL_MS) { + return cached.data; + } + + const data = await getGeminiCliSubscriptionInfo(accessToken); + _geminiCliSubCache.set(cacheKey, { data, fetchedAt: Date.now() }); + return data; +} + +/** + * Get Gemini CLI subscription info using correct headers. + */ +async function getGeminiCliSubscriptionInfo(accessToken) { + try { + const response = await fetch( + "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist", + { + method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + metadata: { + ideType: "IDE_UNSPECIFIED", + platform: "PLATFORM_UNSPECIFIED", + pluginType: "GEMINI", + }, + }), + } + ); + + if (!response.ok) return null; + + return await response.json(); + } catch { + return null; + } +} + +/** + * Map Gemini CLI subscription tier to display label (same tiers as Antigravity). + */ +function getGeminiCliPlanLabel(subscriptionInfo) { + if (!subscriptionInfo || Object.keys(subscriptionInfo).length === 0) return "Free"; + + let tierId = ""; + if (Array.isArray(subscriptionInfo.allowedTiers)) { + for (const tier of subscriptionInfo.allowedTiers) { + if (tier.isDefault && tier.id) { + tierId = tier.id.trim().toUpperCase(); + break; + } + } + } + + if (!tierId) { + tierId = (subscriptionInfo.currentTier?.id || "").toUpperCase(); + } + + if (tierId) { + if (tierId.includes("ULTRA")) return "Ultra"; + if (tierId.includes("PRO")) return "Pro"; + if (tierId.includes("ENTERPRISE")) return "Enterprise"; + if (tierId.includes("BUSINESS") || tierId.includes("STANDARD")) return "Business"; + if (tierId.includes("FREE") || tierId.includes("INDIVIDUAL") || tierId.includes("LEGACY")) + return "Free"; + } + + const tierName = + subscriptionInfo.currentTier?.name || + subscriptionInfo.currentTier?.displayName || + subscriptionInfo.subscriptionType || + subscriptionInfo.tier || + ""; + const upper = tierName.toUpperCase(); + + if (upper.includes("ULTRA")) return "Ultra"; + if (upper.includes("PRO")) return "Pro"; + if (upper.includes("ENTERPRISE")) return "Enterprise"; + if (upper.includes("STANDARD") || upper.includes("BUSINESS")) return "Business"; + if (upper.includes("INDIVIDUAL") || upper.includes("FREE")) return "Free"; + + if (subscriptionInfo.currentTier?.upgradeSubscriptionType) return "Free"; + if (tierName) { + return tierName.charAt(0).toUpperCase() + tierName.slice(1).toLowerCase(); + } + + return "Free"; +} + // ── Antigravity subscription info cache ────────────────────────────────────── // Prevents duplicate loadCodeAssist calls within the same quota cycle. // Key: truncated accessToken → { data, fetchedAt } diff --git a/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx b/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx index 4075c078e9..a0a450c127 100644 --- a/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx +++ b/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx @@ -28,6 +28,7 @@ const QUOTA_BAR_YELLOW_THRESHOLD = 20; // Provider display config const PROVIDER_CONFIG = { antigravity: { label: "Antigravity", color: "#F59E0B" }, + "gemini-cli": { label: "Gemini CLI", color: "#4285F4" }, github: { label: "GitHub Copilot", color: "#333" }, kiro: { label: "Kiro AI", color: "#FF6B35" }, codex: { label: "OpenAI Codex", color: "#10A37F" }, @@ -279,12 +280,13 @@ export default function ProviderLimits() { const sortedConnections = useMemo(() => { const priority = { antigravity: 1, - github: 2, - codex: 3, - claude: 4, - kiro: 5, - glm: 6, - "kimi-coding": 7, + "gemini-cli": 2, + github: 3, + codex: 4, + claude: 5, + kiro: 6, + glm: 7, + "kimi-coding": 8, }; return [...filteredConnections].sort( (a, b) => (priority[a.provider] || 9) - (priority[b.provider] || 9) @@ -624,6 +626,7 @@ export default function ProviderLimits() { > {/* Model label */} diff --git a/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.tsx b/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.tsx index a14643b785..b01a36514b 100644 --- a/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.tsx +++ b/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/utils.tsx @@ -11,15 +11,6 @@ const PROVIDER_PLAN_FALLBACKS = new Set([ ]); const QUOTA_LABEL_MAP: Record = { - "gemini-3-pro-high": "G3 Pro", - "gemini-3-pro-low": "G3 Pro Low", - "gemini-3-flash": "G3 Flash", - "gemini-2.5-flash": "G2.5 Flash", - "claude-opus-4-6-thinking": "Opus 4.6 Tk", - "claude-opus-4-5-thinking": "Opus 4.5 Tk", - "claude-opus-4-5": "Opus 4.5", - "claude-sonnet-4-5-thinking": "Sonnet 4.5 Tk", - "claude-sonnet-4-5": "Sonnet 4.5", chat: "Chat", completions: "Completions", premium_interactions: "Premium", @@ -254,6 +245,14 @@ export function parseQuotaData(provider, data) { } break; + case "gemini-cli": + if (data.quotas) { + Object.entries(data.quotas).forEach(([modelKey, quota]: [string, any]) => { + normalizedQuotas.push(normalizeQuotaEntry(modelKey, quota, { modelKey })); + }); + } + break; + default: // Generic fallback for unknown providers if (data.quotas) { diff --git a/src/app/api/providers/[id]/models/route.ts b/src/app/api/providers/[id]/models/route.ts index ef8a8d1a36..4a9f95a1e9 100644 --- a/src/app/api/providers/[id]/models/route.ts +++ b/src/app/api/providers/[id]/models/route.ts @@ -139,19 +139,7 @@ const PROVIDER_MODELS_CONFIG: Record = { name: m.displayName || (m.name || "").replace(/^models\//, ""), })), }, - "gemini-cli": { - url: "https://generativelanguage.googleapis.com/v1beta/models", - method: "GET", - headers: { "Content-Type": "application/json" }, - authHeader: "Authorization", - authPrefix: "Bearer ", - parseResponse: (data) => - (data.models || []).map((m) => ({ - ...m, - id: (m.name || m.id || "").replace(/^models\//, ""), - name: m.displayName || (m.name || "").replace(/^models\//, ""), - })), - }, + // gemini-cli handled via retrieveUserQuota (see GET handler) qwen: { url: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/models", method: "GET", @@ -505,6 +493,71 @@ export async function GET( return buildResponse({ provider, connectionId, models }); } + if (provider === "gemini-cli") { + // Gemini CLI doesn't have a /models endpoint. Instead, query the quota + // endpoint to discover available models from the quota buckets. + if (!accessToken) { + return NextResponse.json( + { error: "No access token for Gemini CLI. Please reconnect OAuth." }, + { status: 400 } + ); + } + + const psd = asRecord(connection.providerSpecificData); + const projectId = + connection.projectId || psd.projectId || null; + + if (!projectId) { + return NextResponse.json( + { error: "Gemini CLI project ID not available. Please reconnect OAuth." }, + { status: 400 } + ); + } + + try { + const quotaRes = await fetch( + "https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota", + { + method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ project: projectId }), + } + ); + + if (!quotaRes.ok) { + const errText = await quotaRes.text(); + console.log(`[models] Gemini CLI quota fetch failed (${quotaRes.status}):`, errText); + return NextResponse.json( + { error: `Failed to fetch Gemini CLI models: ${quotaRes.status}` }, + { status: quotaRes.status } + ); + } + + const quotaData = await quotaRes.json(); + const buckets: Array<{ modelId?: string; tokenType?: string }> = + quotaData.buckets || []; + + const models = buckets + .filter((b) => b.modelId) + .map((b) => ({ + id: b.modelId, + name: b.modelId, + owned_by: "google", + })); + + return buildResponse({ provider, connectionId, models }); + } catch (err: any) { + console.log("[models] Gemini CLI model fetch error:", err.message); + return NextResponse.json( + { error: "Failed to fetch Gemini CLI models" }, + { status: 500 } + ); + } + } + if (isAnthropicCompatibleProvider(provider)) { let baseUrl = getProviderBaseUrl(connection.providerSpecificData); if (!baseUrl) { diff --git a/src/shared/constants/providers.ts b/src/shared/constants/providers.ts index 85ea9128dc..266dc08234 100644 --- a/src/shared/constants/providers.ts +++ b/src/shared/constants/providers.ts @@ -656,6 +656,7 @@ export const ID_TO_ALIAS = Object.values(AI_PROVIDERS).reduce((acc, p) => { // Providers that support usage/quota API export const USAGE_SUPPORTED_PROVIDERS = [ "antigravity", + "gemini-cli", "kiro", "github", "codex", From c5d9b5f51d3693e4090722f8bce9686d4f135b78 Mon Sep 17 00:00:00 2001 From: Chris Staley Date: Mon, 30 Mar 2026 13:39:42 -0600 Subject: [PATCH 06/18] fix: apply PR review feedback for Gemini CLI quota - Add early return guard for missing accessToken in getGeminiUsage - Add 10s fetch timeout (AbortSignal.timeout) on retrieveUserQuota calls - Clamp used value with Math.max(0, ...) for non-negative display - Use full accessToken as cache key instead of truncated prefix - Replace catch(err: any) with instanceof Error check in models route --- open-sse/services/usage.ts | 11 ++++++++--- src/app/api/providers/[id]/models/route.ts | 6 ++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/open-sse/services/usage.ts b/open-sse/services/usage.ts index fcd547e47b..e112332b11 100644 --- a/open-sse/services/usage.ts +++ b/open-sse/services/usage.ts @@ -417,7 +417,7 @@ function inferGitHubPlanName(data: JsonRecord, premiumQuota: UsageQuota | null): // ── Gemini CLI subscription info cache ────────────────────────────────────── // Prevents duplicate loadCodeAssist calls within the same quota cycle. -// Key: truncated accessToken → { data, fetchedAt } +// Key: accessToken → { data, fetchedAt } const _geminiCliSubCache = new Map(); const GEMINI_CLI_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes @@ -427,6 +427,10 @@ const GEMINI_CLI_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes * so this follows the same pattern as getAntigravityUsage(). */ async function getGeminiUsage(accessToken, providerSpecificData?, connectionProjectId?) { + if (!accessToken) { + return { plan: "Free", message: "Gemini CLI access token not available." }; + } + try { const subscriptionInfo = await getGeminiCliSubscriptionInfoCached(accessToken); const projectId = @@ -452,6 +456,7 @@ async function getGeminiUsage(accessToken, providerSpecificData?, connectionProj "Content-Type": "application/json", }, body: JSON.stringify({ project: projectId }), + signal: AbortSignal.timeout(10000), } ); @@ -471,7 +476,7 @@ async function getGeminiUsage(accessToken, providerSpecificData?, connectionProj const QUOTA_NORMALIZED_BASE = 1000; const total = QUOTA_NORMALIZED_BASE; const remaining = Math.round(total * remainingFraction); - const used = total - remaining; + const used = Math.max(0, total - remaining); quotas[bucket.modelId] = { used, @@ -493,7 +498,7 @@ async function getGeminiUsage(accessToken, providerSpecificData?, connectionProj * Get Gemini CLI subscription info (cached, 5 min TTL) */ async function getGeminiCliSubscriptionInfoCached(accessToken) { - const cacheKey = accessToken.substring(0, 16); + const cacheKey = accessToken; const cached = _geminiCliSubCache.get(cacheKey); if (cached && Date.now() - cached.fetchedAt < GEMINI_CLI_CACHE_TTL_MS) { diff --git a/src/app/api/providers/[id]/models/route.ts b/src/app/api/providers/[id]/models/route.ts index 4a9f95a1e9..660076c6c6 100644 --- a/src/app/api/providers/[id]/models/route.ts +++ b/src/app/api/providers/[id]/models/route.ts @@ -524,6 +524,7 @@ export async function GET( "Content-Type": "application/json", }, body: JSON.stringify({ project: projectId }), + signal: AbortSignal.timeout(10000), } ); @@ -549,8 +550,9 @@ export async function GET( })); return buildResponse({ provider, connectionId, models }); - } catch (err: any) { - console.log("[models] Gemini CLI model fetch error:", err.message); + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.log("[models] Gemini CLI model fetch error:", msg); return NextResponse.json( { error: "Failed to fetch Gemini CLI models" }, { status: 500 } From d043e7a242831b1c4ebcc06ec089467789f270fb Mon Sep 17 00:00:00 2001 From: oyi77 Date: Tue, 31 Mar 2026 00:53:18 +0700 Subject: [PATCH 07/18] feat(cache): fix cache page to display prompt cache metrics and trend data Closes #813 --- src/app/(dashboard)/dashboard/cache/page.tsx | 195 ++++++++++++++++++- src/app/api/cache/route.ts | 24 +-- src/i18n/messages/en.json | 15 +- src/lib/db/settings.ts | 59 +++++- 4 files changed, 270 insertions(+), 23 deletions(-) diff --git a/src/app/(dashboard)/dashboard/cache/page.tsx b/src/app/(dashboard)/dashboard/cache/page.tsx index eaa51e4ad6..34007450e2 100644 --- a/src/app/(dashboard)/dashboard/cache/page.tsx +++ b/src/app/(dashboard)/dashboard/cache/page.tsx @@ -16,13 +16,44 @@ interface SemanticCacheStats { tokensSaved: number; } +interface PromptCacheProviderStats { + requests: number; + inputTokens: number; + cachedTokens: number; + cacheCreationTokens: number; +} + +interface PromptCacheMetrics { + totalRequests: number; + requestsWithCacheControl: number; + totalInputTokens: number; + totalCachedTokens: number; + totalCacheCreationTokens: number; + tokensSaved: number; + estimatedCostSaved: number; + byProvider: Record; + byStrategy: Record; + lastUpdated: string; +} + interface IdempotencyStats { activeKeys: number; windowMs: number; } +interface CacheTrendPoint { + timestamp: string; + requests: number; + cachedRequests: number; + inputTokens: number; + cachedTokens: number; + cacheCreationTokens: number; +} + interface CacheStats { semanticCache: SemanticCacheStats; + promptCache: PromptCacheMetrics | null; + trend: CacheTrendPoint[]; idempotency: IdempotencyStats; } @@ -136,27 +167,32 @@ export default function CachePage() { const res = await fetch("/api/cache", { method: "DELETE" }); if (res.ok) { const data = await res.json(); - notify.add({ - type: "success", - message: t("clearSuccess", { count: data.expiredRemoved ?? 0 }), - }); + notify.success(t("clearSuccess", { count: data.expiredRemoved ?? 0 })); await fetchStats(); } else { - notify.add({ type: "error", message: t("clearError") }); + notify.error(t("clearError")); } } catch (error) { console.error("[CachePage] Failed to clear cache:", error); - notify.add({ type: "error", message: t("clearError") }); + notify.error(t("clearError")); } finally { setClearing(false); } }; const sc = stats?.semanticCache; + const pc = stats?.promptCache; + const trend = stats?.trend ?? []; const idp = stats?.idempotency; const hitRate = sc ? parseFloat(sc.hitRate) : 0; const totalRequests = sc ? sc.hits + sc.misses : 0; + const promptCacheHitRate = + pc && pc.totalRequests > 0 ? (pc.requestsWithCacheControl / pc.totalRequests) * 100 : 0; + const providerEntries = pc ? Object.entries(pc.byProvider) : []; + + const maxTrendRequests = Math.max(1, ...trend.map((p) => p.requests)); + return (
{/* Header */} @@ -278,6 +314,153 @@ export default function CachePage() {
+ {/* Prompt Cache Stats */} + {pc && ( + +
+
+ +

{t("promptCache")}

+
+ +
+
+
+ {pc.requestsWithCacheControl.toLocaleString()} +
+
{t("cachedRequests")}
+
+
+
+ {promptCacheHitRate.toFixed(1)}% +
+
{t("cacheHitRate")}
+
+
+
+ {pc.totalCachedTokens.toLocaleString()} +
+
{t("cachedTokens")}
+
+
+
+ {pc.totalCacheCreationTokens.toLocaleString()} +
+
{t("cacheCreationTokens")}
+
+
+ + {providerEntries.length > 0 && ( +
+

{t("byProvider")}

+
+ + + + + + + + + + + + {providerEntries.map(([provider, data]) => ( + + + + + + + + ))} + +
{t("provider")}{t("requests")}{t("inputTokens")}{t("cachedTokensCol")}{t("cacheCreation")}
{provider} + {data.requests.toLocaleString()} + + {data.inputTokens.toLocaleString()} + + {data.cachedTokens.toLocaleString()} + + {data.cacheCreationTokens.toLocaleString()} +
+
+
+ )} +
+
+ )} + + {/* Cache Trend (24h) */} + {trend.length > 0 && ( + +
+
+ +

{t("trend24h")}

+
+
+ {trend.map((point) => { + const height = Math.max(4, (point.requests / maxTrendRequests) * 100); + const cachedHeight = + point.requests > 0 + ? Math.max(2, (point.cachedRequests / point.requests) * height) + : 0; + const hour = new Date(point.timestamp).toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); + return ( +
+
+ {hour}: {point.requests} {t("requests").toLowerCase()},{" "} + {point.cachedRequests} {t("cached").toLowerCase()} +
+
+
+
+
+ + {hour.split(":")[0]} + +
+ ); + })} +
+
+
+
+ {t("total")} +
+
+
+ {t("cached")} +
+
+
+ + )} + {/* Cache behavior */}
diff --git a/src/app/api/cache/route.ts b/src/app/api/cache/route.ts index ebb02dc2f4..d1bca53891 100644 --- a/src/app/api/cache/route.ts +++ b/src/app/api/cache/route.ts @@ -8,21 +8,26 @@ import { invalidateStale, } from "@/lib/semanticCache"; import { getIdempotencyStats } from "@/lib/idempotencyLayer"; +import { getCacheMetrics, getCacheTrend } from "@/lib/db/settings"; function errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } -/** - * GET /api/cache — Cache statistics - */ -export async function GET() { +export async function GET(req: NextRequest) { try { + const { searchParams } = new URL(req.url); + const trendHours = parseInt(searchParams.get("trendHours") || "24", 10); + const cacheStats = getCacheStats(); const idempotencyStats = getIdempotencyStats(); + const promptCacheMetrics = await getCacheMetrics(); + const trend = await getCacheTrend(trendHours); return NextResponse.json({ semanticCache: cacheStats, + promptCache: promptCacheMetrics, + trend, idempotency: idempotencyStats, }); } catch (error) { @@ -30,17 +35,6 @@ export async function GET() { } } -/** - * DELETE /api/cache — Clear all caches or targeted invalidation. - * - * Exactly one optional query parameter may be provided: - * ?model= — invalidate all entries for a specific model - * ?signature= — invalidate a single entry by its SHA-256 signature - * ?staleMs= — invalidate entries older than N milliseconds - * (no params) — clear all cache entries - * - * Providing more than one parameter returns 400 Bad Request. - */ export async function DELETE(req: NextRequest) { try { const { searchParams } = new URL(req.url); diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index bd5605eb7f..745da07ac3 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -2920,6 +2920,19 @@ "clearSuccess": "Cache cleared. {count} expired entries removed.", "clearError": "Failed to clear cache.", "unavailable": "Cache unavailable", - "unavailableDesc": "Could not fetch cache statistics. Make sure the server is running." + "unavailableDesc": "Could not fetch cache statistics. Make sure the server is running.", + "promptCache": "Prompt Cache (Provider-Side)", + "cachedRequests": "Cached Requests", + "cacheHitRate": "Cache Hit Rate", + "cachedTokens": "Cached Tokens", + "cacheCreationTokens": "Cache Creation Tokens", + "byProvider": "Breakdown by Provider", + "provider": "Provider", + "requests": "Requests", + "inputTokens": "Input Tokens", + "cachedTokensCol": "Cached", + "cacheCreation": "Creation", + "trend24h": "Cache Trend (24h)", + "cached": "Cached" } } diff --git a/src/lib/db/settings.ts b/src/lib/db/settings.ts index 00224a353a..333d71379f 100644 --- a/src/lib/db/settings.ts +++ b/src/lib/db/settings.ts @@ -577,9 +577,14 @@ export async function getCacheMetrics() { cacheCreationTokens: number | null; }>; - // Calculate tokens saved (cached tokens are reused, not charged at full price) const tokensSaved = totalsRow?.totalCachedTokens || 0; + const AVG_INPUT_PRICE_PER_MILLION = 3; + const CACHE_DISCOUNT = 0.9; + const estimatedCostSaved = + Math.round((tokensSaved / 1_000_000) * AVG_INPUT_PRICE_PER_MILLION * CACHE_DISCOUNT * 100) / + 100; + // Build byProvider object const byProvider: Record< string, @@ -653,6 +658,58 @@ export async function updateCacheMetrics(_metrics: Record) { return getCacheMetrics(); } +export interface CacheTrendPoint { + timestamp: string; + requests: number; + cachedRequests: number; + inputTokens: number; + cachedTokens: number; + cacheCreationTokens: number; +} + +export async function getCacheTrend(hours = 24): Promise { + const db = getDbInstance(); + + try { + const rows = db + .prepare( + ` + SELECT + strftime('%Y-%m-%dT%H:00:00Z', timestamp) as hour, + COUNT(*) as requests, + SUM(CASE WHEN tokens_cache_read > 0 OR tokens_cache_creation > 0 THEN 1 ELSE 0 END) as cachedRequests, + SUM(tokens_input) as inputTokens, + SUM(tokens_cache_read) as cachedTokens, + SUM(tokens_cache_creation) as cacheCreationTokens + FROM usage_history + WHERE timestamp >= datetime('now', ?) + GROUP BY hour + ORDER BY hour ASC + ` + ) + .all(`-${hours} hours`) as Array<{ + hour: string; + requests: number; + cachedRequests: number; + inputTokens: number | null; + cachedTokens: number | null; + cacheCreationTokens: number | null; + }>; + + return rows.map((r) => ({ + timestamp: r.hour, + requests: r.requests, + cachedRequests: r.cachedRequests, + inputTokens: r.inputTokens || 0, + cachedTokens: r.cachedTokens || 0, + cacheCreationTokens: r.cacheCreationTokens || 0, + })); + } catch (error) { + console.error("Failed to fetch cache trend:", error); + return []; + } +} + export async function resetCacheMetrics() { // No-op: cannot delete historical usage data // Cache metrics are computed from usage_history, so they reflect actual request history From 678048505109d6795db459082e7a7e44097756f2 Mon Sep 17 00:00:00 2001 From: oyi77 Date: Tue, 31 Mar 2026 02:41:30 +0700 Subject: [PATCH 08/18] feat(cache): persistent metrics, cache entry browser, settings UI, MCP tools, prefix analyzer Implements remaining features from #813: Phase 1 - Persistent Metrics: - Add cache_metrics table for persistent hit/miss tracking - Semantic cache stats now survive server restarts Phase 2 - Cache Entry Browser: - /api/cache/entries endpoint with search, pagination, delete - CacheEntriesTab component for browsing cached entries Phase 3 - Settings UI: - CacheSettingsTab for semantic/prompt cache configuration - /api/settings/cache-config endpoint Phase 4 - Prefix Analyzer: - src/lib/promptCache/prefixAnalyzer.ts for intelligent caching - Analyzes message arrays to find stable prefixes Phase 5 - Provider Support: - Added deepseek to CACHING_PROVIDERS Phase 6 - MCP Tools: - omniroute_cache_stats tool - omniroute_cache_flush tool Phase 7 - Retention: - cleanOldMetrics() for auto-purge of old entries Closes #813 --- open-sse/mcp-server/schemas/index.ts | 6 + open-sse/mcp-server/schemas/tools.ts | 67 +- open-sse/utils/cacheControlPolicy.ts | 7 +- .../cache/components/CacheEntriesTab.tsx | 174 +++++ src/app/(dashboard)/dashboard/cache/page.tsx | 598 ++++++++++-------- .../settings/components/CacheSettingsTab.tsx | 191 ++++++ .../(dashboard)/dashboard/settings/page.tsx | 2 + src/app/api/cache/entries/route.ts | 95 +++ src/app/api/settings/cache-config/route.ts | 73 +++ src/i18n/messages/en.json | 24 +- src/lib/promptCache/index.ts | 1 + src/lib/promptCache/prefixAnalyzer.ts | 77 +++ src/lib/semanticCache.ts | 85 ++- 13 files changed, 1094 insertions(+), 306 deletions(-) create mode 100644 src/app/(dashboard)/dashboard/cache/components/CacheEntriesTab.tsx create mode 100644 src/app/(dashboard)/dashboard/settings/components/CacheSettingsTab.tsx create mode 100644 src/app/api/cache/entries/route.ts create mode 100644 src/app/api/settings/cache-config/route.ts create mode 100644 src/lib/promptCache/index.ts create mode 100644 src/lib/promptCache/prefixAnalyzer.ts diff --git a/open-sse/mcp-server/schemas/index.ts b/open-sse/mcp-server/schemas/index.ts index 99a947f3ff..ed1a5c9ba6 100644 --- a/open-sse/mcp-server/schemas/index.ts +++ b/open-sse/mcp-server/schemas/index.ts @@ -60,6 +60,12 @@ export { getSessionSnapshotInput, getSessionSnapshotOutput, getSessionSnapshotTool, + cacheStatsInput, + cacheStatsOutput, + cacheStatsTool, + cacheFlushInput, + cacheFlushOutput, + cacheFlushTool, } from "./tools.ts"; // A2A schemas diff --git a/open-sse/mcp-server/schemas/tools.ts b/open-sse/mcp-server/schemas/tools.ts index 1557c0a136..46d03f325a 100644 --- a/open-sse/mcp-server/schemas/tools.ts +++ b/open-sse/mcp-server/schemas/tools.ts @@ -806,11 +806,73 @@ export const syncPricingTool: McpToolDefinition = { + name: "omniroute_cache_stats", + description: + "Returns cache statistics including semantic cache hit rate, prompt cache metrics by provider, and idempotency layer stats.", + inputSchema: cacheStatsInput, + outputSchema: cacheStatsOutput, + scopes: ["read:cache"], + auditLevel: "basic", + phase: 2, + sourceEndpoints: ["/api/cache"], +}; + +export const cacheFlushInput = z.object({ + signature: z.string().optional().describe("Specific cache signature to invalidate"), + model: z.string().optional().describe("Invalidate all entries for a specific model"), +}); + +export const cacheFlushOutput = z.object({ + ok: z.boolean(), + invalidated: z.number().optional(), + scope: z.string().optional(), +}); + +export const cacheFlushTool: McpToolDefinition = { + name: "omniroute_cache_flush", + description: + "Flush cache entries. Provide signature to invalidate a single entry, model to invalidate all entries for a model, or omit both to clear all.", + inputSchema: cacheFlushInput, + outputSchema: cacheFlushOutput, + scopes: ["write:cache"], + auditLevel: "full", + phase: 2, + sourceEndpoints: ["/api/cache"], +}; + // ============ Tool Registry ============ /** All MCP tool definitions, ordered by phase then name */ export const MCP_TOOLS = [ - // Phase 1: Essential getHealthTool, listCombosTool, getComboMetricsTool, @@ -819,7 +881,6 @@ export const MCP_TOOLS = [ routeRequestTool, costReportTool, listModelsCatalogTool, - // Phase 2: Advanced simulateRouteTool, setBudgetGuardTool, setRoutingStrategyTool, @@ -830,6 +891,8 @@ export const MCP_TOOLS = [ explainRouteTool, getSessionSnapshotTool, syncPricingTool, + cacheStatsTool, + cacheFlushTool, ] as const; /** Essential tools only (Phase 1) */ diff --git a/open-sse/utils/cacheControlPolicy.ts b/open-sse/utils/cacheControlPolicy.ts index af501d7495..3f18e2cf6d 100644 --- a/open-sse/utils/cacheControlPolicy.ts +++ b/open-sse/utils/cacheControlPolicy.ts @@ -72,12 +72,7 @@ const DETERMINISTIC_STRATEGIES: Set = new Set(["priority", /** * Providers that support prompt caching */ -const CACHING_PROVIDERS = new Set([ - "claude", - "anthropic", - "zai", - "qwen", // Alibaba Qwen Coding Plan International -]); +const CACHING_PROVIDERS = new Set(["claude", "anthropic", "zai", "qwen", "deepseek"]); /** * Detect if the client is Claude Code or another caching-aware client diff --git a/src/app/(dashboard)/dashboard/cache/components/CacheEntriesTab.tsx b/src/app/(dashboard)/dashboard/cache/components/CacheEntriesTab.tsx new file mode 100644 index 0000000000..66792dcbfa --- /dev/null +++ b/src/app/(dashboard)/dashboard/cache/components/CacheEntriesTab.tsx @@ -0,0 +1,174 @@ +"use client"; + +import { useState, useEffect, useCallback } from "react"; +import { Button } from "@/shared/components"; +import { useTranslations } from "next-intl"; + +interface CacheEntry { + id: string; + signature: string; + model: string; + hit_count: number; + tokens_saved: number; + created_at: string; + expires_at: string; +} + +interface Pagination { + page: number; + limit: number; + total: number; + totalPages: number; +} + +export default function CacheEntriesTab() { + const t = useTranslations("cache"); + const [entries, setEntries] = useState([]); + const [pagination, setPagination] = useState({ + page: 1, + limit: 20, + total: 0, + totalPages: 0, + }); + const [loading, setLoading] = useState(true); + const [search, setSearch] = useState(""); + const [deleting, setDeleting] = useState(null); + + const fetchEntries = useCallback( + async (page = 1) => { + setLoading(true); + try { + const params = new URLSearchParams({ page: String(page), limit: String(pagination.limit) }); + if (search) params.set("search", search); + + const res = await fetch(`/api/cache/entries?${params}`); + if (res.ok) { + const data = await res.json(); + setEntries(data.entries); + setPagination(data.pagination); + } + } catch { + // ignore + } finally { + setLoading(false); + } + }, + [search, pagination.limit] + ); + + useEffect(() => { + fetchEntries(); + }, [fetchEntries]); + + const handleDelete = async (signature: string) => { + setDeleting(signature); + try { + await fetch(`/api/cache/entries?signature=${encodeURIComponent(signature)}`, { + method: "DELETE", + }); + await fetchEntries(pagination.page); + } finally { + setDeleting(null); + } + }; + + const formatDate = (dateStr: string) => { + return new Date(dateStr).toLocaleString(); + }; + + return ( +
+
+ setSearch(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && fetchEntries()} + className="flex-1 px-3 py-2 text-sm rounded-lg border border-border bg-surface text-text-main placeholder:text-text-muted" + /> + +
+ + {loading ? ( +
{t("loading")}
+ ) : entries.length === 0 ? ( +
{t("noEntries")}
+ ) : ( + <> +
+ + + + + + + + + + + + + + {entries.map((entry) => ( + + + + + + + + + + ))} + +
{t("signature")}{t("model")}{t("hits")}{t("tokensSaved")}{t("created")}{t("expires")}{t("actions")}
+ {entry.signature.slice(0, 12)}... + {entry.model}{entry.hit_count} + {entry.tokens_saved.toLocaleString()} + + {formatDate(entry.created_at)} + + {formatDate(entry.expires_at)} + + +
+
+ + {/* Pagination */} + {pagination.totalPages > 1 && ( +
+ + + {pagination.page} / {pagination.totalPages} + + +
+ )} + + )} +
+ ); +} diff --git a/src/app/(dashboard)/dashboard/cache/page.tsx b/src/app/(dashboard)/dashboard/cache/page.tsx index 34007450e2..24064e8403 100644 --- a/src/app/(dashboard)/dashboard/cache/page.tsx +++ b/src/app/(dashboard)/dashboard/cache/page.tsx @@ -4,6 +4,7 @@ import { useState, useEffect, useCallback } from "react"; import { Card, Button, EmptyState } from "@/shared/components"; import { useNotificationStore } from "@/store/notificationStore"; import { useTranslations } from "next-intl"; +import CacheEntriesTab from "./components/CacheEntriesTab"; // ─── Types ─────────────────────────────────────────────────────────────────── @@ -138,6 +139,7 @@ export default function CachePage() { const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); const [clearing, setClearing] = useState(false); + const [activeTab, setActiveTab] = useState<"overview" | "entries">("overview"); const notify = useNotificationStore(); const fetchStats = useCallback(async () => { @@ -226,296 +228,334 @@ export default function CachePage() {
- {/* Loading skeleton */} - {loading && ( -
+ + +
- {/* Error / empty state */} - {!loading && !stats && ( - void fetchStats()} - /> - )} + {/* Entries tab */} + {activeTab === "entries" && } - {/* Main content */} - {!loading && stats && ( + {/* Overview tab content */} + {activeTab === "overview" && ( <> - {/* Stats grid */} -
- - - - -
- - {/* Hit rate + breakdown */} - -
-
-

{t("performance")}

- - {t("autoRefresh", { seconds: REFRESH_INTERVAL_SECONDS })} - -
- -
-
-
- {sc?.hits ?? 0} -
-
{t("hits")}
-
-
-
- {sc?.misses ?? 0} -
-
{t("misses")}
-
-
-
{totalRequests}
-
{t("total")}
-
-
+ {/* Loading skeleton */} + {loading && ( +
+ {Array.from({ length: 4 }).map((_, i) => ( +
+ ))}
- - - {/* Prompt Cache Stats */} - {pc && ( - -
-
- -

{t("promptCache")}

-
- -
-
-
- {pc.requestsWithCacheControl.toLocaleString()} -
-
{t("cachedRequests")}
-
-
-
- {promptCacheHitRate.toFixed(1)}% -
-
{t("cacheHitRate")}
-
-
-
- {pc.totalCachedTokens.toLocaleString()} -
-
{t("cachedTokens")}
-
-
-
- {pc.totalCacheCreationTokens.toLocaleString()} -
-
{t("cacheCreationTokens")}
-
-
- - {providerEntries.length > 0 && ( -
-

{t("byProvider")}

-
- - - - - - - - - - - - {providerEntries.map(([provider, data]) => ( - - - - - - - - ))} - -
{t("provider")}{t("requests")}{t("inputTokens")}{t("cachedTokensCol")}{t("cacheCreation")}
{provider} - {data.requests.toLocaleString()} - - {data.inputTokens.toLocaleString()} - - {data.cachedTokens.toLocaleString()} - - {data.cacheCreationTokens.toLocaleString()} -
-
-
- )} -
-
)} - {/* Cache Trend (24h) */} - {trend.length > 0 && ( - -
-
- -

{t("trend24h")}

-
-
- {trend.map((point) => { - const height = Math.max(4, (point.requests / maxTrendRequests) * 100); - const cachedHeight = - point.requests > 0 - ? Math.max(2, (point.cachedRequests / point.requests) * height) - : 0; - const hour = new Date(point.timestamp).toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - hour12: false, - }); - return ( -
-
- {hour}: {point.requests} {t("requests").toLowerCase()},{" "} - {point.cachedRequests} {t("cached").toLowerCase()} -
-
-
-
-
- - {hour.split(":")[0]} - + {/* Error / empty state */} + {!loading && !stats && ( + void fetchStats()} + /> + )} + + {/* Main content */} + {!loading && stats && ( + <> + {/* Stats grid */} +
+ + + + +
+ + {/* Hit rate + breakdown */} + +
+
+

{t("performance")}

+ + {t("autoRefresh", { seconds: REFRESH_INTERVAL_SECONDS })} + +
+ +
+
+
+ {sc?.hits ?? 0}
- ); - })} -
-
-
-
- {t("total")} -
-
-
- {t("cached")} +
{t("hits")}
+
+
+
+ {sc?.misses ?? 0} +
+
{t("misses")}
+
+
+
{totalRequests}
+
{t("total")}
+
-
- + + + {/* Prompt Cache Stats */} + {pc && ( + +
+
+ +

{t("promptCache")}

+
+ +
+
+
+ {pc.requestsWithCacheControl.toLocaleString()} +
+
{t("cachedRequests")}
+
+
+
+ {promptCacheHitRate.toFixed(1)}% +
+
{t("cacheHitRate")}
+
+
+
+ {pc.totalCachedTokens.toLocaleString()} +
+
{t("cachedTokens")}
+
+
+
+ {pc.totalCacheCreationTokens.toLocaleString()} +
+
+ {t("cacheCreationTokens")} +
+
+
+ + {providerEntries.length > 0 && ( +
+

+ {t("byProvider")} +

+
+ + + + + + + + + + + + {providerEntries.map(([provider, data]) => ( + + + + + + + + ))} + +
{t("provider")}{t("requests")}{t("inputTokens")}{t("cachedTokensCol")}{t("cacheCreation")}
{provider} + {data.requests.toLocaleString()} + + {data.inputTokens.toLocaleString()} + + {data.cachedTokens.toLocaleString()} + + {data.cacheCreationTokens.toLocaleString()} +
+
+
+ )} +
+
+ )} + + {/* Cache Trend (24h) */} + {trend.length > 0 && ( + +
+
+ +

{t("trend24h")}

+
+
+ {trend.map((point) => { + const height = Math.max(4, (point.requests / maxTrendRequests) * 100); + const cachedHeight = + point.requests > 0 + ? Math.max(2, (point.cachedRequests / point.requests) * height) + : 0; + const hour = new Date(point.timestamp).toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); + return ( +
+
+ {hour}: {point.requests} {t("requests").toLowerCase()},{" "} + {point.cachedRequests} {t("cached").toLowerCase()} +
+
+
+
+
+ + {hour.split(":")[0]} + +
+ ); + })} +
+
+
+
+ {t("total")} +
+
+
+ {t("cached")} +
+
+
+ + )} + + {/* Cache behavior */} + +
+

{t("behavior")}

+
+ {t("behaviorDeterministic")} + + {t.rich("behaviorBypass", { + header: () => ( + + X-OmniRoute-No-Cache: true + + ), + })} + + {t("behaviorTwoTier")} + + {t.rich("behaviorTtl", { + envVar: () => ( + + SEMANTIC_CACHE_TTL_MS + + ), + })} + +
+
+
+ + {/* Idempotency */} + +
+
+ +

{t("idempotency")}

+
+
+
+
+ {idp?.activeKeys ?? 0} +
+
{t("activeDedupKeys")}
+
+
+
+ {idp ? `${(idp.windowMs / 1000).toFixed(0)}s` : "—"} +
+
{t("dedupWindow")}
+
+
+
+
+ )} - - {/* Cache behavior */} - -
-

{t("behavior")}

-
- {t("behaviorDeterministic")} - - {t.rich("behaviorBypass", { - header: () => ( - - X-OmniRoute-No-Cache: true - - ), - })} - - {t("behaviorTwoTier")} - - {t.rich("behaviorTtl", { - envVar: () => ( - - SEMANTIC_CACHE_TTL_MS - - ), - })} - -
-
-
- - {/* Idempotency */} - -
-
- -

{t("idempotency")}

-
-
-
-
{idp?.activeKeys ?? 0}
-
{t("activeDedupKeys")}
-
-
-
- {idp ? `${(idp.windowMs / 1000).toFixed(0)}s` : "—"} -
-
{t("dedupWindow")}
-
-
-
-
)}
diff --git a/src/app/(dashboard)/dashboard/settings/components/CacheSettingsTab.tsx b/src/app/(dashboard)/dashboard/settings/components/CacheSettingsTab.tsx new file mode 100644 index 0000000000..633592a462 --- /dev/null +++ b/src/app/(dashboard)/dashboard/settings/components/CacheSettingsTab.tsx @@ -0,0 +1,191 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Card, Button } from "@/shared/components"; +import { useTranslations } from "next-intl"; + +interface CacheConfig { + semanticCacheEnabled: boolean; + semanticCacheMaxSize: number; + semanticCacheTTL: number; + promptCacheEnabled: boolean; + promptCacheStrategy: "auto" | "system-only" | "manual"; + alwaysPreserveClientCache: "auto" | "always" | "never"; +} + +export default function CacheSettingsTab() { + const t = useTranslations("settings"); + const [config, setConfig] = useState({ + semanticCacheEnabled: true, + semanticCacheMaxSize: 100, + semanticCacheTTL: 1800000, + promptCacheEnabled: true, + promptCacheStrategy: "auto", + alwaysPreserveClientCache: "auto", + }); + const [saving, setSaving] = useState(false); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetch("/api/settings/cache-config") + .then((r) => (r.ok ? r.json() : null)) + .then((data) => { + if (data) setConfig(data); + }) + .catch(() => {}) + .finally(() => setLoading(false)); + }, []); + + const handleSave = async () => { + setSaving(true); + try { + await fetch("/api/settings/cache-config", { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(config), + }); + } finally { + setSaving(false); + } + }; + + if (loading) { + return ( + +

{t("loading")}

+
+ ); + } + + return ( + +

+ cached + {t("cacheSettings")} +

+ +
+ {/* Semantic Cache */} +
+

{t("semanticCache")}

+ + + + + + +
+ + {/* Prompt Cache */} +
+

{t("promptCache")}

+ + + + + + +
+ + {/* Save */} +
+ +
+
+
+ ); +} diff --git a/src/app/(dashboard)/dashboard/settings/page.tsx b/src/app/(dashboard)/dashboard/settings/page.tsx index 7aa69f112c..9d0b261022 100644 --- a/src/app/(dashboard)/dashboard/settings/page.tsx +++ b/src/app/(dashboard)/dashboard/settings/page.tsx @@ -18,6 +18,7 @@ import ModelAliasesTab from "./components/ModelAliasesTab"; import BackgroundDegradationTab from "./components/BackgroundDegradationTab"; import CacheStatsCard from "./components/CacheStatsCard"; +import CacheSettingsTab from "./components/CacheSettingsTab"; import ResilienceTab from "./components/ResilienceTab"; const tabs = [ @@ -89,6 +90,7 @@ export default function SettingsPage() { +
)} diff --git a/src/app/api/cache/entries/route.ts b/src/app/api/cache/entries/route.ts new file mode 100644 index 0000000000..26f30b02a9 --- /dev/null +++ b/src/app/api/cache/entries/route.ts @@ -0,0 +1,95 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getDbInstance } from "@/lib/db/core"; + +interface CacheEntry { + id: string; + signature: string; + model: string; + hit_count: number; + tokens_saved: number; + created_at: string; + expires_at: string; +} + +export async function GET(req: NextRequest) { + try { + const { searchParams } = new URL(req.url); + const page = Math.max(1, parseInt(searchParams.get("page") || "1", 10)); + const limit = Math.min(100, Math.max(1, parseInt(searchParams.get("limit") || "20", 10))); + const search = searchParams.get("search") || ""; + const model = searchParams.get("model") || ""; + const sortBy = searchParams.get("sortBy") || "created_at"; + const sortOrder = searchParams.get("sortOrder") || "desc"; + + const db = getDbInstance(); + const offset = (page - 1) * limit; + + const conditions: string[] = []; + const params: unknown[] = []; + + if (search) { + conditions.push("(signature LIKE ? OR model LIKE ?)"); + params.push(`%${search}%`, `%${search}%`); + } + + if (model) { + conditions.push("model = ?"); + params.push(model); + } + + const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : ""; + + const validSortColumns = ["created_at", "expires_at", "hit_count", "tokens_saved", "model"]; + const orderBy = validSortColumns.includes(sortBy) ? sortBy : "created_at"; + const order = sortOrder === "asc" ? "ASC" : "DESC"; + + const countRow = db + .prepare(`SELECT COUNT(*) as total FROM semantic_cache ${whereClause}`) + .get(...params) as { total: number }; + + const entries = db + .prepare( + `SELECT id, signature, model, hit_count, tokens_saved, created_at, expires_at + FROM semantic_cache ${whereClause} + ORDER BY ${orderBy} ${order} + LIMIT ? OFFSET ?` + ) + .all(...params, limit, offset) as CacheEntry[]; + + return NextResponse.json({ + entries, + pagination: { + page, + limit, + total: countRow?.total || 0, + totalPages: Math.ceil((countRow?.total || 0) / limit), + }, + }); + } catch (error) { + return NextResponse.json({ error: String(error) }, { status: 500 }); + } +} + +export async function DELETE(req: NextRequest) { + try { + const { searchParams } = new URL(req.url); + const signature = searchParams.get("signature"); + const model = searchParams.get("model"); + + const db = getDbInstance(); + + if (signature) { + db.prepare("DELETE FROM semantic_cache WHERE signature = ?").run(signature); + return NextResponse.json({ ok: true, deleted: 1 }); + } + + if (model) { + const result = db.prepare("DELETE FROM semantic_cache WHERE model = ?").run(model); + return NextResponse.json({ ok: true, deleted: result.changes }); + } + + return NextResponse.json({ error: "Provide signature or model parameter" }, { status: 400 }); + } catch (error) { + return NextResponse.json({ error: String(error) }, { status: 500 }); + } +} diff --git a/src/app/api/settings/cache-config/route.ts b/src/app/api/settings/cache-config/route.ts new file mode 100644 index 0000000000..a3fecff41f --- /dev/null +++ b/src/app/api/settings/cache-config/route.ts @@ -0,0 +1,73 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getSettings, updateSettings } from "@/lib/localDb"; +import { isAuthenticated } from "@/shared/utils/apiAuth"; + +const CACHE_CONFIG_KEYS = [ + "semanticCacheEnabled", + "semanticCacheMaxSize", + "semanticCacheTTL", + "promptCacheEnabled", + "promptCacheStrategy", + "alwaysPreserveClientCache", +] as const; + +const DEFAULTS = { + semanticCacheEnabled: true, + semanticCacheMaxSize: 100, + semanticCacheTTL: 1800000, + promptCacheEnabled: true, + promptCacheStrategy: "auto", + alwaysPreserveClientCache: "auto", +}; + +export async function GET(request: NextRequest) { + if (!(await isAuthenticated(request))) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + try { + const settings = await getSettings(); + const config: Record = {}; + for (const key of CACHE_CONFIG_KEYS) { + config[key] = settings[key] ?? DEFAULTS[key]; + } + return NextResponse.json(config); + } catch (error) { + return NextResponse.json({ error: String(error) }, { status: 500 }); + } +} + +export async function PUT(request: NextRequest) { + if (!(await isAuthenticated(request))) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + try { + const body = await request.json(); + const updates: Record = {}; + + if (typeof body.semanticCacheEnabled === "boolean") { + updates.semanticCacheEnabled = body.semanticCacheEnabled; + } + if (typeof body.semanticCacheMaxSize === "number" && body.semanticCacheMaxSize > 0) { + updates.semanticCacheMaxSize = body.semanticCacheMaxSize; + } + if (typeof body.semanticCacheTTL === "number" && body.semanticCacheTTL > 0) { + updates.semanticCacheTTL = body.semanticCacheTTL; + } + if (typeof body.promptCacheEnabled === "boolean") { + updates.promptCacheEnabled = body.promptCacheEnabled; + } + if (["auto", "system-only", "manual"].includes(body.promptCacheStrategy)) { + updates.promptCacheStrategy = body.promptCacheStrategy; + } + if (["auto", "always", "never"].includes(body.alwaysPreserveClientCache)) { + updates.alwaysPreserveClientCache = body.alwaysPreserveClientCache; + } + + await updateSettings(updates); + return NextResponse.json({ ok: true }); + } catch (error) { + return NextResponse.json({ error: String(error) }, { status: 500 }); + } +} diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 745da07ac3..43463d7cba 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -1712,6 +1712,17 @@ "cacheMisses": "Cache Misses", "hitRate": "Hit Rate", "cacheEntries": "Cache Entries", + "cacheSettings": "Cache Settings", + "semanticCache": "Semantic Cache", + "maxEntries": "Max Entries", + "ttlMinutes": "TTL (minutes)", + "promptCache": "Prompt Cache", + "strategy": "Strategy", + "preserveClientCache": "Preserve Client Cache", + "enabled": "Enabled", + "loading": "Loading...", + "saving": "Saving...", + "save": "Save", "circuitBreaker": "Circuit Breaker", "retryPolicy": "Retry Policy", "maxRetries": "Max Retries", @@ -2933,6 +2944,17 @@ "cachedTokensCol": "Cached", "cacheCreation": "Creation", "trend24h": "Cache Trend (24h)", - "cached": "Cached" + "cached": "Cached", + "overview": "Overview", + "entries": "Entries", + "searchEntries": "Search entries...", + "search": "Search", + "loading": "Loading...", + "noEntries": "No cache entries found", + "signature": "Signature", + "model": "Model", + "created": "Created", + "expires": "Expires", + "actions": "Actions" } } diff --git a/src/lib/promptCache/index.ts b/src/lib/promptCache/index.ts new file mode 100644 index 0000000000..f4ee1fd501 --- /dev/null +++ b/src/lib/promptCache/index.ts @@ -0,0 +1 @@ +export { analyzePrefix, shouldInjectCacheControl } from "./prefixAnalyzer"; diff --git a/src/lib/promptCache/prefixAnalyzer.ts b/src/lib/promptCache/prefixAnalyzer.ts new file mode 100644 index 0000000000..43f622c87f --- /dev/null +++ b/src/lib/promptCache/prefixAnalyzer.ts @@ -0,0 +1,77 @@ +import crypto from "crypto"; + +interface Message { + role: string; + content: string | unknown[]; +} + +interface PrefixAnalysis { + prefixEndIdx: number; + prefixHash: string; + prefixTokens: number; + prefixType: "system_only" | "system_and_tools" | "system_tools_history"; + confidence: number; +} + +function normalizeContent(content: string | unknown[]): string { + if (typeof content === "string") return content; + return JSON.stringify(content); +} + +function estimateTokens(text: string): number { + return Math.ceil(text.length / 4); +} + +export function analyzePrefix(messages: Message[]): PrefixAnalysis { + if (!Array.isArray(messages) || messages.length === 0) { + return { + prefixEndIdx: -1, + prefixHash: "", + prefixTokens: 0, + prefixType: "system_only", + confidence: 0, + }; + } + + let prefixEndIdx = -1; + let prefixType: PrefixAnalysis["prefixType"] = "system_only"; + let confidence = 0.5; + + for (let i = 0; i < messages.length; i++) { + const msg = messages[i]; + const role = msg.role || "user"; + + if (role === "system") { + prefixEndIdx = i; + prefixType = "system_only"; + confidence = 0.9; + } else if (role === "tool" || (role === "assistant" && Array.isArray(msg.content))) { + prefixEndIdx = i; + prefixType = "system_and_tools"; + confidence = 0.8; + } else if (role === "assistant") { + prefixEndIdx = i; + prefixType = "system_tools_history"; + confidence = 0.7; + } else { + break; + } + } + + const prefixMessages = messages.slice(0, prefixEndIdx + 1); + const prefixText = prefixMessages.map((m) => normalizeContent(m.content)).join("\n"); + const prefixHash = crypto.createHash("sha256").update(prefixText).digest("hex"); + const prefixTokens = estimateTokens(prefixText); + + return { + prefixEndIdx, + prefixHash, + prefixTokens, + prefixType, + confidence, + }; +} + +export function shouldInjectCacheControl(analysis: PrefixAnalysis, minTokens = 1024): boolean { + return analysis.prefixTokens >= minTokens && analysis.confidence >= 0.7; +} diff --git a/src/lib/semanticCache.ts b/src/lib/semanticCache.ts index d20e4b560e..369d7a0d68 100644 --- a/src/lib/semanticCache.ts +++ b/src/lib/semanticCache.ts @@ -29,6 +29,45 @@ function toNumber(value: unknown, fallback = 0): number { return fallback; } +function ensureCacheMetricsTable() { + try { + const db = getDbInstance(); + db.prepare( + `CREATE TABLE IF NOT EXISTS cache_metrics ( + key TEXT PRIMARY KEY, + value INTEGER NOT NULL DEFAULT 0, + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + )` + ).run(); + db.prepare( + `INSERT OR IGNORE INTO cache_metrics (key, value) VALUES ('hits', 0), ('misses', 0), ('tokens_saved', 0)` + ).run(); + } catch { + // DB not available + } +} + +function incrementMetric(metric: "hits" | "misses" | "tokens_saved", amount = 1) { + try { + const db = getDbInstance(); + db.prepare( + `UPDATE cache_metrics SET value = value + ?, updated_at = datetime('now') WHERE key = ?` + ).run(amount, metric); + } catch { + // DB not available — fall back to in-memory + } +} + +function getMetricValue(metric: string): number { + try { + const db = getDbInstance(); + const row = db.prepare(`SELECT value FROM cache_metrics WHERE key = ?`).get(metric); + return row ? toNumber(asRecord(row).value, 0) : 0; + } catch { + return 0; + } +} + function getHeaderValue( headers: { get?: (name: string) => string | null } | Record | null | undefined, name: string @@ -51,7 +90,6 @@ function getHeaderValue( // ─── Singleton ───────────────── let memoryCache: LRUCache | null = null; -let stats = { hits: 0, misses: 0, tokensSaved: 0 }; function getMemoryCache() { if (!memoryCache) { @@ -60,6 +98,7 @@ function getMemoryCache() { maxBytes: parseInt(process.env.SEMANTIC_CACHE_MAX_BYTES || String(4 * 1024 * 1024), 10), defaultTTL: parseInt(process.env.SEMANTIC_CACHE_TTL_MS || "1800000", 10), }); + ensureCacheMetricsTable(); } return memoryCache; } @@ -108,8 +147,8 @@ export function getCachedResponse(signature) { // 1. Check memory cache const memResult = getMemoryCache().get(signature); if (memResult) { - stats.hits++; - stats.tokensSaved += memResult.tokensSaved || 0; + incrementMetric("hits"); + incrementMetric("tokens_saved", memResult.tokensSaved || 0); return memResult.response; } @@ -126,7 +165,7 @@ export function getCachedResponse(signature) { const record = asRecord(row); const responsePayload = typeof record.response === "string" ? record.response : null; if (!responsePayload) { - stats.misses++; + incrementMetric("misses"); return null; } const parsed = JSON.parse(responsePayload); @@ -141,15 +180,15 @@ export function getCachedResponse(signature) { signature ); - stats.hits++; - stats.tokensSaved += tokensSaved; + incrementMetric("hits"); + incrementMetric("tokens_saved", tokensSaved); return parsed; } } catch { // DB not available — fail open } - stats.misses++; + incrementMetric("misses"); return null; } @@ -280,6 +319,17 @@ export function stopAutoCleanup(): void { } } +export function cleanOldMetrics(retentionDays = 90): number { + try { + const db = getDbInstance(); + const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString(); + const result = db.prepare("DELETE FROM semantic_cache WHERE created_at < ?").run(cutoff); + return result.changes || 0; + } catch { + return 0; + } +} + /** * Clear all cache entries. */ @@ -288,17 +338,12 @@ export function clearCache() { try { const db = getDbInstance(); db.prepare("DELETE FROM semantic_cache").run(); + db.prepare("UPDATE cache_metrics SET value = 0").run(); } catch { // DB not available } - stats = { hits: 0, misses: 0, tokensSaved: 0 }; } -// ─── Stats ───────────────── - -/** - * Get cache statistics. - */ export function getCacheStats() { const memStats = getMemoryCache().getStats(); let dbSize = 0; @@ -312,14 +357,18 @@ export function getCacheStats() { // DB not available } - const total = stats.hits + stats.misses; + const hits = getMetricValue("hits"); + const misses = getMetricValue("misses"); + const tokensSaved = getMetricValue("tokens_saved"); + const total = hits + misses; + return { memoryEntries: memStats.size, dbEntries: dbSize, - hits: stats.hits, - misses: stats.misses, - hitRate: total > 0 ? ((stats.hits / total) * 100).toFixed(1) : "0.0", - tokensSaved: stats.tokensSaved, + hits, + misses, + hitRate: total > 0 ? ((hits / total) * 100).toFixed(1) : "0.0", + tokensSaved, }; } From a5dc5687f8a402f368bd7421fd9dca206b574620 Mon Sep 17 00:00:00 2001 From: Chris Staley Date: Mon, 30 Mar 2026 10:03:10 -0600 Subject: [PATCH 09/18] fix: remove auto-opening OAuth/API key modal on provider detail page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auto-opening the "Add Connection" dialog when navigating to a provider with zero connections was a poor UX pattern. It surprised users who were simply browsing provider details (e.g. after deleting a connection or checking settings). The page already displays a clear empty state with an "Add Connection" button — users should click it when ready. --- .../dashboard/providers/[id]/page.tsx | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx index 66488b657e..6da47e5bdb 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx @@ -802,7 +802,6 @@ export default function ProviderDetailPage() { const { copied, copy } = useCopyToClipboard(); const t = useTranslations("providers"); const notify = useNotificationStore(); - const hasAutoOpened = useRef(false); const userDismissed = useRef(false); const [proxyTarget, setProxyTarget] = useState(null); const [proxyConfig, setProxyConfig] = useState(null); @@ -989,25 +988,12 @@ export default function ProviderDetailPage() { } }, [loading, connections, loadConnProxies]); - // Auto-open Add Connection modal when no connections exist (better UX) - // Only fires once on initial load, not on HMR remounts or after user dismissal - useEffect(() => { - if ( - !loading && - connections.length === 0 && - providerInfo && - !isCompatible && - !hasAutoOpened.current && - !userDismissed.current - ) { - hasAutoOpened.current = true; - if (isOAuth) { - setShowOAuthModal(true); - } else { - setShowAddApiKeyModal(true); - } - } - }, [loading]); // eslint-disable-line react-hooks/exhaustive-deps + // NOTE: Removed auto-open of Add Connection modal when no connections exist. + // The page already shows a clear "No connections yet" empty state with an + // "Add Connection" button. Auto-opening the modal was a poor UX pattern that + // surprised users navigating to a provider detail page — especially when + // re-visiting a provider after deleting a connection. Users should explicitly + // click the button when they're ready to connect. const handleSetAlias = async (modelId, alias, providerAliasOverride = providerAlias) => { const fullModel = `${providerAliasOverride}/${modelId}`; From 89eb8885b199cb88b05599038d9baae74ac3a17b Mon Sep 17 00:00:00 2001 From: Chris Staley Date: Mon, 30 Mar 2026 10:06:30 -0600 Subject: [PATCH 10/18] fix: remove unnecessary comment from previous commit --- src/app/(dashboard)/dashboard/providers/[id]/page.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx index 6da47e5bdb..df28fc9089 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx @@ -988,12 +988,6 @@ export default function ProviderDetailPage() { } }, [loading, connections, loadConnProxies]); - // NOTE: Removed auto-open of Add Connection modal when no connections exist. - // The page already shows a clear "No connections yet" empty state with an - // "Add Connection" button. Auto-opening the modal was a poor UX pattern that - // surprised users navigating to a provider detail page — especially when - // re-visiting a provider after deleting a connection. Users should explicitly - // click the button when they're ready to connect. const handleSetAlias = async (modelId, alias, providerAliasOverride = providerAlias) => { const fullModel = `${providerAliasOverride}/${modelId}`; From d5647eab3396ded5befe16e351e5a82ec12fa8dd Mon Sep 17 00:00:00 2001 From: Chris Staley Date: Mon, 30 Mar 2026 10:33:39 -0600 Subject: [PATCH 11/18] fix: remove dead userDismissed ref after auto-open removal The userDismissed ref was only read by the removed auto-open useEffect. Remove the ref declaration and the three onClose assignments that set it. --- src/app/(dashboard)/dashboard/providers/[id]/page.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx index df28fc9089..ca1a3a1f4b 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx @@ -802,7 +802,6 @@ export default function ProviderDetailPage() { const { copied, copy } = useCopyToClipboard(); const t = useTranslations("providers"); const notify = useNotificationStore(); - const userDismissed = useRef(false); const [proxyTarget, setProxyTarget] = useState(null); const [proxyConfig, setProxyConfig] = useState(null); const [connProxyMap, setConnProxyMap] = useState< @@ -2408,7 +2407,6 @@ export default function ProviderDetailPage() { providerInfo={providerInfo} onSuccess={handleOAuthSuccess} onClose={() => { - userDismissed.current = true; setShowOAuthModal(false); }} /> @@ -2417,7 +2415,6 @@ export default function ProviderDetailPage() { isOpen={showOAuthModal} onSuccess={handleOAuthSuccess} onClose={() => { - userDismissed.current = true; setShowOAuthModal(false); }} /> @@ -2428,7 +2425,6 @@ export default function ProviderDetailPage() { providerInfo={providerInfo} onSuccess={handleOAuthSuccess} onClose={() => { - userDismissed.current = true; setShowOAuthModal(false); }} /> From eaeb28b4e1ef12a8785c0f9e4668199e35f41406 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:51:53 +0000 Subject: [PATCH 12/18] deps: bump the development group with 7 updates Bumps the development group with 7 updates: | Package | From | To | | --- | --- | --- | | [@tailwindcss/postcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-postcss) | `4.2.1` | `4.2.2` | | [@types/keytar](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/keytar) | `4.4.0` | `4.4.2` | | [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `16.1.6` | `16.2.1` | | [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.2.1` | `4.2.2` | | [typescript](https://github.com/microsoft/TypeScript) | `5.9.3` | `6.0.2` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.57.1` | `8.58.0` | | [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.0` | `4.1.2` | Updates `@tailwindcss/postcss` from 4.2.1 to 4.2.2 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.2.2/packages/@tailwindcss-postcss) Updates `@types/keytar` from 4.4.0 to 4.4.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/keytar) Updates `eslint-config-next` from 16.1.6 to 16.2.1 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v16.2.1/packages/eslint-config-next) Updates `tailwindcss` from 4.2.1 to 4.2.2 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.2.2/packages/tailwindcss) Updates `typescript` from 5.9.3 to 6.0.2 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.2) Updates `typescript-eslint` from 8.57.1 to 8.58.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.58.0/packages/typescript-eslint) Updates `vitest` from 4.1.0 to 4.1.2 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.2/packages/vitest) --- updated-dependencies: - dependency-name: "@tailwindcss/postcss" dependency-version: 4.2.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development - dependency-name: "@types/keytar" dependency-version: 4.4.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development - dependency-name: eslint-config-next dependency-version: 16.2.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development - dependency-name: tailwindcss dependency-version: 4.2.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development - dependency-name: typescript dependency-version: 6.0.2 dependency-type: direct:development update-type: version-update:semver-major dependency-group: development - dependency-name: typescript-eslint dependency-version: 8.58.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development - dependency-name: vitest dependency-version: 4.1.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development ... Signed-off-by: dependabot[bot] --- package-lock.json | 1028 ++++++++++++++++++--------------------------- package.json | 2 +- 2 files changed, 414 insertions(+), 616 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5bfe7593fc..c3ff4245ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,7 +69,7 @@ "lint-staged": "^16.2.7", "prettier": "^3.8.1", "tailwindcss": "^4", - "typescript": "^5.9.3", + "typescript": "^6.0.2", "typescript-eslint": "^8.56.0", "vitest": "^4.0.18", "wait-on": "^9.0.4" @@ -2577,20 +2577,22 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", - "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, "node_modules/@next/env": { @@ -2600,9 +2602,9 @@ "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.6.tgz", - "integrity": "sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.1.tgz", + "integrity": "sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA==", "dev": true, "license": "MIT", "dependencies": { @@ -2801,20 +2803,10 @@ "resolved": "open-sse", "link": true }, - "node_modules/@oxc-project/runtime": { - "version": "0.115.0", - "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", - "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@oxc-project/types": { - "version": "0.115.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", - "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", "dev": true, "license": "MIT", "funding": { @@ -4805,9 +4797,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", "cpu": [ "arm64" ], @@ -4822,9 +4814,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", "cpu": [ "arm64" ], @@ -4839,9 +4831,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", - "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", "cpu": [ "x64" ], @@ -4856,9 +4848,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", - "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", "cpu": [ "x64" ], @@ -4873,9 +4865,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", - "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", "cpu": [ "arm" ], @@ -4890,9 +4882,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", "cpu": [ "arm64" ], @@ -4907,9 +4899,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", - "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", "cpu": [ "arm64" ], @@ -4924,9 +4916,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", "cpu": [ "ppc64" ], @@ -4941,9 +4933,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", "cpu": [ "s390x" ], @@ -4958,9 +4950,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", "cpu": [ "x64" ], @@ -4975,9 +4967,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", - "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", "cpu": [ "x64" ], @@ -4992,9 +4984,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", "cpu": [ "arm64" ], @@ -5009,9 +5001,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", - "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", "cpu": [ "wasm32" ], @@ -5026,9 +5018,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", - "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", "cpu": [ "arm64" ], @@ -5043,9 +5035,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", - "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", "cpu": [ "x64" ], @@ -5060,9 +5052,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", - "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", "dev": true, "license": "MIT" }, @@ -5381,49 +5373,49 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", - "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", - "lightningcss": "1.31.1", + "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.2.1" + "tailwindcss": "4.2.2" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz", - "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", "dev": true, "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.1", - "@tailwindcss/oxide-darwin-arm64": "4.2.1", - "@tailwindcss/oxide-darwin-x64": "4.2.1", - "@tailwindcss/oxide-freebsd-x64": "4.2.1", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", - "@tailwindcss/oxide-linux-x64-musl": "4.2.1", - "@tailwindcss/oxide-wasm32-wasi": "4.2.1", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" + "@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" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz", - "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", "cpu": [ "arm64" ], @@ -5438,9 +5430,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz", - "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", "cpu": [ "arm64" ], @@ -5455,9 +5447,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz", - "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", "cpu": [ "x64" ], @@ -5472,9 +5464,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz", - "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", "cpu": [ "x64" ], @@ -5489,9 +5481,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz", - "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", "cpu": [ "arm" ], @@ -5506,9 +5498,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz", - "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", "cpu": [ "arm64" ], @@ -5523,9 +5515,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz", - "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", "cpu": [ "arm64" ], @@ -5540,9 +5532,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz", - "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", "cpu": [ "x64" ], @@ -5557,9 +5549,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz", - "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", "cpu": [ "x64" ], @@ -5574,9 +5566,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz", - "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -5603,10 +5595,74 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.8.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.8.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", - "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", "cpu": [ "arm64" ], @@ -5621,9 +5677,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz", - "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", "cpu": [ "x64" ], @@ -5638,17 +5694,17 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.1.tgz", - "integrity": "sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz", + "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.2.1", - "@tailwindcss/oxide": "4.2.1", + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", "postcss": "^8.5.6", - "tailwindcss": "4.2.1" + "tailwindcss": "4.2.2" } }, "node_modules/@tybys/wasm-util": { @@ -6064,11 +6120,15 @@ "peer": true }, "node_modules/@types/keytar": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@types/keytar/-/keytar-4.4.0.tgz", - "integrity": "sha512-cq/NkUUy6rpWD8n7PweNQQBpw2o0cf5v6fbkUVEpOB9VzzIvyPvSEId1/goIj+MciW2v1Lw5mRimKO01XgE9EA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@types/keytar/-/keytar-4.4.2.tgz", + "integrity": "sha512-xtQcDj9ruGnMwvSu1E2BH4SFa5Dv2PvSPd0CKEBLN5hEj/v5YpXJY+B6hAfuKIbvEomD7vJTc/P1s1xPNh2kRw==", + "deprecated": "This is a stub types definition. keytar provides its own type definitions, so you do not need this installed.", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "keytar": "*" + } }, "node_modules/@types/mdast": { "version": "4.0.4", @@ -6148,20 +6208,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", - "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/type-utils": "8.57.1", - "@typescript-eslint/utils": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6171,9 +6231,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.1", + "@typescript-eslint/parser": "^8.58.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -6187,16 +6247,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz", - "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3" }, "engines": { @@ -6208,18 +6268,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", - "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.1", - "@typescript-eslint/types": "^8.57.1", + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", "debug": "^4.4.3" }, "engines": { @@ -6230,18 +6290,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", - "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1" + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6252,9 +6312,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", - "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", "dev": true, "license": "MIT", "engines": { @@ -6265,21 +6325,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz", - "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/utils": "8.57.1", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6290,13 +6350,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", - "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", "dev": true, "license": "MIT", "engines": { @@ -6308,21 +6368,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", - "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.1", - "@typescript-eslint/tsconfig-utils": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6332,7 +6392,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { @@ -6359,13 +6419,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -6388,16 +6448,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz", - "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1" + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6408,17 +6468,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", - "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -6763,31 +6823,31 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz", - "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", + "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", "chai": "^6.2.2", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz", - "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz", + "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.0", + "@vitest/spy": "4.1.2", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -6796,7 +6856,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -6808,26 +6868,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz", - "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", + "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", - "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz", + "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.0", + "@vitest/utils": "4.1.2", "pathe": "^2.0.3" }, "funding": { @@ -6835,14 +6895,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", - "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz", + "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/pretty-format": "4.1.2", + "@vitest/utils": "4.1.2", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -6851,9 +6911,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", - "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz", + "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", "dev": true, "license": "MIT", "funding": { @@ -6861,15 +6921,15 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", - "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz", + "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", + "@vitest/pretty-format": "4.1.2", "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -9309,9 +9369,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", - "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", "dev": true, "license": "MIT", "dependencies": { @@ -9712,13 +9772,13 @@ } }, "node_modules/eslint-config-next": { - "version": "16.1.6", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.6.tgz", - "integrity": "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.1.tgz", + "integrity": "sha512-qhabwjQZ1Mk53XzXvmogf8KQ0tG0CQXF0CZ56+2/lVhmObgmaqj7x5A1DSrWdZd3kwI7GTPGUjFne+krRxYmFg==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.1.6", + "@next/eslint-plugin-next": "16.2.1", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", @@ -12732,9 +12792,9 @@ } }, "node_modules/lightningcss": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", - "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -12748,23 +12808,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.31.1", - "lightningcss-darwin-arm64": "1.31.1", - "lightningcss-darwin-x64": "1.31.1", - "lightningcss-freebsd-x64": "1.31.1", - "lightningcss-linux-arm-gnueabihf": "1.31.1", - "lightningcss-linux-arm64-gnu": "1.31.1", - "lightningcss-linux-arm64-musl": "1.31.1", - "lightningcss-linux-x64-gnu": "1.31.1", - "lightningcss-linux-x64-musl": "1.31.1", - "lightningcss-win32-arm64-msvc": "1.31.1", - "lightningcss-win32-x64-msvc": "1.31.1" + "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" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz", - "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", "cpu": [ "arm64" ], @@ -12783,9 +12843,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz", - "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", "cpu": [ "arm64" ], @@ -12804,9 +12864,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz", - "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" ], @@ -12825,9 +12885,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz", - "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", "cpu": [ "x64" ], @@ -12846,9 +12906,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz", - "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", "cpu": [ "arm" ], @@ -12867,9 +12927,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz", - "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", "cpu": [ "arm64" ], @@ -12888,9 +12948,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz", - "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", "cpu": [ "arm64" ], @@ -12909,9 +12969,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz", - "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" ], @@ -12930,9 +12990,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz", - "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" ], @@ -12951,9 +13011,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz", - "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", "cpu": [ "arm64" ], @@ -12972,9 +13032,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz", - "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" ], @@ -17131,14 +17191,14 @@ "peer": true }, "node_modules/rolldown": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", - "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.115.0", - "@rolldown/pluginutils": "1.0.0-rc.9" + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" }, "bin": { "rolldown": "bin/cli.mjs" @@ -17147,21 +17207,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.9", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", - "@rolldown/binding-darwin-x64": "1.0.0-rc.9", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" } }, "node_modules/roughjs": { @@ -18344,16 +18404,16 @@ "peer": true }, "node_modules/tailwindcss": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", - "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", "dev": true, "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", "dev": true, "license": "MIT", "engines": { @@ -18616,9 +18676,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -18836,9 +18896,9 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -18850,16 +18910,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.1.tgz", - "integrity": "sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.1", - "@typescript-eslint/parser": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/utils": "8.57.1" + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -18870,7 +18930,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/ufo": { @@ -19334,17 +19394,16 @@ } }, "node_modules/vite": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz", - "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/runtime": "0.115.0", "lightningcss": "^1.32.0", - "picomatch": "^4.0.3", + "picomatch": "^4.0.4", "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.9", + "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "bin": { @@ -19361,7 +19420,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.0.0-alpha.31", + "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -19427,267 +19486,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/vite/node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "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" - } - }, - "node_modules/vite/node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/vite/node_modules/picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", @@ -19702,19 +19500,19 @@ } }, "node_modules/vitest": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", - "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz", + "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.0", - "@vitest/mocker": "4.1.0", - "@vitest/pretty-format": "4.1.0", - "@vitest/runner": "4.1.0", - "@vitest/snapshot": "4.1.0", - "@vitest/spy": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/expect": "4.1.2", + "@vitest/mocker": "4.1.2", + "@vitest/pretty-format": "4.1.2", + "@vitest/runner": "4.1.2", + "@vitest/snapshot": "4.1.2", + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -19725,8 +19523,8 @@ "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -19742,13 +19540,13 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.0", - "@vitest/browser-preview": "4.1.0", - "@vitest/browser-webdriverio": "4.1.0", - "@vitest/ui": "4.1.0", + "@vitest/browser-playwright": "4.1.2", + "@vitest/browser-preview": "4.1.2", + "@vitest/browser-webdriverio": "4.1.2", + "@vitest/ui": "4.1.2", "happy-dom": "*", "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { diff --git a/package.json b/package.json index d4574106a1..20cb8368a9 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "lint-staged": "^16.2.7", "prettier": "^3.8.1", "tailwindcss": "^4", - "typescript": "^5.9.3", + "typescript": "^6.0.2", "typescript-eslint": "^8.56.0", "vitest": "^4.0.18", "wait-on": "^9.0.4" From 2c12f18b4425481486c998e1dbb72d70a32b46ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:50:12 +0000 Subject: [PATCH 13/18] deps: bump the production group with 8 updates Bumps the production group with 8 updates: | Package | From | To | | --- | --- | --- | | [@lobehub/icons](https://github.com/lobehub/lobe-icons) | `5.0.1` | `5.2.0` | | [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) | `1.27.1` | `1.29.0` | | [@swc/helpers](https://github.com/swc-project/swc/tree/HEAD/packages/helpers) | `0.5.19` | `0.5.20` | | [jose](https://github.com/panva/jose) | `6.2.1` | `6.2.2` | | [next](https://github.com/vercel/next.js) | `16.1.7` | `16.2.1` | | [recharts](https://github.com/recharts/recharts) | `3.8.0` | `3.8.1` | | [undici](https://github.com/nodejs/undici) | `7.24.4` | `7.24.6` | | [wreq-js](https://github.com/sqdshguy/wreq-js) | `2.2.0` | `2.2.2` | Updates `@lobehub/icons` from 5.0.1 to 5.2.0 - [Release notes](https://github.com/lobehub/lobe-icons/releases) - [Changelog](https://github.com/lobehub/lobe-icons/blob/master/CHANGELOG.md) - [Commits](https://github.com/lobehub/lobe-icons/compare/v5.0.1...v5.2.0) Updates `@modelcontextprotocol/sdk` from 1.27.1 to 1.29.0 - [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases) - [Commits](https://github.com/modelcontextprotocol/typescript-sdk/compare/v1.27.1...v1.29.0) Updates `@swc/helpers` from 0.5.19 to 0.5.20 - [Release notes](https://github.com/swc-project/swc/releases) - [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG-CORE.md) - [Commits](https://github.com/swc-project/swc/commits/HEAD/packages/helpers) Updates `jose` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/panva/jose/releases) - [Changelog](https://github.com/panva/jose/blob/main/CHANGELOG.md) - [Commits](https://github.com/panva/jose/compare/v6.2.1...v6.2.2) Updates `next` from 16.1.7 to 16.2.1 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v16.1.7...v16.2.1) Updates `recharts` from 3.8.0 to 3.8.1 - [Release notes](https://github.com/recharts/recharts/releases) - [Changelog](https://github.com/recharts/recharts/blob/main/CHANGELOG.md) - [Commits](https://github.com/recharts/recharts/compare/v3.8.0...v3.8.1) Updates `undici` from 7.24.4 to 7.24.6 - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v7.24.4...v7.24.6) Updates `wreq-js` from 2.2.0 to 2.2.2 - [Release notes](https://github.com/sqdshguy/wreq-js/releases) - [Commits](https://github.com/sqdshguy/wreq-js/compare/v2.2.0...v2.2.2) --- updated-dependencies: - dependency-name: "@lobehub/icons" dependency-version: 5.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: "@modelcontextprotocol/sdk" dependency-version: 1.29.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: "@swc/helpers" dependency-version: 0.5.20 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: jose dependency-version: 6.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: next dependency-version: 16.2.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production - dependency-name: recharts dependency-version: 3.8.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: undici dependency-version: 7.24.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production - dependency-name: wreq-js dependency-version: 2.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production ... Signed-off-by: dependabot[bot] --- package-lock.json | 124 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3ff4245ba..76f45c9831 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@lobehub/icons": "^5.0.1", "@modelcontextprotocol/sdk": "^1.27.1", "@monaco-editor/react": "^4.7.0", - "@swc/helpers": "0.5.19", + "@swc/helpers": "0.5.20", "bcryptjs": "^3.0.3", "better-sqlite3": "^12.6.2", "bottleneck": "^2.19.5", @@ -2278,9 +2278,9 @@ "peer": true }, "node_modules/@lobehub/icons": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@lobehub/icons/-/icons-5.0.1.tgz", - "integrity": "sha512-Wp9KINavihoWtTOHqHFj80GaKOrIRnOT0S7q5JxMRjijv4CEzbyEkJ2ILJlTz8zstRUfx+HvCVAKUv/Mbdp00Q==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@lobehub/icons/-/icons-5.2.0.tgz", + "integrity": "sha512-dDp/5wQWuhfAItrrdXmIYPQMFFqlE+0tk3EplivEm3HDf33W4al+7Y815y/MPIACHhg9fNCu8+HKo0Sn9JVOaw==", "license": "MIT", "workspaces": [ "packages/*" @@ -2492,9 +2492,9 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.27.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz", - "integrity": "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==", + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.9", @@ -2596,9 +2596,9 @@ } }, "node_modules/@next/env": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.7.tgz", - "integrity": "sha512-rJJbIdJB/RQr2F1nylZr/PJzamvNNhfr3brdKP6s/GW850jbtR70QlSfFselvIBbcPUOlQwBakexjFzqLzF6pg==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.1.tgz", + "integrity": "sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -2612,9 +2612,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.7.tgz", - "integrity": "sha512-b2wWIE8sABdyafc4IM8r5Y/dS6kD80JRtOGrUiKTsACFQfWWgUQ2NwoUX1yjFMXVsAwcQeNpnucF2ZrujsBBPg==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.1.tgz", + "integrity": "sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==", "cpu": [ "arm64" ], @@ -2628,9 +2628,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.7.tgz", - "integrity": "sha512-zcnVaaZulS1WL0Ss38R5Q6D2gz7MtBu8GZLPfK+73D/hp4GFMrC2sudLky1QibfV7h6RJBJs/gOFvYP0X7UVlQ==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.1.tgz", + "integrity": "sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==", "cpu": [ "x64" ], @@ -2644,9 +2644,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.7.tgz", - "integrity": "sha512-2ant89Lux/Q3VyC8vNVg7uBaFVP9SwoK2jJOOR0L8TQnX8CAYnh4uctAScy2Hwj2dgjVHqHLORQZJ2wH6VxhSQ==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.1.tgz", + "integrity": "sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==", "cpu": [ "arm64" ], @@ -2660,9 +2660,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.7.tgz", - "integrity": "sha512-uufcze7LYv0FQg9GnNeZ3/whYfo+1Q3HnQpm16o6Uyi0OVzLlk2ZWoY7j07KADZFY8qwDbsmFnMQP3p3+Ftprw==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.1.tgz", + "integrity": "sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==", "cpu": [ "arm64" ], @@ -2676,9 +2676,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.7.tgz", - "integrity": "sha512-KWVf2gxYvHtvuT+c4MBOGxuse5TD7DsMFYSxVxRBnOzok/xryNeQSjXgxSv9QpIVlaGzEn/pIuI6Koosx8CGWA==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.1.tgz", + "integrity": "sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==", "cpu": [ "x64" ], @@ -2692,9 +2692,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.7.tgz", - "integrity": "sha512-HguhaGwsGr1YAGs68uRKc4aGWxLET+NevJskOcCAwXbwj0fYX0RgZW2gsOCzr9S11CSQPIkxmoSbuVaBp4Z3dA==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.1.tgz", + "integrity": "sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==", "cpu": [ "x64" ], @@ -2708,9 +2708,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.7.tgz", - "integrity": "sha512-S0n3KrDJokKTeFyM/vGGGR8+pCmXYrjNTk2ZozOL1C/JFdfUIL9O1ATaJOl5r2POe56iRChbsszrjMAdWSv7kQ==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.1.tgz", + "integrity": "sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==", "cpu": [ "arm64" ], @@ -2724,9 +2724,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.7.tgz", - "integrity": "sha512-mwgtg8CNZGYm06LeEd+bNnOUfwOyNem/rOiP14Lsz+AnUY92Zq/LXwtebtUiaeVkhbroRCQ0c8GlR4UT1U+0yg==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.1.tgz", + "integrity": "sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==", "cpu": [ "x64" ], @@ -5355,9 +5355,9 @@ "license": "Apache-2.0" }, "node_modules/@swc/helpers": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.19.tgz", - "integrity": "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==", + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.20.tgz", + "integrity": "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" @@ -12500,9 +12500,9 @@ } }, "node_modules/jose": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.1.tgz", - "integrity": "sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -14892,12 +14892,12 @@ } }, "node_modules/next": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/next/-/next-16.1.7.tgz", - "integrity": "sha512-WM0L7WrSvKwoLegLYr6V+mz+RIofqQgVAfHhMp9a88ms0cFX8iX9ew+snpWlSBwpkURJOUdvCEt3uLl3NNzvWg==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.1.tgz", + "integrity": "sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==", "license": "MIT", "dependencies": { - "@next/env": "16.1.7", + "@next/env": "16.2.1", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -14911,15 +14911,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.1.7", - "@next/swc-darwin-x64": "16.1.7", - "@next/swc-linux-arm64-gnu": "16.1.7", - "@next/swc-linux-arm64-musl": "16.1.7", - "@next/swc-linux-x64-gnu": "16.1.7", - "@next/swc-linux-x64-musl": "16.1.7", - "@next/swc-win32-arm64-msvc": "16.1.7", - "@next/swc-win32-x64-msvc": "16.1.7", - "sharp": "^0.34.4" + "@next/swc-darwin-arm64": "16.2.1", + "@next/swc-darwin-x64": "16.2.1", + "@next/swc-linux-arm64-gnu": "16.2.1", + "@next/swc-linux-arm64-musl": "16.2.1", + "@next/swc-linux-x64-gnu": "16.2.1", + "@next/swc-linux-x64-musl": "16.2.1", + "@next/swc-win32-arm64-msvc": "16.2.1", + "@next/swc-win32-x64-msvc": "16.2.1", + "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -16641,9 +16641,9 @@ } }, "node_modules/recharts": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.0.tgz", - "integrity": "sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.1.tgz", + "integrity": "sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg==", "license": "MIT", "workspaces": [ "www" @@ -18960,9 +18960,9 @@ } }, "node_modules/undici": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", - "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.6.tgz", + "integrity": "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -19874,9 +19874,9 @@ "license": "ISC" }, "node_modules/wreq-js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/wreq-js/-/wreq-js-2.2.0.tgz", - "integrity": "sha512-lXW1/bvdPTpFMdfBftkJIp6OzxkAqAON4dlrKrmaFNT86eu60VCEVmEdK3nWY1ZyiEZ6IXQPRrc1uXG394BoBA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/wreq-js/-/wreq-js-2.2.2.tgz", + "integrity": "sha512-iNcPyvVg14nWtHMzN595GDH1ELB1CDfVUV4s+AfSrP2go01/LYVBCkx4AdyMNAup4myQEiNBBmRI2Co2MKsFPQ==", "cpu": [ "x64", "arm64" diff --git a/package.json b/package.json index 20cb8368a9..9724a9d5fe 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "@lobehub/icons": "^5.0.1", "@modelcontextprotocol/sdk": "^1.27.1", "@monaco-editor/react": "^4.7.0", - "@swc/helpers": "0.5.19", + "@swc/helpers": "0.5.20", "bcryptjs": "^3.0.3", "better-sqlite3": "^12.6.2", "bottleneck": "^2.19.5", From df23162e9d6a915485f9dad91eadb518493367d5 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Mon, 30 Mar 2026 17:35:51 -0300 Subject: [PATCH 14/18] chore(release): v3.3.5 - all changes in ONE commit --- CHANGELOG.md | 16 + docs/openapi.yaml | 2 +- package-lock.json | 1156 ++++++++++++++++++++++++++------------------- package.json | 6 +- 4 files changed, 699 insertions(+), 481 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcbc2fa784..46196d0690 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,22 @@ ## [Unreleased] --- + +## [3.3.5] - 2026-03-30 + +### ✨ New Features + +- **Gemini Quota Tracking:** Added real-time Gemini CLI quota tracking via the `retrieveUserQuota` API (PR #825) +- **Cache Dashboard:** Enhanced the Cache Dashboard to display prompt cache metrics, 24h trends, and estimated cost savings (PR #824) + +### 🐛 Bug Fixes + +- **Token Accounting:** Included prompt cache tokens safely in historical usage inputs calculations for correct quota deductions (PR #822) +- **User Experience:** Removed invasive auto-opening OAuth modal loops on barren provider detailed pages (PR #820) +- **Dependency Updates:** Bumped and locked down dependencies for development and production trees including Next.js 16.2.1, Recharts, and TailwindCSS 4.2.2 (PR #826, #827) + +--- + ## [3.3.4] - 2026-03-30 ### ✨ New Features diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 11d7f50a89..4d8f27b1db 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: OmniRoute API - version: 3.3.4 + version: 3.3.5 description: | OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible endpoint that routes requests to multiple AI providers with load balancing, diff --git a/package-lock.json b/package-lock.json index 76f45c9831..3bf732feac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "omniroute", - "version": "3.3.4", + "version": "3.3.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "omniroute", - "version": "3.3.4", + "version": "3.3.5", "hasInstallScript": true, "license": "MIT", "workspaces": [ @@ -16,7 +16,7 @@ "@lobehub/icons": "^5.0.1", "@modelcontextprotocol/sdk": "^1.27.1", "@monaco-editor/react": "^4.7.0", - "@swc/helpers": "0.5.20", + "@swc/helpers": "0.5.19", "bcryptjs": "^3.0.3", "better-sqlite3": "^12.6.2", "bottleneck": "^2.19.5", @@ -69,7 +69,7 @@ "lint-staged": "^16.2.7", "prettier": "^3.8.1", "tailwindcss": "^4", - "typescript": "^6.0.2", + "typescript": "^5.9.3", "typescript-eslint": "^8.56.0", "vitest": "^4.0.18", "wait-on": "^9.0.4" @@ -2278,9 +2278,9 @@ "peer": true }, "node_modules/@lobehub/icons": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@lobehub/icons/-/icons-5.2.0.tgz", - "integrity": "sha512-dDp/5wQWuhfAItrrdXmIYPQMFFqlE+0tk3EplivEm3HDf33W4al+7Y815y/MPIACHhg9fNCu8+HKo0Sn9JVOaw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@lobehub/icons/-/icons-5.0.1.tgz", + "integrity": "sha512-Wp9KINavihoWtTOHqHFj80GaKOrIRnOT0S7q5JxMRjijv4CEzbyEkJ2ILJlTz8zstRUfx+HvCVAKUv/Mbdp00Q==", "license": "MIT", "workspaces": [ "packages/*" @@ -2492,9 +2492,9 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "version": "1.27.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz", + "integrity": "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==", "license": "MIT", "dependencies": { "@hono/node-server": "^1.19.9", @@ -2577,34 +2577,32 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", - "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", "dev": true, "license": "MIT", "optional": true, "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" } }, "node_modules/@next/env": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.1.tgz", - "integrity": "sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==", + "version": "16.1.7", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.7.tgz", + "integrity": "sha512-rJJbIdJB/RQr2F1nylZr/PJzamvNNhfr3brdKP6s/GW850jbtR70QlSfFselvIBbcPUOlQwBakexjFzqLzF6pg==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.1.tgz", - "integrity": "sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.6.tgz", + "integrity": "sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2612,9 +2610,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.1.tgz", - "integrity": "sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==", + "version": "16.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.7.tgz", + "integrity": "sha512-b2wWIE8sABdyafc4IM8r5Y/dS6kD80JRtOGrUiKTsACFQfWWgUQ2NwoUX1yjFMXVsAwcQeNpnucF2ZrujsBBPg==", "cpu": [ "arm64" ], @@ -2628,9 +2626,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.1.tgz", - "integrity": "sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==", + "version": "16.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.7.tgz", + "integrity": "sha512-zcnVaaZulS1WL0Ss38R5Q6D2gz7MtBu8GZLPfK+73D/hp4GFMrC2sudLky1QibfV7h6RJBJs/gOFvYP0X7UVlQ==", "cpu": [ "x64" ], @@ -2644,9 +2642,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.1.tgz", - "integrity": "sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==", + "version": "16.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.7.tgz", + "integrity": "sha512-2ant89Lux/Q3VyC8vNVg7uBaFVP9SwoK2jJOOR0L8TQnX8CAYnh4uctAScy2Hwj2dgjVHqHLORQZJ2wH6VxhSQ==", "cpu": [ "arm64" ], @@ -2660,9 +2658,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.1.tgz", - "integrity": "sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==", + "version": "16.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.7.tgz", + "integrity": "sha512-uufcze7LYv0FQg9GnNeZ3/whYfo+1Q3HnQpm16o6Uyi0OVzLlk2ZWoY7j07KADZFY8qwDbsmFnMQP3p3+Ftprw==", "cpu": [ "arm64" ], @@ -2676,9 +2674,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.1.tgz", - "integrity": "sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==", + "version": "16.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.7.tgz", + "integrity": "sha512-KWVf2gxYvHtvuT+c4MBOGxuse5TD7DsMFYSxVxRBnOzok/xryNeQSjXgxSv9QpIVlaGzEn/pIuI6Koosx8CGWA==", "cpu": [ "x64" ], @@ -2692,9 +2690,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.1.tgz", - "integrity": "sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==", + "version": "16.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.7.tgz", + "integrity": "sha512-HguhaGwsGr1YAGs68uRKc4aGWxLET+NevJskOcCAwXbwj0fYX0RgZW2gsOCzr9S11CSQPIkxmoSbuVaBp4Z3dA==", "cpu": [ "x64" ], @@ -2708,9 +2706,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.1.tgz", - "integrity": "sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==", + "version": "16.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.7.tgz", + "integrity": "sha512-S0n3KrDJokKTeFyM/vGGGR8+pCmXYrjNTk2ZozOL1C/JFdfUIL9O1ATaJOl5r2POe56iRChbsszrjMAdWSv7kQ==", "cpu": [ "arm64" ], @@ -2724,9 +2722,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.1.tgz", - "integrity": "sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==", + "version": "16.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.7.tgz", + "integrity": "sha512-mwgtg8CNZGYm06LeEd+bNnOUfwOyNem/rOiP14Lsz+AnUY92Zq/LXwtebtUiaeVkhbroRCQ0c8GlR4UT1U+0yg==", "cpu": [ "x64" ], @@ -2803,10 +2801,20 @@ "resolved": "open-sse", "link": true }, + "node_modules/@oxc-project/runtime": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", + "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", + "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", "dev": true, "license": "MIT", "funding": { @@ -4797,9 +4805,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", "cpu": [ "arm64" ], @@ -4814,9 +4822,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", "cpu": [ "arm64" ], @@ -4831,9 +4839,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", "cpu": [ "x64" ], @@ -4848,9 +4856,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", "cpu": [ "x64" ], @@ -4865,9 +4873,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", - "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", + "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", "cpu": [ "arm" ], @@ -4882,9 +4890,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", "cpu": [ "arm64" ], @@ -4899,9 +4907,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", "cpu": [ "arm64" ], @@ -4916,9 +4924,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", "cpu": [ "ppc64" ], @@ -4933,9 +4941,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", "cpu": [ "s390x" ], @@ -4950,9 +4958,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", "cpu": [ "x64" ], @@ -4967,9 +4975,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", "cpu": [ "x64" ], @@ -4984,9 +4992,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", "cpu": [ "arm64" ], @@ -5001,9 +5009,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", - "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", + "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", "cpu": [ "wasm32" ], @@ -5018,9 +5026,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", "cpu": [ "arm64" ], @@ -5035,9 +5043,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", "cpu": [ "x64" ], @@ -5052,9 +5060,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", - "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", + "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", "dev": true, "license": "MIT" }, @@ -5355,9 +5363,9 @@ "license": "Apache-2.0" }, "node_modules/@swc/helpers": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.20.tgz", - "integrity": "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.19.tgz", + "integrity": "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" @@ -5373,49 +5381,49 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", - "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", + "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", - "lightningcss": "1.32.0", + "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.2.2" + "tailwindcss": "4.2.1" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", - "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz", + "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==", "dev": true, "license": "MIT", "engines": { "node": ">= 20" }, "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" + "@tailwindcss/oxide-android-arm64": "4.2.1", + "@tailwindcss/oxide-darwin-arm64": "4.2.1", + "@tailwindcss/oxide-darwin-x64": "4.2.1", + "@tailwindcss/oxide-freebsd-x64": "4.2.1", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", + "@tailwindcss/oxide-linux-x64-musl": "4.2.1", + "@tailwindcss/oxide-wasm32-wasi": "4.2.1", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", - "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz", + "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==", "cpu": [ "arm64" ], @@ -5430,9 +5438,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", - "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz", + "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==", "cpu": [ "arm64" ], @@ -5447,9 +5455,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", - "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz", + "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==", "cpu": [ "x64" ], @@ -5464,9 +5472,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", - "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz", + "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==", "cpu": [ "x64" ], @@ -5481,9 +5489,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", - "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz", + "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==", "cpu": [ "arm" ], @@ -5498,9 +5506,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", - "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz", + "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==", "cpu": [ "arm64" ], @@ -5515,9 +5523,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", - "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz", + "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==", "cpu": [ "arm64" ], @@ -5532,9 +5540,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", - "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz", + "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==", "cpu": [ "x64" ], @@ -5549,9 +5557,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", - "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz", + "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==", "cpu": [ "x64" ], @@ -5566,9 +5574,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", - "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz", + "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -5595,74 +5603,10 @@ "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.8.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.8.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", - "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", + "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==", "cpu": [ "arm64" ], @@ -5677,9 +5621,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", - "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz", + "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==", "cpu": [ "x64" ], @@ -5694,17 +5638,17 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz", - "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.1.tgz", + "integrity": "sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.2.2", - "@tailwindcss/oxide": "4.2.2", + "@tailwindcss/node": "4.2.1", + "@tailwindcss/oxide": "4.2.1", "postcss": "^8.5.6", - "tailwindcss": "4.2.2" + "tailwindcss": "4.2.1" } }, "node_modules/@tybys/wasm-util": { @@ -6120,15 +6064,11 @@ "peer": true }, "node_modules/@types/keytar": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@types/keytar/-/keytar-4.4.2.tgz", - "integrity": "sha512-xtQcDj9ruGnMwvSu1E2BH4SFa5Dv2PvSPd0CKEBLN5hEj/v5YpXJY+B6hAfuKIbvEomD7vJTc/P1s1xPNh2kRw==", - "deprecated": "This is a stub types definition. keytar provides its own type definitions, so you do not need this installed.", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/keytar/-/keytar-4.4.0.tgz", + "integrity": "sha512-cq/NkUUy6rpWD8n7PweNQQBpw2o0cf5v6fbkUVEpOB9VzzIvyPvSEId1/goIj+MciW2v1Lw5mRimKO01XgE9EA==", "dev": true, - "license": "MIT", - "dependencies": { - "keytar": "*" - } + "license": "MIT" }, "node_modules/@types/mdast": { "version": "4.0.4", @@ -6208,20 +6148,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", - "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", + "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/type-utils": "8.58.0", - "@typescript-eslint/utils": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/type-utils": "8.57.1", + "@typescript-eslint/utils": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6231,9 +6171,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.58.0", + "@typescript-eslint/parser": "^8.57.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -6247,16 +6187,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", - "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz", + "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", "debug": "^4.4.3" }, "engines": { @@ -6268,18 +6208,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", - "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", + "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.58.0", - "@typescript-eslint/types": "^8.58.0", + "@typescript-eslint/tsconfig-utils": "^8.57.1", + "@typescript-eslint/types": "^8.57.1", "debug": "^4.4.3" }, "engines": { @@ -6290,18 +6230,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", - "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", + "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0" + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6312,9 +6252,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", - "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", + "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", "dev": true, "license": "MIT", "engines": { @@ -6325,21 +6265,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", - "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz", + "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/utils": "8.57.1", "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6350,13 +6290,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", - "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", + "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", "dev": true, "license": "MIT", "engines": { @@ -6368,21 +6308,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", - "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", + "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.58.0", - "@typescript-eslint/tsconfig-utils": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/project-service": "8.57.1", + "@typescript-eslint/tsconfig-utils": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6392,7 +6332,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { @@ -6419,13 +6359,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.2" }, "engines": { "node": "18 || 20 || >=22" @@ -6448,16 +6388,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", - "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz", + "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0" + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6468,17 +6408,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", - "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", + "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/types": "8.57.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -6823,31 +6763,31 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", - "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz", + "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/spy": "4.1.0", + "@vitest/utils": "4.1.0", "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz", - "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz", + "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.2", + "@vitest/spy": "4.1.0", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -6856,7 +6796,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -6868,26 +6808,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", - "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz", + "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.1.0" + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz", - "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", + "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.2", + "@vitest/utils": "4.1.0", "pathe": "^2.0.3" }, "funding": { @@ -6895,14 +6835,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz", - "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", + "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/pretty-format": "4.1.0", + "@vitest/utils": "4.1.0", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -6911,9 +6851,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz", - "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", + "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", "dev": true, "license": "MIT", "funding": { @@ -6921,15 +6861,15 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz", - "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", + "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.2", + "@vitest/pretty-format": "4.1.0", "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" @@ -9369,9 +9309,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dev": true, "license": "MIT", "dependencies": { @@ -9772,13 +9712,13 @@ } }, "node_modules/eslint-config-next": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.1.tgz", - "integrity": "sha512-qhabwjQZ1Mk53XzXvmogf8KQ0tG0CQXF0CZ56+2/lVhmObgmaqj7x5A1DSrWdZd3kwI7GTPGUjFne+krRxYmFg==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.6.tgz", + "integrity": "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.2.1", + "@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", @@ -12500,9 +12440,9 @@ } }, "node_modules/jose": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", - "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.1.tgz", + "integrity": "sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -12792,9 +12732,9 @@ } }, "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", + "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -12808,23 +12748,23 @@ "url": "https://opencollective.com/parcel" }, "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" + "lightningcss-android-arm64": "1.31.1", + "lightningcss-darwin-arm64": "1.31.1", + "lightningcss-darwin-x64": "1.31.1", + "lightningcss-freebsd-x64": "1.31.1", + "lightningcss-linux-arm-gnueabihf": "1.31.1", + "lightningcss-linux-arm64-gnu": "1.31.1", + "lightningcss-linux-arm64-musl": "1.31.1", + "lightningcss-linux-x64-gnu": "1.31.1", + "lightningcss-linux-x64-musl": "1.31.1", + "lightningcss-win32-arm64-msvc": "1.31.1", + "lightningcss-win32-x64-msvc": "1.31.1" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz", + "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==", "cpu": [ "arm64" ], @@ -12843,9 +12783,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz", + "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==", "cpu": [ "arm64" ], @@ -12864,9 +12804,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz", + "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==", "cpu": [ "x64" ], @@ -12885,9 +12825,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz", + "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==", "cpu": [ "x64" ], @@ -12906,9 +12846,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz", + "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==", "cpu": [ "arm" ], @@ -12927,9 +12867,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz", + "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==", "cpu": [ "arm64" ], @@ -12948,9 +12888,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz", + "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==", "cpu": [ "arm64" ], @@ -12969,9 +12909,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz", + "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==", "cpu": [ "x64" ], @@ -12990,9 +12930,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz", + "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==", "cpu": [ "x64" ], @@ -13011,9 +12951,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz", + "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==", "cpu": [ "arm64" ], @@ -13032,9 +12972,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz", + "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==", "cpu": [ "x64" ], @@ -14892,12 +14832,12 @@ } }, "node_modules/next": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.1.tgz", - "integrity": "sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==", + "version": "16.1.7", + "resolved": "https://registry.npmjs.org/next/-/next-16.1.7.tgz", + "integrity": "sha512-WM0L7WrSvKwoLegLYr6V+mz+RIofqQgVAfHhMp9a88ms0cFX8iX9ew+snpWlSBwpkURJOUdvCEt3uLl3NNzvWg==", "license": "MIT", "dependencies": { - "@next/env": "16.2.1", + "@next/env": "16.1.7", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -14911,15 +14851,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.1", - "@next/swc-darwin-x64": "16.2.1", - "@next/swc-linux-arm64-gnu": "16.2.1", - "@next/swc-linux-arm64-musl": "16.2.1", - "@next/swc-linux-x64-gnu": "16.2.1", - "@next/swc-linux-x64-musl": "16.2.1", - "@next/swc-win32-arm64-msvc": "16.2.1", - "@next/swc-win32-x64-msvc": "16.2.1", - "sharp": "^0.34.5" + "@next/swc-darwin-arm64": "16.1.7", + "@next/swc-darwin-x64": "16.1.7", + "@next/swc-linux-arm64-gnu": "16.1.7", + "@next/swc-linux-arm64-musl": "16.1.7", + "@next/swc-linux-x64-gnu": "16.1.7", + "@next/swc-linux-x64-musl": "16.1.7", + "@next/swc-win32-arm64-msvc": "16.1.7", + "@next/swc-win32-x64-msvc": "16.1.7", + "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -16641,9 +16581,9 @@ } }, "node_modules/recharts": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.1.tgz", - "integrity": "sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.0.tgz", + "integrity": "sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ==", "license": "MIT", "workspaces": [ "www" @@ -17191,14 +17131,14 @@ "peer": true }, "node_modules/rolldown": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", - "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", + "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.12" + "@oxc-project/types": "=0.115.0", + "@rolldown/pluginutils": "1.0.0-rc.9" }, "bin": { "rolldown": "bin/cli.mjs" @@ -17207,21 +17147,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-x64": "1.0.0-rc.12", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + "@rolldown/binding-android-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-x64": "1.0.0-rc.9", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" } }, "node_modules/roughjs": { @@ -18404,16 +18344,16 @@ "peer": true }, "node_modules/tailwindcss": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", - "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", + "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", "dev": true, "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", - "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -18676,9 +18616,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { @@ -18896,9 +18836,9 @@ } }, "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -18910,16 +18850,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", - "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.1.tgz", + "integrity": "sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.58.0", - "@typescript-eslint/parser": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/utils": "8.58.0" + "@typescript-eslint/eslint-plugin": "8.57.1", + "@typescript-eslint/parser": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/utils": "8.57.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -18930,7 +18870,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/ufo": { @@ -18960,9 +18900,9 @@ } }, "node_modules/undici": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.6.tgz", - "integrity": "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", + "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -19394,16 +19334,17 @@ } }, "node_modules/vite": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", - "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz", + "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", "dev": true, "license": "MIT", "dependencies": { + "@oxc-project/runtime": "0.115.0", "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", + "picomatch": "^4.0.3", "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.12", + "rolldown": "1.0.0-rc.9", "tinyglobby": "^0.2.15" }, "bin": { @@ -19420,7 +19361,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", + "@vitejs/devtools": "^0.0.0-alpha.31", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -19486,6 +19427,267 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/vite/node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "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" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/vite/node_modules/picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", @@ -19500,19 +19702,19 @@ } }, "node_modules/vitest": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz", - "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", + "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.2", - "@vitest/mocker": "4.1.2", - "@vitest/pretty-format": "4.1.2", - "@vitest/runner": "4.1.2", - "@vitest/snapshot": "4.1.2", - "@vitest/spy": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/expect": "4.1.0", + "@vitest/mocker": "4.1.0", + "@vitest/pretty-format": "4.1.0", + "@vitest/runner": "4.1.0", + "@vitest/snapshot": "4.1.0", + "@vitest/spy": "4.1.0", + "@vitest/utils": "4.1.0", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -19523,8 +19725,8 @@ "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -19540,13 +19742,13 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.2", - "@vitest/browser-preview": "4.1.2", - "@vitest/browser-webdriverio": "4.1.2", - "@vitest/ui": "4.1.2", + "@vitest/browser-playwright": "4.1.0", + "@vitest/browser-preview": "4.1.0", + "@vitest/browser-webdriverio": "4.1.0", + "@vitest/ui": "4.1.0", "happy-dom": "*", "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -19874,9 +20076,9 @@ "license": "ISC" }, "node_modules/wreq-js": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/wreq-js/-/wreq-js-2.2.2.tgz", - "integrity": "sha512-iNcPyvVg14nWtHMzN595GDH1ELB1CDfVUV4s+AfSrP2go01/LYVBCkx4AdyMNAup4myQEiNBBmRI2Co2MKsFPQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/wreq-js/-/wreq-js-2.2.0.tgz", + "integrity": "sha512-lXW1/bvdPTpFMdfBftkJIp6OzxkAqAON4dlrKrmaFNT86eu60VCEVmEdK3nWY1ZyiEZ6IXQPRrc1uXG394BoBA==", "cpu": [ "x64", "arm64" diff --git a/package.json b/package.json index 9724a9d5fe..c884cf219f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "omniroute", - "version": "3.3.4", + "version": "3.3.5", "description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.", "type": "module", "bin": { @@ -87,7 +87,7 @@ "@lobehub/icons": "^5.0.1", "@modelcontextprotocol/sdk": "^1.27.1", "@monaco-editor/react": "^4.7.0", - "@swc/helpers": "0.5.20", + "@swc/helpers": "0.5.19", "bcryptjs": "^3.0.3", "better-sqlite3": "^12.6.2", "bottleneck": "^2.19.5", @@ -136,7 +136,7 @@ "lint-staged": "^16.2.7", "prettier": "^3.8.1", "tailwindcss": "^4", - "typescript": "^6.0.2", + "typescript": "^5.9.3", "typescript-eslint": "^8.56.0", "vitest": "^4.0.18", "wait-on": "^9.0.4" From ba227c5ec30441f5cf9646875dcb9a39f5ea4241 Mon Sep 17 00:00:00 2001 From: "R.D." Date: Mon, 30 Mar 2026 16:49:01 -0400 Subject: [PATCH 15/18] Run combo health probes concurrently --- src/app/api/combos/test/route.ts | 173 +++++++++++++-------------- tests/unit/combo-test-route.test.mjs | 64 ++++++++++ 2 files changed, 150 insertions(+), 87 deletions(-) diff --git a/src/app/api/combos/test/route.ts b/src/app/api/combos/test/route.ts index fb9888ea29..5e93979a79 100644 --- a/src/app/api/combos/test/route.ts +++ b/src/app/api/combos/test/route.ts @@ -5,6 +5,87 @@ import { getComboByName } from "@/lib/localDb"; import { testComboSchema } from "@/shared/validation/schemas"; import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; +async function testComboModel(modelStr, internalUrl) { + const startTime = Date.now(); + try { + // Send a minimal but real chat request through the same internal + // endpoint an external OpenAI-compatible client would use. + const testBody = buildComboTestRequestBody(modelStr); + + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 20000); + + let res; + try { + res = await fetch(internalUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + // Internal dashboard tests still use the normal /v1 pipeline but + // bypass REQUIRE_API_KEY so admins can test with local session auth. + "X-Internal-Test": "combo-health-check", + // Force a fresh execution path so combo tests cannot be satisfied by + // OmniRoute's semantic cache or other request reuse layers. + "X-OmniRoute-No-Cache": "true", + "X-Request-Id": `combo-test-${randomUUID()}`, + }, + body: JSON.stringify(testBody), + signal: controller.signal, + }); + } finally { + clearTimeout(timeout); + } + + const latencyMs = Date.now() - startTime; + + if (res.ok) { + let responseBody = null; + try { + responseBody = await res.json(); + } catch { + responseBody = null; + } + + const responseText = extractComboTestResponseText(responseBody); + if (!responseText) { + return { + model: modelStr, + status: "error", + statusCode: res.status, + error: "Provider returned HTTP 200 but no text content.", + latencyMs, + }; + } + + return { model: modelStr, status: "ok", latencyMs, responseText }; + } + + let errorMsg = ""; + try { + const errBody = await res.json(); + errorMsg = errBody?.error?.message || errBody?.error || res.statusText; + } catch { + errorMsg = res.statusText; + } + + return { + model: modelStr, + status: "error", + statusCode: res.status, + error: errorMsg, + latencyMs, + }; + } catch (error) { + const latencyMs = Date.now() - startTime; + return { + model: modelStr, + status: "error", + error: error.name === "AbortError" ? "Timeout (20s)" : error.message, + latencyMs, + }; + } +} + /** * POST /api/combos/test - Quick test a combo * Sends a real chat completion request through each model in the combo @@ -44,93 +125,11 @@ export async function POST(request) { return NextResponse.json({ error: "Combo has no models" }, { status: 400 }); } - const results = []; - let resolvedBy = null; - - // Test each model sequentially - for (const modelStr of models) { - const startTime = Date.now(); - try { - // Send a minimal but real chat request through the same internal - // endpoint an external OpenAI-compatible client would use. - const testBody = buildComboTestRequestBody(modelStr); - - const internalUrl = `${getBaseUrl(request)}/v1/chat/completions`; - const controller = new AbortController(); - const timeout = setTimeout(() => controller.abort(), 20000); - - let res; - try { - res = await fetch(internalUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - // Internal dashboard tests still use the normal /v1 pipeline but - // bypass REQUIRE_API_KEY so admins can test with local session auth. - "X-Internal-Test": "combo-health-check", - // Force a fresh execution path so combo tests cannot be satisfied by - // OmniRoute's semantic cache or other request reuse layers. - "X-OmniRoute-No-Cache": "true", - "X-Request-Id": `combo-test-${randomUUID()}`, - }, - body: JSON.stringify(testBody), - signal: controller.signal, - }); - } finally { - clearTimeout(timeout); - } - - const latencyMs = Date.now() - startTime; - - if (res.ok) { - let responseBody = null; - try { - responseBody = await res.json(); - } catch { - responseBody = null; - } - - const responseText = extractComboTestResponseText(responseBody); - if (!responseText) { - results.push({ - model: modelStr, - status: "error", - statusCode: res.status, - error: "Provider returned HTTP 200 but no text content.", - latencyMs, - }); - continue; - } - - results.push({ model: modelStr, status: "ok", latencyMs, responseText }); - if (!resolvedBy) resolvedBy = modelStr; - } else { - let errorMsg = ""; - try { - const errBody = await res.json(); - errorMsg = errBody?.error?.message || errBody?.error || res.statusText; - } catch { - errorMsg = res.statusText; - } - - results.push({ - model: modelStr, - status: "error", - statusCode: res.status, - error: errorMsg, - latencyMs, - }); - } - } catch (error) { - const latencyMs = Date.now() - startTime; - results.push({ - model: modelStr, - status: "error", - error: error.name === "AbortError" ? "Timeout (20s)" : error.message, - latencyMs, - }); - } - } + const internalUrl = `${getBaseUrl(request)}/v1/chat/completions`; + const results = await Promise.all( + models.map((modelStr) => testComboModel(modelStr, internalUrl)) + ); + const resolvedBy = results.find((result) => result.status === "ok")?.model || null; return NextResponse.json({ comboName, diff --git a/tests/unit/combo-test-route.test.mjs b/tests/unit/combo-test-route.test.mjs index 3f09834152..3a75a3322f 100644 --- a/tests/unit/combo-test-route.test.mjs +++ b/tests/unit/combo-test-route.test.mjs @@ -189,3 +189,67 @@ test("combo test route surfaces provider errors instead of downgrading them to r assert.equal(body.results[0].error, "Upstream rejected this request shape"); assert.equal("probeMethod" in body.results[0], false); }); + +test("combo test route launches model probes concurrently while preserving combo order", async () => { + await createTestCombo(["provider/first", "provider/second", "provider/third"]); + + const fetchCalls = []; + const resolvers = []; + globalThis.fetch = (url, init = {}) => + new Promise((resolve) => { + fetchCalls.push({ url: String(url), init }); + resolvers.push(resolve); + }); + + const responsePromise = route.POST(makeRequest()); + await new Promise((resolve) => setTimeout(resolve, 0)); + + assert.equal(fetchCalls.length, 3); + assert.deepEqual( + fetchCalls.map(({ init }) => JSON.parse(init.body).model), + ["provider/first", "provider/second", "provider/third"] + ); + + resolvers[2]( + new Response( + JSON.stringify({ + choices: [{ message: { role: "assistant", content: "THIRD" } }], + }), + { status: 200, headers: { "content-type": "application/json" } } + ) + ); + resolvers[1]( + new Response( + JSON.stringify({ + choices: [{ message: { role: "assistant", content: "SECOND" } }], + }), + { status: 200, headers: { "content-type": "application/json" } } + ) + ); + resolvers[0]( + new Response( + JSON.stringify({ + choices: [{ message: { role: "assistant", content: "FIRST" } }], + }), + { status: 200, headers: { "content-type": "application/json" } } + ) + ); + + const response = await responsePromise; + const body = await response.json(); + + assert.equal(response.status, 200); + assert.equal(body.resolvedBy, "provider/first"); + assert.deepEqual( + body.results.map((result) => ({ + model: result.model, + status: result.status, + responseText: result.responseText, + })), + [ + { model: "provider/first", status: "ok", responseText: "FIRST" }, + { model: "provider/second", status: "ok", responseText: "SECOND" }, + { model: "provider/third", status: "ok", responseText: "THIRD" }, + ] + ); +}); From 7a37c79ebcd3ad314aa82ec045ba9c25f9befada Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Mon, 30 Mar 2026 17:54:44 -0300 Subject: [PATCH 16/18] ci: fix pipeline errors and enforce route lint validatation --- .github/workflows/npm-publish.yml | 17 +++++----- src/app/api/settings/cache-config/route.ts | 39 +++++++++++++++++----- src/app/api/tunnels/cloudflared/route.ts | 19 ++++++----- 3 files changed, 50 insertions(+), 25 deletions(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 46ae68bbb8..b3d0abdab9 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -57,17 +57,18 @@ jobs: - name: Resolve version and dist-tag id: resolve run: | - case "${{ github.event_name }}" in - workflow_dispatch|workflow_call) - VERSION="${{ inputs.version }}" - TAG="${{ inputs.tag }}" - ;; - release) + VERSION="${{ inputs.version }}" + TAG="${{ inputs.tag }}" + + if [ -z "$VERSION" ]; then + if [ "${{ github.event_name }}" = "release" ]; then VERSION="${GITHUB_REF_NAME}" - ;; - esac + fi + fi + # Strip v prefix if present VERSION="${VERSION#v}" + # Default dist-tag logic if [ -z "$TAG" ]; then if [[ "$VERSION" == *-* ]]; then diff --git a/src/app/api/settings/cache-config/route.ts b/src/app/api/settings/cache-config/route.ts index a3fecff41f..e7704454e3 100644 --- a/src/app/api/settings/cache-config/route.ts +++ b/src/app/api/settings/cache-config/route.ts @@ -1,6 +1,17 @@ import { NextRequest, NextResponse } from "next/server"; import { getSettings, updateSettings } from "@/lib/localDb"; import { isAuthenticated } from "@/shared/utils/apiAuth"; +import { z } from "zod"; +import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; + +const cacheConfigUpdateSchema = z.object({ + semanticCacheEnabled: z.boolean().optional(), + semanticCacheMaxSize: z.number().positive().optional(), + semanticCacheTTL: z.number().positive().optional(), + promptCacheEnabled: z.boolean().optional(), + promptCacheStrategy: z.enum(["auto", "system-only", "manual"]).optional(), + alwaysPreserveClientCache: z.enum(["auto", "always", "never"]).optional(), +}); const CACHE_CONFIG_KEYS = [ "semanticCacheEnabled", @@ -43,25 +54,37 @@ export async function PUT(request: NextRequest) { } try { - const body = await request.json(); - const updates: Record = {}; + let rawBody: unknown; + try { + rawBody = await request.json(); + } catch { + return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); + } - if (typeof body.semanticCacheEnabled === "boolean") { + const validation = validateBody(cacheConfigUpdateSchema, rawBody); + if (isValidationFailure(validation)) { + return validation.response; + } + + const updates: Record = {}; + const body = validation.data; + + if (body.semanticCacheEnabled !== undefined) { updates.semanticCacheEnabled = body.semanticCacheEnabled; } - if (typeof body.semanticCacheMaxSize === "number" && body.semanticCacheMaxSize > 0) { + if (body.semanticCacheMaxSize !== undefined) { updates.semanticCacheMaxSize = body.semanticCacheMaxSize; } - if (typeof body.semanticCacheTTL === "number" && body.semanticCacheTTL > 0) { + if (body.semanticCacheTTL !== undefined) { updates.semanticCacheTTL = body.semanticCacheTTL; } - if (typeof body.promptCacheEnabled === "boolean") { + if (body.promptCacheEnabled !== undefined) { updates.promptCacheEnabled = body.promptCacheEnabled; } - if (["auto", "system-only", "manual"].includes(body.promptCacheStrategy)) { + if (body.promptCacheStrategy !== undefined) { updates.promptCacheStrategy = body.promptCacheStrategy; } - if (["auto", "always", "never"].includes(body.alwaysPreserveClientCache)) { + if (body.alwaysPreserveClientCache !== undefined) { updates.alwaysPreserveClientCache = body.alwaysPreserveClientCache; } diff --git a/src/app/api/tunnels/cloudflared/route.ts b/src/app/api/tunnels/cloudflared/route.ts index 3b65450532..14f78fbdf5 100644 --- a/src/app/api/tunnels/cloudflared/route.ts +++ b/src/app/api/tunnels/cloudflared/route.ts @@ -1,6 +1,7 @@ import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { isAuthenticated } from "@/shared/utils/apiAuth"; +import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; import { getCloudflaredTunnelStatus, startCloudflaredTunnel, @@ -40,27 +41,27 @@ export async function POST(request: NextRequest) { return unauthorized(); } - let payload: unknown; + let rawBody: unknown; try { - payload = await request.json(); + rawBody = await request.json(); } catch { return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); } - const parsed = actionSchema.safeParse(payload); - if (!parsed.success) { - return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 }); + const validation = validateBody(actionSchema, rawBody); + if (isValidationFailure(validation)) { + return validation.response; } + const parsed = validation.data; + try { const status = - parsed.data.action === "enable" - ? await startCloudflaredTunnel() - : await stopCloudflaredTunnel(); + parsed.action === "enable" ? await startCloudflaredTunnel() : await stopCloudflaredTunnel(); return NextResponse.json({ success: true, - action: parsed.data.action, + action: parsed.action, status, }); } catch (error) { From b6a5c9104524f50a3056083cd4a89eac055800fb Mon Sep 17 00:00:00 2001 From: "R.D." Date: Mon, 30 Mar 2026 17:01:50 -0400 Subject: [PATCH 17/18] Install CA certificates in runtime image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cb0868cf8e..974b11dbf5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,7 @@ ENV NODE_OPTIONS="--max-old-space-size=256" # Data directory inside Docker — must match the volume mount in docker-compose.yml ENV DATA_DIR=/app/data RUN apt-get update \ - && apt-get install -y --no-install-recommends libsecret-1-0 \ + && apt-get install -y --no-install-recommends libsecret-1-0 ca-certificates \ && rm -rf /var/lib/apt/lists/* RUN mkdir -p /app/data From 04d0c350dbe58e2e91ce878024f819d08ec39298 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Mon, 30 Mar 2026 18:02:33 -0300 Subject: [PATCH 18/18] build: sync monorepo package versions across electron and open-sse --- .agents/workflows/generate-release.md | 13 ++++++++++++- electron/package.json | 2 +- open-sse/package.json | 2 +- package-lock.json | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.agents/workflows/generate-release.md b/.agents/workflows/generate-release.md index 6a4d723208..3747c54f84 100644 --- a/.agents/workflows/generate-release.md +++ b/.agents/workflows/generate-release.md @@ -96,7 +96,18 @@ Keep an empty `## [Unreleased]` section above it. // turbo ```bash -VERSION=$(node -p "require('./package.json').version") && sed -i "s/ version: .*/ version: $VERSION/" docs/openapi.yaml && echo "✓ openapi.yaml → $VERSION" +VERSION=$(node -p "require('./package.json').version") +sed -i "s/ version: .*/ version: $VERSION/" docs/openapi.yaml +echo "✓ openapi.yaml → $VERSION" + +for dir in electron open-sse; do + if [ -d "$dir" ] && [ -f "$dir/package.json" ]; then + (cd "$dir" && npm version "$VERSION" --no-git-tag-version --allow-same-version > /dev/null) + echo "✓ $dir/package.json → $VERSION" + fi +done +# Re-run install to assert the workspace lockfile is updated +npm install ``` ### 6. Update README.md and i18n docs diff --git a/electron/package.json b/electron/package.json index 5b3a4ddc32..86095302fa 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "omniroute-desktop", - "version": "2.3.13", + "version": "3.3.5", "description": "OmniRoute Desktop Application", "main": "main.js", "author": { diff --git a/open-sse/package.json b/open-sse/package.json index e1e62f47b7..ebff3729bd 100644 --- a/open-sse/package.json +++ b/open-sse/package.json @@ -1,6 +1,6 @@ { "name": "@omniroute/open-sse", - "version": "0.0.1", + "version": "3.3.5", "description": "Express SSE sidecar for OmniRoute — handles streaming, protocol translation, and provider orchestration", "type": "module", "main": "index.js", diff --git a/package-lock.json b/package-lock.json index 3bf732feac..d159aa3e98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20324,7 +20324,7 @@ }, "open-sse": { "name": "@omniroute/open-sse", - "version": "0.0.1" + "version": "3.3.5" } } }