diff --git a/.dockerignore b/.dockerignore index d43f823..c39c107 100644 --- a/.dockerignore +++ b/.dockerignore @@ -56,6 +56,15 @@ build/ *.egg-info/ dist/ .pytest_cache/ +*.ipynb +.ipynb_checkpoints/ +*.coverage +htmlcov/ + +# AI service specific +services/ai-service/data/evaluations/ +services/ai-service/data/reports/ +services/ai-service/test.ipynb # Temporary files *.tmp diff --git a/.github/.copilot-instructions.md b/.github/.copilot-instructions.md index f58ea44..0b6516c 100644 --- a/.github/.copilot-instructions.md +++ b/.github/.copilot-instructions.md @@ -4,6 +4,7 @@ - Treat all backends as APIs; do not couple services together. - The AI service is stateless and must not call application APIs directly. - always use pnpm and pnpx for package management +- don't write unnecessary markdown files or documentation, prefer inline code comments and self documenting code where possible. ## Monorepo Structure @@ -32,6 +33,7 @@ - Use Tailwind CSS for styling. - Prefer shadcn/ui components where applicable. - Do not create ad-hoc UI components in app folders. +- [tanstack-start](https://tanstack.com/start/llms.txt) is the frontend application framework ## Forms & State diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..851a84e --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,76 @@ +name: Build and Push Docker Images + +on: + push: + branches: + - main + - dev + tags: + - 'v*' + pull_request: + branches: + - main + - dev + workflow_dispatch: + +env: + REGISTRY: docker.io + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + strategy: + matrix: + include: + - dockerfile: ai.dockerfile + image: governance-agent-ai_service + context: . + - dockerfile: next.dockerfile + image: governance-agent-frontend + context: . + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata (tags, labels) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha,prefix={{branch}}- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ${{ matrix.context }} + file: ${{ matrix.dockerfile }} + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64 + + - name: Image digest + run: echo "Image pushed with digest ${{ steps.build.outputs.digest }}" diff --git a/ai.dockerfile b/ai.dockerfile index 9f59ff8..f80fa5d 100644 --- a/ai.dockerfile +++ b/ai.dockerfile @@ -1,18 +1,55 @@ -FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim +# Build stage +FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS builder WORKDIR /app ENV UV_COMPILE_BYTECODE=1 ENV PYTHONPATH=/app/src +ENV UV_INDEX_STRATEGY=unsafe-best-match +ENV UV_HTTP_TIMEOUT=300 -COPY services/ai-service/pyproject.toml ./ +# Copy dependency files for caching +COPY services/ai-service/pyproject.toml services/ai-service/uv.lock ./ +# Install CPU-only PyTorch to reduce image size dramatically +RUN uv pip install --system --no-cache \ + torch==2.5.1+cpu \ + --index-url https://download.pytorch.org/whl/cpu + +# Install other dependencies RUN uv sync --no-install-project --no-dev COPY services/ai-service/ ./ RUN uv sync --no-dev +# Remove unnecessary files +RUN find /app/.venv -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true +RUN find /app/.venv -type f -name "*.pyc" -delete +RUN find /app/.venv -type f -name "*.pyo" -delete +RUN find /app/.venv -name "tests" -type d -exec rm -rf {} + 2>/dev/null || true +RUN find /app/.venv -name "test" -type d -exec rm -rf {} + 2>/dev/null || true + +# Runtime stage +FROM python:3.13-slim-bookworm + +WORKDIR /app + +ENV PYTHONPATH=/app/src +ENV PYTHONUNBUFFERED=1 +ENV PYTHONDONTWRITEBYTECODE=1 + +# Install runtime dependencies only +RUN apt-get update && apt-get install -y --no-install-recommends \ + libgomp1 \ + && rm -rf /var/lib/apt/lists/* + +# Copy only the virtual environment and application code +COPY --from=builder /app/.venv /app/.venv +COPY --from=builder /app/src /app/src +COPY --from=builder /app/run.py /app/run.py +COPY --from=builder /app/main.py /app/main.py + # Create non-root user RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app USER appuser diff --git a/apps/tanstack-start/package.json b/apps/tanstack-start/package.json index e26f9de..df5e667 100644 --- a/apps/tanstack-start/package.json +++ b/apps/tanstack-start/package.json @@ -10,7 +10,7 @@ "format": "prettier --check . --ignore-path ../../.gitignore --ignore-path .prettierignore", "lint": "eslint --flag unstable_native_nodejs_ts_config", "typecheck": "tsc --noEmit", - "with-env": "dotenv -e ../../.env --" + "with-env": "dotenv -e ../../.env.local --" }, "dependencies": { "@fontsource-variable/geist": "^5.2.8", @@ -19,6 +19,7 @@ "@governance/auth": "workspace:*", "@governance/db": "workspace:*", "@governance/ui": "workspace:*", + "@radix-ui/themes": "^3.3.0", "@t3-oss/env-core": "^0.13.8", "@tanstack/react-form": "catalog:", "@tanstack/react-query": "catalog:", @@ -29,6 +30,8 @@ "@trpc/client": "catalog:", "@trpc/server": "catalog:", "@trpc/tanstack-react-query": "catalog:", + "@workos-inc/widgets": "^1.8.0", + "@workos-inc/widgets-i18n": "^0.1.0", "@workos/authkit-tanstack-react-start": "^0.5.0", "nitro": "3.0.1-alpha.1", "react": "catalog:react19", diff --git a/apps/tanstack-start/src/component/NotFound.tsx b/apps/tanstack-start/src/component/NotFound.tsx index c4e06aa..88028f0 100644 --- a/apps/tanstack-start/src/component/NotFound.tsx +++ b/apps/tanstack-start/src/component/NotFound.tsx @@ -1,26 +1,28 @@ import { Link, useLocation } from "@tanstack/react-router"; export function NotFound() { - const location = useLocation(); - console.log("NotFound triggered for path:", location.pathname); + const location = useLocation(); + console.log("NotFound triggered for path:", location.pathname); - return ( -
-

404

-
- Page Not Found -
-
-
- Oops! The page you asked for doesn't exist. -
- - Go Home - -
+ return ( +
+

+ 404 +

+
+ Page Not Found +
+
+
+ Oops! The page you asked for doesn't exist.
- ); + + Go Home + +
+
+ ); } diff --git a/apps/tanstack-start/src/component/auth-showcase.tsx b/apps/tanstack-start/src/component/auth-showcase.tsx index ff887d0..3fc65c0 100644 --- a/apps/tanstack-start/src/component/auth-showcase.tsx +++ b/apps/tanstack-start/src/component/auth-showcase.tsx @@ -1,6 +1,6 @@ -import { useAuth } from "@workos/authkit-tanstack-react-start/client"; -import { getSignInUrl } from "@workos/authkit-tanstack-react-start"; import { createServerFn } from "@tanstack/react-start"; +import { getSignInUrl } from "@workos/authkit-tanstack-react-start"; +import { useAuth } from "@workos/authkit-tanstack-react-start/client"; import { Button } from "@governance/ui/button"; diff --git a/apps/tanstack-start/src/component/index/IndexCard.tsx b/apps/tanstack-start/src/component/index/IndexCard.tsx index b0d5ee1..37ed317 100644 --- a/apps/tanstack-start/src/component/index/IndexCard.tsx +++ b/apps/tanstack-start/src/component/index/IndexCard.tsx @@ -1,63 +1,60 @@ import { Link } from "@tanstack/react-router"; + import { cn } from "@governance/ui"; interface IndexCardProps { - title: string; - subtitle: string; - isActive: boolean; - href?: string; + title: string; + subtitle: string; + isActive: boolean; + href?: string; } export function IndexCard({ title, subtitle, isActive, href }: IndexCardProps) { - const CardContent = ( -
- {/* Active Indicator */} - {isActive && ( -
-
-
-
-
-
- )} - - {/* Content */} -
-

- {title} -

-

- {subtitle} -

-
- - {/* Active Label */} - {isActive && ( -
-
- - Active - -
- )} - - {/* Hover Effect */} - {isActive && ( -
- )} + const CardContent = ( +
+ {/* Active Indicator */} + {isActive && ( +
+
+
+
+
- ); + )} + + {/* Content */} +
+

{title}

+

{subtitle}

+
+ + {/* Active Label */} + {isActive && ( +
+
+ + Active + +
+ )} + + {/* Hover Effect */} + {isActive && ( +
+ )} +
+ ); - if (isActive && href) { - return {CardContent}; - } + if (isActive && href) { + return {CardContent}; + } - return CardContent; + return CardContent; } diff --git a/apps/tanstack-start/src/component/layout/Header.tsx b/apps/tanstack-start/src/component/layout/Header.tsx index 8fa071c..794a8d9 100644 --- a/apps/tanstack-start/src/component/layout/Header.tsx +++ b/apps/tanstack-start/src/component/layout/Header.tsx @@ -2,61 +2,69 @@ import { Link } from "@tanstack/react-router"; import { createServerFn } from "@tanstack/react-start"; -import { useAuth } from "@workos/authkit-tanstack-react-start/client"; import { getSignInUrl } from "@workos/authkit-tanstack-react-start"; +import { useAuth } from "@workos/authkit-tanstack-react-start/client"; + +import { hasPermission, SUBMISSION_PERMISSIONS } from "~/lib/permissions"; export function Header() { - const { user, signOut } = useAuth(); + const { user, permissions, signOut } = useAuth(); + const canViewAdmin = hasPermission(permissions, SUBMISSION_PERMISSIONS.READ); - return ( -
-
- {/* Logo */} - - NDMO - + return ( +
+
+ {/* Logo */} + + NDMO + - {/* Navigation */} - -
-
- ); + {/* Navigation */} + +
+
+ ); } const getSignInUrlFn = createServerFn({ method: "GET" }).handler(async () => { - return await getSignInUrl(); + return await getSignInUrl(); }); diff --git a/apps/tanstack-start/src/env.ts b/apps/tanstack-start/src/env.ts index 32477d3..445b620 100644 --- a/apps/tanstack-start/src/env.ts +++ b/apps/tanstack-start/src/env.ts @@ -1,8 +1,9 @@ -import { authEnv } from "@governance/auth/env"; import { createEnv } from "@t3-oss/env-core"; import { vercel } from "@t3-oss/env-core/presets-zod"; import { z } from "zod/v4"; +import { authEnv } from "@governance/auth/env"; + export const env = createEnv({ clientPrefix: "VITE_", extends: [authEnv(), vercel()], diff --git a/apps/tanstack-start/src/lib/permissions.ts b/apps/tanstack-start/src/lib/permissions.ts new file mode 100644 index 0000000..58d2b6d --- /dev/null +++ b/apps/tanstack-start/src/lib/permissions.ts @@ -0,0 +1,48 @@ +/** + * Submission permissions matching backend + */ +export const SUBMISSION_PERMISSIONS = { + READ: "submitions:read", + WRITE: "submitions:write", + DELETE: "submitions:delete", +} as const; + +export type SubmissionPermission = + (typeof SUBMISSION_PERMISSIONS)[keyof typeof SUBMISSION_PERMISSIONS]; + +/** + * Check if user has a specific permission + */ +export function hasPermission( + userPermissions: string[] | undefined, + requiredPermission: string, +): boolean { + if (!userPermissions) return false; + return userPermissions.includes(requiredPermission); +} + +/** + * Check if user has all required permissions + */ +export function hasAllPermissions( + userPermissions: string[] | undefined, + requiredPermissions: string[], +): boolean { + if (!userPermissions) return false; + return requiredPermissions.every((permission) => + userPermissions.includes(permission), + ); +} + +/** + * Check if user has any of the required permissions + */ +export function hasAnyPermission( + userPermissions: string[] | undefined, + requiredPermissions: string[], +): boolean { + if (!userPermissions) return false; + return requiredPermissions.some((permission) => + userPermissions.includes(permission), + ); +} diff --git a/apps/tanstack-start/src/lib/trpc.ts b/apps/tanstack-start/src/lib/trpc.ts index c066fde..88acf20 100644 --- a/apps/tanstack-start/src/lib/trpc.ts +++ b/apps/tanstack-start/src/lib/trpc.ts @@ -7,8 +7,8 @@ import { unstable_localLink, } from "@trpc/client"; import { createTRPCContext } from "@trpc/tanstack-react-query"; -import SuperJSON from "superjson"; import { getAuth } from "@workos/authkit-tanstack-react-start"; +import SuperJSON from "superjson"; import * as Api from "@governance/api"; diff --git a/apps/tanstack-start/src/routes/__root.tsx b/apps/tanstack-start/src/routes/__root.tsx index 5c9c075..f7c68bc 100644 --- a/apps/tanstack-start/src/routes/__root.tsx +++ b/apps/tanstack-start/src/routes/__root.tsx @@ -7,6 +7,8 @@ import { HeadContent, Outlet, Scripts, + ErrorComponent, + Link, } from "@tanstack/react-router"; import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; import { AuthKitProvider } from "@workos/authkit-tanstack-react-start/client"; @@ -18,11 +20,11 @@ import { ThemeToggle, } from "@governance/ui/theme"; import { Toaster } from "@governance/ui/toast"; +import { Button } from "@governance/ui/button"; import appCss from "~/styles.css?url"; - -import { NotFound } from "../component/NotFound"; import { Header } from "../component/layout/Header"; +import { NotFound } from "../component/NotFound"; export const Route = createRootRouteWithContext<{ queryClient: QueryClient; @@ -33,6 +35,25 @@ export const Route = createRootRouteWithContext<{ }), component: RootComponent, notFoundComponent: NotFound, + errorComponent: ({ error }) => { + return ( + +
+
+
+

Error

+

+ {error.message || "An unexpected error occurred"} +

+
+ +
+
+
+ ); + }, }); function RootComponent() { @@ -67,6 +88,6 @@ function RootDocument({ children }: { children: React.ReactNode }) { - + ); } diff --git a/apps/tanstack-start/src/routes/admin/company.$companyId.tsx b/apps/tanstack-start/src/routes/admin/company.$companyId.tsx index 76fe9b1..9fc549e 100644 --- a/apps/tanstack-start/src/routes/admin/company.$companyId.tsx +++ b/apps/tanstack-start/src/routes/admin/company.$companyId.tsx @@ -1,10 +1,30 @@ import { useState } from "react"; -import { useQuery } from "@tanstack/react-query"; -import { createFileRoute, Link } from "@tanstack/react-router"; -import { useTRPC } from "~/lib/trpc"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { createFileRoute, Link, redirect } from "@tanstack/react-router"; +import { getAuth } from "@workos/authkit-tanstack-react-start"; +import { toast } from "@governance/ui/toast"; + import { NDI_DOMAINS } from "~/lib/ndi-domains"; +import { useTRPC } from "~/lib/trpc"; +import { hasPermission, SUBMISSION_PERMISSIONS } from "~/lib/permissions"; export const Route = createFileRoute("/admin/company/$companyId")({ + beforeLoad: async () => { + const auth = await getAuth(); + + // Check if user is authenticated + if (!auth.user) { + throw redirect({ to: "/" }); + } + + // Check if user has read permission + if (!hasPermission(auth.permissions, SUBMISSION_PERMISSIONS.READ)) { + throw redirect({ + to: "/", + search: { error: "insufficient_permissions" } + }); + } + }, component: RouteComponent, }); @@ -44,12 +64,6 @@ function getScore(decision: string): number { return SCORE_MAP[decision.toLowerCase()] ?? 0; } -function getMaxScore(decision: string): number { - const d = decision.toLowerCase(); - if (d === "compliant" || d === "not compliant") return 6; - return 6; // scale max is Leader = 6 -} - function getDecisionColor(decision: string): string { const d = decision.toLowerCase(); if (["leader", "excellent", "good", "compliant"].includes(d)) @@ -69,14 +83,38 @@ function getDecisionIcon(decision: string): string { function RouteComponent() { const { companyId } = Route.useParams(); const trpc = useTRPC(); - const { data, isLoading, error } = useQuery( + const { data, isLoading, error, refetch } = useQuery( trpc.submission.getById.queryOptions({ id: companyId }), ); + const rerunEvaluation = useMutation( + trpc.submission.rerunEvaluation.mutationOptions(), + ); + + const [isRerunning, setIsRerunning] = useState(false); + + const handleRerun = async () => { + try { + setIsRerunning(true); + toast.info("Starting evaluation..."); + + await rerunEvaluation.mutateAsync({ id: companyId }); + + toast.success("Evaluation completed successfully!"); + await refetch(); + } catch (err) { + console.error("Rerun failed:", err); + const errorMessage = err instanceof Error ? err.message : "Failed to rerun evaluation"; + toast.error(errorMessage); + } finally { + setIsRerunning(false); + } + }; + if (isLoading) { return (
-
+
Loading report...
@@ -86,11 +124,9 @@ function RouteComponent() { if (error || !data) { return (
-
+

Failed to load submission

-

- {error?.message} -

+

{error?.message}

e.decisions, - ); + const allDecisions = Object.values(evalsByDomain).flatMap((e) => e.decisions); const overallScore = allDecisions.length > 0 ? allDecisions.reduce((sum, d) => sum + getScore(d.decision), 0) / @@ -165,7 +199,7 @@ function RouteComponent() { return (
-
+
{/* Header */}
-

+

{submission.companyName}

NDI Assessment Report

{report?.reportPath && ( - + )}
)} {/* Domain Evaluations */} -
-

+
+

{hasEvaluations ? "Domain Evaluations" : "Submitted Domains"}

@@ -302,15 +396,15 @@ function RouteComponent() {
{domainsWithoutFiles.length > 0 && ( -
-

+
+

Domains Not Submitted

-
+
{domainsWithoutFiles.map((domain) => (
{domain.order}. {domain.name}
@@ -361,19 +455,19 @@ function DomainCard({ domain, files, evaluation }: DomainCardProps) { const warnCount = decisions.length - passCount - failCount; return ( -
+
{/* Domain Header */}
-
+
{domain.order}
-

+

{domain.name}

- + {files.length} file{files.length !== 1 ? "s" : ""} {hasEval && ` ยท ${decisions.length} controls evaluated`} @@ -384,8 +478,8 @@ function DomainCard({ domain, files, evaluation }: DomainCardProps) { {hasEval && ( <> {/* Score */} -
-
+
+
{Math.round(domainScore * 100)}%
@@ -393,9 +487,7 @@ function DomainCard({ domain, files, evaluation }: DomainCardProps) { {passCount} pass )} {warnCount > 0 && ( - - {warnCount} fair - + {warnCount} fair )} {failCount > 0 && ( {failCount} fail @@ -406,7 +498,7 @@ function DomainCard({ domain, files, evaluation }: DomainCardProps) { {/* More Details Button */} @@ -418,17 +510,17 @@ function DomainCard({ domain, files, evaluation }: DomainCardProps) { {/* Expanded Details */} {expanded && hasEval && ( -
+
{/* AI Summary */} {summaries.length > 0 && (
-

+

AI Assessment Summary

{summaries.map((summary, i) => (

{summary}

@@ -437,23 +529,20 @@ function DomainCard({ domain, files, evaluation }: DomainCardProps) { )} {/* Control Decisions Checklist */} -

+

Control Evaluations

{decisions.map((decision, i) => { const icon = getDecisionIcon(decision.decision); return ( -
+
{/* Status Icon */} -
+
{icon === "pass" && ( {/* Content */} -
-
- +
+
+ {decision.control_id} {decision.decision}
-

+

{decision.rationale}

diff --git a/apps/tanstack-start/src/routes/admin/index.tsx b/apps/tanstack-start/src/routes/admin/index.tsx index 389f3a6..67480c9 100644 --- a/apps/tanstack-start/src/routes/admin/index.tsx +++ b/apps/tanstack-start/src/routes/admin/index.tsx @@ -1,39 +1,58 @@ -import { createFileRoute, Link } from "@tanstack/react-router"; +import { createFileRoute, Link, redirect } from "@tanstack/react-router"; +import { getAuth } from "@workos/authkit-tanstack-react-start"; + import { IndexCard } from "~/component/index/IndexCard"; +import { hasPermission, SUBMISSION_PERMISSIONS } from "~/lib/permissions"; export const Route = createFileRoute("/admin/")({ - component: RouteComponent, + beforeLoad: async () => { + const auth = await getAuth(); + + // Check if user is authenticated + if (!auth.user) { + throw redirect({ to: "/" }); + } + + // Check if user has read permission + if (!hasPermission(auth.permissions, SUBMISSION_PERMISSIONS.READ)) { + throw redirect({ + to: "/", + search: { error: "insufficient_permissions" } + }); + } + }, + component: RouteComponent, }); function RouteComponent() { - return ( -
-
- {/* Header Section */} -
-

- Admin Dashboard -

-

- Select an index to view submissions -

-
+ return ( +
+
+ {/* Header Section */} +
+

+ Admin Dashboard +

+

+ Select an index to view submissions +

+
- {/* Index Cards */} -
- - -
-
-
- ); + {/* Index Cards */} +
+ + +
+
+
+ ); } diff --git a/apps/tanstack-start/src/routes/admin/ndi.tsx b/apps/tanstack-start/src/routes/admin/ndi.tsx index f3cb6f2..ae9d908 100644 --- a/apps/tanstack-start/src/routes/admin/ndi.tsx +++ b/apps/tanstack-start/src/routes/admin/ndi.tsx @@ -1,123 +1,177 @@ import { useQuery } from "@tanstack/react-query"; -import { createFileRoute, Link } from "@tanstack/react-router"; +import { createFileRoute, Link, redirect } from "@tanstack/react-router"; +import { getAuth } from "@workos/authkit-tanstack-react-start"; + import { useTRPC } from "~/lib/trpc"; +import { hasPermission, SUBMISSION_PERMISSIONS } from "~/lib/permissions"; export const Route = createFileRoute("/admin/ndi")({ - component: RouteComponent, + beforeLoad: async () => { + const auth = await getAuth(); + + // Check if user is authenticated + if (!auth.user) { + throw redirect({ to: "/" }); + } + + // Check if user has read permission + if (!hasPermission(auth.permissions, SUBMISSION_PERMISSIONS.READ)) { + throw redirect({ + to: "/", + search: { error: "insufficient_permissions" } + }); + } + }, + component: RouteComponent, }); - - function RouteComponent() { - const trpc = useTRPC(); - const { data: submissions, isLoading, error } = useQuery(trpc.submission.getAll.queryOptions()); + const trpc = useTRPC(); + const { + data: submissions, + isLoading, + error, + } = useQuery(trpc.submission.getAll.queryOptions()); - return ( -
-
- {/* Header */} -
- - - - - Back to Dashboard - -

- NDI Submissions -

-

- View and manage all company submissions -

-
+ return ( +
+
+ {/* Header */} +
+ + + + + Back to Dashboard + +

+ NDI Submissions +

+

+ View and manage all company submissions +

+
- {isLoading ? ( -
Loading submissions...
- ) : error ? ( -
-

Failed to load submissions

-

{error.message}

-
- ) : !submissions || submissions.length === 0 ? ( -
-

- No Submissions Yet -

-

- Company submissions will appear here -

-
- ) : ( -
- {submissions.map((submission) => ( - - ))} -
- )} -
-
- ); + {isLoading ? ( +
+ Loading submissions... +
+ ) : error ? ( +
+

Failed to load submissions

+

{error.message}

+
+ ) : !submissions || submissions.length === 0 ? ( +
+

+ No Submissions Yet +

+

+ Company submissions will appear here +

+
+ ) : ( +
+ {submissions.map((submission) => ( + + ))} +
+ )} +
+
+ ); } interface CompanyCardProps { - submission: { - id: string; - companyName: string; - createdAt: string; - status: string; - }; + submission: { + id: string; + companyName: string; + userId: string; + status: string; + createdAt: Date; + updatedAt: Date; + }; } function CompanyCard({ submission }: CompanyCardProps) { - const statusColors = { - completed: "bg-ndmo-green text-primary", - processing: "bg-ndmo-yellow text-primary", - pending: "bg-ndmo-gray-medium text-primary", - failed: "bg-ndmo-red text-primary", - }; + const statusColors = { + completed: "bg-ndmo-green text-primary", + processing: "bg-ndmo-yellow text-primary", + pending: "bg-ndmo-gray-medium text-primary", + failed: "bg-ndmo-red text-primary", + }; - const statusColor = statusColors[submission.status as keyof typeof statusColors] || statusColors.pending; + const statusColor = + statusColors[submission.status as keyof typeof statusColors] || + statusColors.pending; - return ( - -
- {/* Company Name */} -

- {submission.companyName} -

+ return ( + +
+ {/* Company Name */} +

+ {submission.companyName} +

- {/* Metadata */} -
-
- - - - {new Date(submission.createdAt).toLocaleDateString()} -
-
+ {/* Metadata */} +
+
+ + + + {new Date(submission.createdAt).toLocaleDateString()} +
+
- {/* Status Badge */} -
- - {submission.status} - - - - -
-
- - ); + {/* Status Badge */} +
+ + {submission.status} + + + + +
+
+ + ); } diff --git a/apps/tanstack-start/src/routes/api/auth/callback.tsx b/apps/tanstack-start/src/routes/api/auth/callback.tsx index faf3097..5d655ec 100644 --- a/apps/tanstack-start/src/routes/api/auth/callback.tsx +++ b/apps/tanstack-start/src/routes/api/auth/callback.tsx @@ -2,9 +2,9 @@ import { createFileRoute } from "@tanstack/react-router"; import { handleCallbackRoute } from "@workos/authkit-tanstack-react-start"; export const Route = createFileRoute("/api/auth/callback")({ - server: { - handlers: { - GET: handleCallbackRoute(), - }, + server: { + handlers: { + GET: handleCallbackRoute(), }, + }, }); diff --git a/apps/tanstack-start/src/routes/index.tsx b/apps/tanstack-start/src/routes/index.tsx index 3637273..0c3bcf6 100644 --- a/apps/tanstack-start/src/routes/index.tsx +++ b/apps/tanstack-start/src/routes/index.tsx @@ -1,26 +1,65 @@ -import { createFileRoute } from "@tanstack/react-router"; +import { createFileRoute, useSearch } from "@tanstack/react-router"; +import { z } from "zod"; + import { IndexCard } from "~/component/index/IndexCard"; +const searchSchema = z.object({ + error: z.string().optional(), +}); + export const Route = createFileRoute("/")({ + validateSearch: searchSchema, component: RouteComponent, }); function RouteComponent() { + const search = useSearch({ from: "/" }); + const showPermissionError = search.error === "insufficient_permissions"; + return (
-
+
+ {/* Permission Error Alert */} + {showPermissionError && ( +
+
+ + + +
+

+ Insufficient Permissions +

+

+ You don't have the necessary permissions to access that page. Please contact your administrator if you believe this is an error. +

+
+
+
+ )} + {/* Header Section */} -
-

+
+

National Data Indices

-

+

Select an index to begin your assessment

{/* Index Cards */} -
+
=> { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.readAsDataURL(file); - reader.onload = () => { - // remove data:application/pdf;base64, prefix - const result = reader.result as string; - const base64 = result.split(',')[1]; - if (base64) resolve(base64); - else reject(new Error('Failed to convert file to base64')); - }; - reader.onerror = error => reject(error); - }); + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => { + // remove data:application/pdf;base64, prefix + const result = reader.result as string; + const base64 = result.split(",")[1]; + if (base64) resolve(base64); + else reject(new Error("Failed to convert file to base64")); + }; + reader.onerror = (error) => reject(error); + }); }; export const Route = createFileRoute("/submit")({ - component: RouteComponent, + beforeLoad: async ({ location }) => { + const auth = await getAuth(); + + // Check if user is authenticated + if (!auth.user) { + const signInUrl = await getSignInUrl(); + throw redirect({ href: signInUrl }); + } + + // Check if user has write permission + if (!hasPermission(auth.permissions, SUBMISSION_PERMISSIONS.WRITE)) { + throw redirect({ + to: "/", + search: { error: "insufficient_permissions" } + }); + } + }, + component: RouteComponent, }); interface FileWithDomain { - file: File; - domainId: string; + file: File; + domainId: string; } function RouteComponent() { - const navigate = useNavigate(); - const [filesByDomain, setFilesByDomain] = useState>({}); - const [isSubmitting, setIsSubmitting] = useState(false); - const trpc = useTRPC(); - const createSubmission = useMutation(trpc.submission.create.mutationOptions()); + const navigate = useNavigate(); + const [filesByDomain, setFilesByDomain] = useState>( + {}, + ); + const [isSubmitting, setIsSubmitting] = useState(false); + const trpc = useTRPC(); + const createSubmission = useMutation( + trpc.submission.create.mutationOptions(), + ); - const form = useForm({ - defaultValues: { - companyName: "", - }, - onSubmit: async ({ value }) => { - // Validate company name - if (!value.companyName.trim()) { - toast.error("Please enter your company name"); - return; - } + const form = useForm({ + defaultValues: { + companyName: "", + }, + onSubmit: async ({ value }) => { + // Validate company name + if (!value.companyName.trim()) { + toast.error("Please enter your company name"); + return; + } - // Check if at least one file is uploaded - const totalFiles = Object.values(filesByDomain).reduce( - (sum, files) => sum + files.length, - 0 - ); - if (totalFiles === 0) { - toast.error("Please upload at least one file"); - return; - } + // Check if at least one file is uploaded + const totalFiles = Object.values(filesByDomain).reduce( + (sum, files) => sum + files.length, + 0, + ); + if (totalFiles === 0) { + toast.error("Please upload at least one file"); + return; + } - setIsSubmitting(true); + setIsSubmitting(true); - try { - const filesToUpload = []; - for (const [domainId, files] of Object.entries(filesByDomain)) { - const domain = NDI_DOMAINS.find(d => d.id === domainId); - const domainName = domain ? domain.name : "Unknown"; - for (const file of files) { - const content = await fileToBase64(file); - filesToUpload.push({ - name: file.name, - content, - type: file.type, - domainId, - domainName, - size: file.size - }); - } - } + try { + const filesToUpload = []; + for (const [domainId, files] of Object.entries(filesByDomain)) { + const domain = NDI_DOMAINS.find((d) => d.id === domainId); + const domainName = domain ? domain.name : "Unknown"; + for (const file of files) { + const content = await fileToBase64(file); + filesToUpload.push({ + name: file.name, + content, + type: file.type, + domainId, + domainName, + size: file.size, + }); + } + } - const result = await createSubmission.mutateAsync({ - companyName: value.companyName, - files: filesToUpload - }); + const result = await createSubmission.mutateAsync({ + companyName: value.companyName, + files: filesToUpload, + }); - if (result.evaluationFailed) { - toast.success("Submission saved. AI evaluation could not be completed โ€” you can retry later."); - } else { - toast.success("Submission created and evaluated successfully!"); - } - await navigate({ to: "/admin/ndi" }); - } catch (error) { - toast.error("Failed to save submission. Please try again."); - console.error(error); - } finally { - setIsSubmitting(false); - } - }, - }); + if (result.evaluationFailed) { + toast.success( + "Submission saved. AI evaluation could not be completed โ€” you can retry later.", + ); + } else { + toast.success("Submission created and evaluated successfully!"); + } + await navigate({ to: "/admin/ndi" }); + } catch (error) { + toast.error("Failed to save submission. Please try again."); + console.error(error); + } finally { + setIsSubmitting(false); + } + }, + }); - const handleFileChange = useCallback( - (domainId: string, files: FileList | null) => { - if (!files) return; + const handleFileChange = useCallback( + (domainId: string, files: FileList | null) => { + if (!files) return; - const fileArray = Array.from(files); - const currentFiles = filesByDomain[domainId] || []; + const fileArray = Array.from(files); + const currentFiles = filesByDomain[domainId] || []; - // Limit to 20 files per domain - const newFiles = [...currentFiles, ...fileArray].slice(0, 20); + // Limit to 20 files per domain + const newFiles = [...currentFiles, ...fileArray].slice(0, 20); - setFilesByDomain((prev) => ({ - ...prev, - [domainId]: newFiles, - })); - }, - [filesByDomain] - ); + setFilesByDomain((prev) => ({ + ...prev, + [domainId]: newFiles, + })); + }, + [filesByDomain], + ); - const removeFile = useCallback((domainId: string, index: number) => { - setFilesByDomain((prev) => ({ - ...prev, - [domainId]: (prev[domainId] || []).filter((_, i) => i !== index), - })); - }, []); + const removeFile = useCallback((domainId: string, index: number) => { + setFilesByDomain((prev) => ({ + ...prev, + [domainId]: (prev[domainId] || []).filter((_, i) => i !== index), + })); + }, []); - return ( -
-
- {/* Header */} -
-

- NDI Assessment Submission -

-

- Upload your documents for Nationality Data Index evaluation -

-
- - {/* Form */} -
{ - e.preventDefault(); - void form.handleSubmit(); - }} - className="space-y-8" - > - {/* Company Name */} -
- - {(field) => { - const isInvalid = - field.state.meta.isTouched && !field.state.meta.isValid; - return ( - - - - Company Name * - - - field.handleChange(e.target.value)} - aria-invalid={isInvalid} - placeholder="Enter your company name" - className="mt-2" - /> - {isInvalid && } - - ); - }} - -
+ return ( +
+
+ {/* Header */} +
+

+ NDI Assessment Submission +

+

+ Upload your documents for Nationality Data Index evaluation +

+
- {/* Upload Documents Section */} -
-

- Upload Documents -

+ {/* Form */} + { + e.preventDefault(); + void form.handleSubmit(); + }} + className="space-y-8" + > + {/* Company Name */} +
+ + {(field) => { + const isInvalid = + field.state.meta.isTouched && !field.state.meta.isValid; + return ( + + + + Company Name * + + + field.handleChange(e.target.value)} + aria-invalid={isInvalid} + placeholder="Enter your company name" + className="mt-2" + /> + {isInvalid && ( + + )} + + ); + }} + +
-
- {NDI_DOMAINS.map((domain) => ( - handleFileChange(domain.id, files)} - onRemoveFile={(index) => removeFile(domain.id, index)} - /> - ))} -
-
+ {/* Upload Documents Section */} +
+

+ Upload Documents +

- {/* Submit Button */} -
- -
- +
+ {NDI_DOMAINS.map((domain) => ( + handleFileChange(domain.id, files)} + onRemoveFile={(index) => removeFile(domain.id, index)} + /> + ))}
-
- ); +
+ + {/* Submit Button */} +
+ +
+ +
+

+ ); } interface DomainUploadSectionProps { - domain: (typeof NDI_DOMAINS)[number]; - files: File[]; - onFileChange: (files: FileList | null) => void; - onRemoveFile: (index: number) => void; + domain: (typeof NDI_DOMAINS)[number]; + files: File[]; + onFileChange: (files: FileList | null) => void; + onRemoveFile: (index: number) => void; } function DomainUploadSection({ - domain, - files, - onFileChange, - onRemoveFile, + domain, + files, + onFileChange, + onRemoveFile, }: DomainUploadSectionProps) { - return ( -
-
- {/* Domain Number */} -
- {domain.order} -
+ return ( +
+
+ {/* Domain Number */} +
+ {domain.order} +
- {/* Domain Content */} -
-

- {domain.name} -

+ {/* Domain Content */} +
+

+ {domain.name} +

- {/* File Upload */} -
- + {/* File Upload */} +
+ - {/* File List */} - {files.length > 0 && ( -
- {files.map((file, index) => ( -
-
- - - - - {file.name} - - - ({(file.size / 1024).toFixed(1)} KB) - -
- -
- ))} - {files.length >= 20 && ( -

- Maximum 20 files reached for this domain -

- )} -
- )} + {/* File List */} + {files.length > 0 && ( +
+ {files.map((file, index) => ( +
+
+ + + + + {file.name} + + + ({(file.size / 1024).toFixed(1)} KB) +
-
-
+ +
+ ))} + {files.length >= 20 && ( +

+ Maximum 20 files reached for this domain +

+ )} +
+ )} +
- ); +
+
+ ); } diff --git a/docker-compose.demo.yml b/docker-compose.demo.yml new file mode 100644 index 0000000..d29e36f --- /dev/null +++ b/docker-compose.demo.yml @@ -0,0 +1,169 @@ +# Demo compose file with Keycloak SSO for WorkOS Admin Portal testing +# Usage: docker compose -f docker-compose.demo.yml up +# +# Access points: +# - Frontend: http://localhost:5001 +# - Keycloak Admin: http://localhost:8080 +# - Keycloak login: admin / admin + +services: + ai_service: + image: governance-agent-ai_service:latest + build: + context: . + dockerfile: ai.dockerfile + ports: + - "5000:8000" + env_file: + - .env + environment: + - PYTHONPATH=/app/src + depends_on: + vector_db: + condition: service_started + restart: unless-stopped + healthcheck: + test: + [ + "CMD", + "python", + "-c", + "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + frontend: + image: governance-agent-frontend:latest + build: + context: . + dockerfile: next.dockerfile + ports: + - "5001:3000" + env_file: + - .env + environment: + - NODE_ENV=production + - AI_HOST=ai_service + - POSTGRES_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@application_db:5432/${POSTGRES_DB:-app_db} + depends_on: + ai_service: + condition: service_started + application_db: + condition: service_healthy + keycloak: + condition: service_healthy + restart: unless-stopped + + vector_db: + image: qdrant/qdrant:latest + restart: unless-stopped + container_name: qdrant_demo + ports: + - "6333:6333" + - "6334:6334" + expose: + - 6333 + - 6334 + - 6335 + configs: + - source: qdrant_config + target: /qdrant/config/production.yaml + volumes: + - qdrant_demo_data:/qdrant/storage + + application_db: + image: postgres:15 + restart: always + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} + POSTGRES_DB: ${POSTGRES_DB:-app_db} + ports: + - "5432:5432" + volumes: + - db_demo_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 30s + + # Keycloak SSO Service for WorkOS Demo + keycloak_db: + image: postgres:15 + restart: always + environment: + POSTGRES_DB: keycloak + POSTGRES_USER: keycloak + POSTGRES_PASSWORD: keycloak_password + volumes: + - keycloak_db_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U keycloak"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + + keycloak: + image: quay.io/keycloak/keycloak:23.0 + container_name: keycloak_demo + command: start-dev + restart: unless-stopped + environment: + # Admin credentials + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + + # Database configuration + KC_DB: postgres + KC_DB_URL: jdbc:postgresql://keycloak_db:5432/keycloak + KC_DB_USERNAME: keycloak + KC_DB_PASSWORD: keycloak_password + + # Server configuration + KC_HOSTNAME_STRICT: false + KC_HOSTNAME_STRICT_HTTPS: false + KC_HTTP_ENABLED: true + KC_HEALTH_ENABLED: true + KC_METRICS_ENABLED: true + + # Proxy settings + KC_PROXY: edge + KC_LOG_LEVEL: INFO + ports: + - "8080:8080" + depends_on: + keycloak_db: + condition: service_healthy + healthcheck: + test: + [ + "CMD-SHELL", + "exec 3<>/dev/tcp/localhost/8080 && echo -e 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q '200 OK'", + ] + interval: 30s + timeout: 10s + retries: 5 + start_period: 90s + volumes: + - keycloak_data:/opt/keycloak/data + +configs: + qdrant_config: + content: | + log_level: INFO + +volumes: + db_demo_data: + driver: local + qdrant_demo_data: + driver: local + keycloak_db_data: + driver: local + keycloak_data: + driver: local diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 0c8e7a5..bddf54d 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -4,58 +4,68 @@ services: context: . dockerfile: ai.dockerfile ports: - - "5050:8000" + - "5002:8000" env_file: - .env environment: - PYTHONPATH=/app/src - - PYTHONDONTWRITEBYTECODE=1 - - PYTHONUNBUFFERED=1 - volumes: - - ./services/ai-service:/app - - ./test_samples:/app/test_samples - - /app/.venv depends_on: vector_db: - condition: service_healthy + condition: service_started restart: unless-stopped - develop: - watch: - - action: sync - path: ./services/ai-service/src - target: /app/src + healthcheck: + test: + [ + "CMD", + "python", + "-c", + "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s frontend: build: context: . dockerfile: next.dockerfile - target: development ports: - - "5001:3001" + - "5001:3000" env_file: - .env environment: - - NODE_ENV=development - volumes: - - ./apps/tanstack-start:/app/apps/tanstack-start - - ./packages:/app/packages - - ./tooling:/app/tooling - - /app/node_modules - - /app/apps/tanstack-start/node_modules + - NODE_ENV=production + - AI_HOST=ai_service + - AI_SERVICE_URL=http://ai_service:8000 + - POSTGRES_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@application_db:5432/${POSTGRES_DB:-app_db} depends_on: - - ai_service + ai_service: + condition: service_started + application_db: + condition: service_healthy restart: unless-stopped - develop: - watch: - - action: sync - path: ./apps/tanstack-start/src - target: /app/apps/tanstack-start/src - - action: rebuild - path: ./apps/tanstack-start/package.json + + vector_db: + image: qdrant/qdrant:latest + restart: unless-stopped + container_name: qdrant + ports: + - "6333:6333" + - "6334:6334" + expose: + - 6333 + - 6334 + - 6335 + configs: + - source: qdrant_config + target: /qdrant/config/production.yaml + volumes: + - qdrant_data:/qdrant/storage application_db: image: postgres:15 - restart: unless-stopped + restart: always environment: POSTGRES_USER: ${POSTGRES_USER:-postgres} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} @@ -63,7 +73,7 @@ services: ports: - "5432:5432" volumes: - - db_dev_data:/var/lib/postgresql/data + - db_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 30s @@ -71,24 +81,13 @@ services: retries: 5 start_period: 30s - vector_db: - image: qdrant/qdrant:latest - restart: unless-stopped - container_name: qdrant_dev - ports: - - "6333:6333" - - "6334:6334" - volumes: - - qdrant_dev_data:/qdrant/storage - healthcheck: - test: ["CMD-SHELL", "bash -c ':> /dev/tcp/localhost/6333'"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s +configs: + qdrant_config: + content: | + log_level: INFO volumes: - db_dev_data: + db_data: driver: local - qdrant_dev_data: + qdrant_data: driver: local diff --git a/docker-compose.yml b/docker-compose.yml index 2976c80..6ca6841 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,10 +11,16 @@ services: - PYTHONPATH=/app/src depends_on: vector_db: - condition: service_healthy + condition: service_started restart: unless-stopped healthcheck: - test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"] + test: + [ + "CMD", + "python", + "-c", + "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')", + ] interval: 30s timeout: 10s retries: 3 @@ -55,19 +61,13 @@ services: target: /qdrant/config/production.yaml volumes: - qdrant_data:/qdrant/storage - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:6333/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 20s application_db: image: postgres:15 restart: always environment: POSTGRES_USER: ${POSTGRES_USER:-postgres} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} POSTGRES_DB: ${POSTGRES_DB:-app_db} ports: - "5432:5432" diff --git a/docs/DEMO_SETUP.md b/docs/DEMO_SETUP.md new file mode 100644 index 0000000..3f77b7e --- /dev/null +++ b/docs/DEMO_SETUP.md @@ -0,0 +1,199 @@ +# WorkOS Admin Portal Demo Setup Guide + +This guide will help you set up and demonstrate the WorkOS Admin Portal with Keycloak SSO integration. + +## Quick Start + +1. **Start the demo environment:** + + ```bash + docker compose -f docker-compose.demo.yml up + ``` + +2. **Access points:** + - **Frontend Application:** http://localhost:5001 + - **Keycloak Admin Console:** http://localhost:8080 + - Username: `admin` + - Password: `admin` + +## Setting Up Keycloak for WorkOS Demo + +### Step 1: Configure Keycloak Realm + +1. Navigate to http://localhost:8080 and login with `admin / admin` + +2. Create a new realm (or use the master realm): + - Click the realm dropdown (top-left) + - Click "Create Realm" + - Name: `governance-demo` + - Click "Create" + +### Step 2: Configure SAML/OIDC Client for WorkOS + +#### For SAML: + +1. Go to **Clients** โ†’ **Create client** +2. Set: + - Client type: `SAML` + - Client ID: `workos-governance-app` +3. Click **Next**, then **Save** +4. Configure the client: + - Valid redirect URIs: `https://api.workos.com/sso/saml/acs/*` + - Master SAML Processing URL: `https://api.workos.com/sso/saml/acs` + - Name ID format: `email` +5. Save the configuration + +#### For OpenID Connect (OIDC): + +1. Go to **Clients** โ†’ **Create client** +2. Set: + - Client type: `OpenID Connect` + - Client ID: `workos-governance-app` +3. Click **Next** +4. Configure: + - Client authentication: `ON` + - Valid redirect URIs: `https://api.workos.com/sso/oauth/callback` + - Web origins: `http://localhost:5001` +5. Save and note the **Client Secret** from the Credentials tab + +### Step 3: Create Test Users + +1. Navigate to **Users** โ†’ **Create new user** +2. Create demo users: + - Username: `demo.user@company.com` + - Email: `demo.user@company.com` + - First name: `Demo` + - Last name: `User` + - Email verified: `ON` +3. Click **Create** +4. Set password: + - Go to **Credentials** tab + - Click **Set password** + - Password: `demo123` + - Temporary: `OFF` +5. Repeat for additional demo users (e.g., admin, manager, employee) + +### Step 4: Configure WorkOS Admin Portal + +1. **Get Keycloak metadata URL:** + - For SAML: `http://localhost:8080/realms/governance-demo/protocol/saml/descriptor` + - For OIDC: `http://localhost:8080/realms/governance-demo/.well-known/openid-configuration` + +2. **In WorkOS Dashboard** (https://dashboard.workos.com): + - Create an Organization + - Navigate to **SSO** โ†’ **Connections** โ†’ **Create Connection** + - Choose **SAML** or **OIDC** + - Enter the Keycloak metadata URL or configure manually: + - **SAML Endpoint:** `http://localhost:8080/realms/governance-demo/protocol/saml` + - **OIDC Discovery:** `http://localhost:8080/realms/governance-demo/.well-known/openid-configuration` + +3. **Configure Domain:** + - Add a domain (e.g., `company.com`) + - Link it to the Keycloak connection + +### Step 5: Test the Admin Portal + +1. **Access the WorkOS Admin Portal:** + + ```javascript + // Generate Admin Portal link in your application + const adminPortalUrl = `https://api.workos.com/portal/launch? + client_id=${WORKOS_CLIENT_ID}& + intent=sso& + organization=${organizationId}& + return_url=${encodeURIComponent("http://localhost:5001/dashboard")}`; + ``` + +2. **Via API:** + ```bash + curl -X POST https://api.workos.com/user_management/portal/generate_link \ + -H "Authorization: Bearer ${WORKOS_API_KEY}" \ + -H "Content-Type: application/json" \ + -d '{ + "organization": "org_...", + "intent": "sso" + }' + ``` + +## Demo Flow + +### For Your Presentation: + +1. **Show the Admin Portal UI:** + - "This is the WorkOS Admin Portal - IT admins can configure SSO without developer involvement" + +2. **Configure Keycloak SSO:** + - Show how an admin would paste the Keycloak SAML/OIDC metadata + - Demonstrate domain configuration + - Show the connection testing feature + +3. **Test SSO Login:** + - Navigate to your app's login + - Enter a user email with the configured domain + - Show the automatic redirect to Keycloak + - Complete authentication + - Show successful login to your app + +4. **Highlight Key Features:** + - Self-service SSO configuration + - No code deployments needed + - Support for multiple identity providers + - Domain-based routing + - User provisioning (SCIM) + +## Troubleshooting + +### Keycloak not accessible + +```bash +# Check if Keycloak is healthy +docker ps +docker logs keycloak_demo + +# Wait for Keycloak to fully start (can take 60-90 seconds) +``` + +### WorkOS can't reach Keycloak + +- Use ngrok or similar to expose Keycloak publicly for the demo: + ```bash + ngrok http 8080 + # Use the ngrok URL in WorkOS configuration + ``` + +### Reset everything + +```bash +docker compose -f docker-compose.demo.yml down -v +docker compose -f docker-compose.demo.yml up +``` + +## Environment Variables + +Make sure your `.env` file contains: + +```env +# WorkOS Configuration +WORKOS_CLIENT_ID=client_01KGHB6BFE17CZKTXGCH52J72X +WORKOS_API_KEY=sk_test_... +WORKOS_REDIRECT_URI=http://localhost:5001/api/auth/callback +WORKOS_COOKIE_PASSWORD=your-secure-cookie-password-here + +# Other required variables +POSTGRES_USER=postgres +POSTGRES_PASSWORD=your-db-password +POSTGRES_DB=app_db +``` + +## Useful Keycloak Endpoints + +- **Admin Console:** http://localhost:8080/admin +- **Account Console:** http://localhost:8080/realms/governance-demo/account +- **SAML Metadata:** http://localhost:8080/realms/governance-demo/protocol/saml/descriptor +- **OIDC Discovery:** http://localhost:8080/realms/governance-demo/.well-known/openid-configuration + +## Additional Resources + +- [WorkOS Admin Portal Docs](https://workos.com/docs/user-management/admin-portal) +- [Keycloak Documentation](https://www.keycloak.org/documentation) +- [WorkOS SSO QuickStart](https://workos.com/docs/sso/guide) diff --git a/docs/RBAC_SETUP.md b/docs/RBAC_SETUP.md new file mode 100644 index 0000000..44517b3 --- /dev/null +++ b/docs/RBAC_SETUP.md @@ -0,0 +1,199 @@ +# RBAC Configuration with WorkOS + +This project uses Role-Based Access Control (RBAC) with WorkOS to manage permissions for submissions. + +## Permissions + +The following permissions are configured for submissions: + +- `submissions:read` - View submissions and their details (access to Admin Dashboard) +- `submissions:write` - Create new submissions (access to Submit page) +- `submissions:delete` - Delete existing submissions + +## Roles + +You should configure the following roles in your WorkOS dashboard: + +### Admin +- Can view, create, and delete submissions +- Permissions: `submissions:read`, `submissions:write`, `submissions:delete` + +### Manager +- Can view and create submissions +- Permissions: `submissions:read`, `submissions:write` + +### Member (Default) +- Can only create submissions (cannot view existing ones) +- Permissions: `submissions:write` + +**Note**: The Member role has write permissions but not read permissions. This means members can submit new evaluations but cannot view the admin dashboard or existing submissions. + +## Setup Instructions + +### 1. Create Permissions in WorkOS Dashboard + +1. Go to [WorkOS Dashboard](https://dashboard.workos.com/) > **Roles & Permissions** +2. Click **"Create Permission"** and add each permission: + - `submissions:read` + - `submissions:write` + - `submissions:delete` + +### 2. Create Roles in WorkOS Dashboard + +1. In the same **Roles & Permissions** page, click **"Create Role"** +2. Create the following roles with their respective permissions: + + **Admin Role:** + - Name: `admin` + - Permissions: `submissions:read`, `submissions:write`, `submissions:delete` + + **Manager Role:** + - Name: `manager` + - Permissions: `submissions:read`, `submissions:write` + + **Member Role:** + - Name: `member` + - Permissions: `submissions:write` + - Set as default role (click ellipsis โ†’ "Set as default") + +### 3. Assign Roles to Users + +#### Manual Assignment +1. Go to **WorkOS Dashboard** > **Users** +2. Create or select a user +3. Go to **Organizations** > Select your organization > **Users** tab +4. Add users to the organization +5. For each user, click ellipsis โ†’ **"Edit role"** โ†’ Select appropriate role + +#### Automatic Assignment via SSO +If you're using SSO with Directory Sync, you can map identity provider groups to roles: +- See [Group to Role Mapping](https://workos.com/blog/group-to-role-mapping) +- Configure in **WorkOS Dashboard** > **Directory Sync** > Your directory > **Role Mapping** + +## Implementation + +### How Permissions Work + +1. **Authentication**: Users authenticate via WorkOS AuthKit +2. **Access Token**: WorkOS includes role permissions in the JWT access token +3. **Backend Check**: The tRPC middleware extracts permissions from the session +4. **Frontend Check**: Routes check permissions in `beforeLoad` hooks +5. **Authorization**: Each endpoint/page checks for required permissions before access + +### Code Structure + +#### Backend +- **Permissions Definition**: `packages/api/src/lib/workos.ts` +- **Permission Middleware**: `packages/api/src/trpc.ts` - `createPermissionProcedure()` +- **Submission Routes**: `packages/api/src/router/submission.ts` + +#### Frontend +- **Permission Utilities**: `apps/tanstack-start/src/lib/permissions.ts` +- **Protected Routes**: + - `apps/tanstack-start/src/routes/admin/index.tsx` + - `apps/tanstack-start/src/routes/admin/ndi.tsx` + - `apps/tanstack-start/src/routes/admin/company.$companyId.tsx` + - `apps/tanstack-start/src/routes/submit.tsx` +- **Conditional UI**: `apps/tanstack-start/src/component/layout/Header.tsx` + +### Example Usage + +#### Backend Permission Check +```typescript +// Create a procedure that requires submissions:write permission +const createSubmission = createPermissionProcedure([SUBMISSION_PERMISSIONS.WRITE]) + .mutation(async ({ ctx, input }) => { + // Only users with submissions:write can access this + }); +``` + +#### Frontend Route Protection +```typescript +export const Route = createFileRoute("/admin/")({ + beforeLoad: async () => { + const auth = await getAuth(); + + if (!auth.user) { + throw redirect({ to: "/" }); + } + + if (!hasPermission(auth.permissions, SUBMISSION_PERMISSIONS.READ)) { + throw redirect({ + to: "/", + search: { error: "insufficient_permissions" } + }); + } + }, + component: RouteComponent, +}); +``` + +## Testing Permissions + +1. **Create test users** with different roles in WorkOS +2. **Sign in** as each user +3. **Test operations**: + - **Member** should only be able to submit new evaluations (access to `/submit` page) + - โŒ No access to Admin Dashboard + - โŒ No "Admin Dashboard" link in header + - โœ… Can access Submit page + - **Manager** should be able to view and create submissions + - โœ… Access to Admin Dashboard (`/admin`) + - โœ… Can submit new evaluations + - โœ… Can view all submissions + - **Admin** should be able to view, create, and delete submissions + - โœ… Full access to all features + - โœ… Can delete submissions (API endpoint available) + +## Environment Variables + +Ensure the following environment variables are set: + +```env +WORKOS_API_KEY=sk_... +WORKOS_CLIENT_ID=client_... +WORKOS_REDIRECT_URI=http://localhost:3000/api/auth/callback +WORKOS_COOKIE_PASSWORD=... # At least 32 characters +``` + +## API Endpoints & Required Permissions + +| Endpoint | Method | Permission | Description | +|----------|--------|------------|-------------| +| `submission.getAll` | Query | `submissions:read` | List all submissions | +| `submission.getById` | Query | `submissions:read` | Get submission details | +| `submission.create` | Mutation | `submissions:write` | Create new submission | +| `submission.delete` | Mutation | `submissions:delete` | Delete submission | + +## Frontend Routes & Required Permissions + +| Route | Permission | Description | +|-------|------------|-------------| +| `/` | None | Home page (shows error if redirected) | +| `/submit` | `submissions:write` | Submit new evaluation | +| `/admin` | `submissions:read` | Admin dashboard index | +| `/admin/ndi` | `submissions:read` | View NDI submissions | +| `/admin/company/:id` | `submissions:read` | View company details | + +## Error Handling + +### Backend Errors +When a user lacks required permissions: +- **Status**: 403 FORBIDDEN +- **Error Message**: "Missing required permissions: submissions:write" + +When a user is not authenticated: +- **Status**: 401 UNAUTHORIZED + +### Frontend Behavior +When a user lacks required permissions: +- **Redirected to**: Home page (`/`) +- **Error displayed**: "Insufficient Permissions" banner +- **UI changes**: Admin Dashboard link hidden from header + +## Resources + +- [WorkOS RBAC Documentation](https://workos.com/docs/rbac) +- [WorkOS Roles & Permissions](https://workos.com/docs/user-management/roles-and-permissions) +- [RBAC with WorkOS and Node Tutorial](https://workos.com/blog/rbac-with-workos-and-node) +- [Identity Provider Role Assignment](https://workos.com/docs/rbac/idp-role-assignment) diff --git a/packages/api/package.json b/packages/api/package.json index 0e522b8..d169caf 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -23,6 +23,7 @@ "@governance/validators": "workspace:*", "@hey-api/client-fetch": "^0.13.1", "@trpc/server": "catalog:", + "@workos-inc/node": "^8.1.0", "drizzle-orm": "^0.44.7", "superjson": "2.2.3", "zod": "catalog:" diff --git a/packages/api/src/ai-client/client.gen.ts b/packages/api/src/ai-client/client.gen.ts index 49f87f3..13eb4d3 100644 --- a/packages/api/src/ai-client/client.gen.ts +++ b/packages/api/src/ai-client/client.gen.ts @@ -1,7 +1,8 @@ -// This file is auto-generated by @hey-api/openapi-ts +import type { ClientOptions, Config } from "./client"; +import type { ClientOptions as ClientOptions2 } from "./types.gen"; +import { createClient, createConfig } from "./client"; -import { type ClientOptions, type Config, createClient, createConfig } from './client'; -import type { ClientOptions as ClientOptions2 } from './types.gen'; +// This file is auto-generated by @hey-api/openapi-ts /** * The `createClientConfig()` function will be called on client initialization @@ -11,6 +12,10 @@ import type { ClientOptions as ClientOptions2 } from './types.gen'; * `setConfig()`. This is useful for example if you're using Next.js * to ensure your client always has the correct values. */ -export type CreateClientConfig = (override?: Config) => Config & T>; +export type CreateClientConfig = ( + override?: Config, +) => Config & T>; -export const client = createClient(createConfig({ baseUrl: 'http://localhost:8000' })); +export const client = createClient( + createConfig({ baseUrl: "http://localhost:8000" }), +); diff --git a/packages/api/src/ai-client/client/client.gen.ts b/packages/api/src/ai-client/client/client.gen.ts index d2e55a1..64254d0 100644 --- a/packages/api/src/ai-client/client/client.gen.ts +++ b/packages/api/src/ai-client/client/client.gen.ts @@ -1,9 +1,14 @@ // This file is auto-generated by @hey-api/openapi-ts -import { createSseClient } from '../core/serverSentEvents.gen'; -import type { HttpMethod } from '../core/types.gen'; -import { getValidRequestBody } from '../core/utils.gen'; -import type { Client, Config, RequestOptions, ResolvedRequestOptions } from './types.gen'; +import type { HttpMethod } from "../core/types.gen"; +import type { + Client, + Config, + RequestOptions, + ResolvedRequestOptions, +} from "./types.gen"; +import { createSseClient } from "../core/serverSentEvents.gen"; +import { getValidRequestBody } from "../core/utils.gen"; import { buildUrl, createConfig, @@ -12,9 +17,9 @@ import { mergeConfigs, mergeHeaders, setAuthParams, -} from './utils.gen'; +} from "./utils.gen"; -type ReqInit = Omit & { +type ReqInit = Omit & { body?: any; headers: ReturnType; }; @@ -29,7 +34,12 @@ export const createClient = (config: Config = {}): Client => { return getConfig(); }; - const interceptors = createInterceptors(); + const interceptors = createInterceptors< + Request, + Response, + unknown, + ResolvedRequestOptions + >(); const beforeRequest = async (options: RequestOptions) => { const opts = { @@ -56,8 +66,8 @@ export const createClient = (config: Config = {}): Client => { } // remove Content-Type header if body is empty to avoid sending invalid requests - if (opts.body === undefined || opts.serializedBody === '') { - opts.headers.delete('Content-Type'); + if (opts.body === undefined || opts.serializedBody === "") { + opts.headers.delete("Content-Type"); } const url = buildUrl(opts); @@ -65,11 +75,11 @@ export const createClient = (config: Config = {}): Client => { return { opts, url }; }; - const request: Client['request'] = async (options) => { + const request: Client["request"] = async (options) => { // @ts-expect-error const { opts, url } = await beforeRequest(options); const requestInit: ReqInit = { - redirect: 'follow', + redirect: "follow", ...opts, body: getValidRequestBody(opts), }; @@ -95,7 +105,12 @@ export const createClient = (config: Config = {}): Client => { for (const fn of interceptors.error.fns) { if (fn) { - finalError = (await fn(error, undefined as any, request, opts)) as unknown; + finalError = (await fn( + error, + undefined as any, + request, + opts, + )) as unknown; } } @@ -106,7 +121,7 @@ export const createClient = (config: Config = {}): Client => { } // Return error response - return opts.responseStyle === 'data' + return opts.responseStyle === "data" ? undefined : { error: finalError, @@ -128,30 +143,33 @@ export const createClient = (config: Config = {}): Client => { if (response.ok) { const parseAs = - (opts.parseAs === 'auto' - ? getParseAs(response.headers.get('Content-Type')) - : opts.parseAs) ?? 'json'; - - if (response.status === 204 || response.headers.get('Content-Length') === '0') { + (opts.parseAs === "auto" + ? getParseAs(response.headers.get("Content-Type")) + : opts.parseAs) ?? "json"; + + if ( + response.status === 204 || + response.headers.get("Content-Length") === "0" + ) { let emptyData: any; switch (parseAs) { - case 'arrayBuffer': - case 'blob': - case 'text': + case "arrayBuffer": + case "blob": + case "text": emptyData = await response[parseAs](); break; - case 'formData': + case "formData": emptyData = new FormData(); break; - case 'stream': + case "stream": emptyData = response.body; break; - case 'json': + case "json": default: emptyData = {}; break; } - return opts.responseStyle === 'data' + return opts.responseStyle === "data" ? emptyData : { data: emptyData, @@ -161,21 +179,21 @@ export const createClient = (config: Config = {}): Client => { let data: any; switch (parseAs) { - case 'arrayBuffer': - case 'blob': - case 'formData': - case 'text': + case "arrayBuffer": + case "blob": + case "formData": + case "text": data = await response[parseAs](); break; - case 'json': { + case "json": { // Some servers return 200 with no Content-Length and empty body. // response.json() would throw; read as text and parse if non-empty. const text = await response.text(); data = text ? JSON.parse(text) : {}; break; } - case 'stream': - return opts.responseStyle === 'data' + case "stream": + return opts.responseStyle === "data" ? response.body : { data: response.body, @@ -183,7 +201,7 @@ export const createClient = (config: Config = {}): Client => { }; } - if (parseAs === 'json') { + if (parseAs === "json") { if (opts.responseValidator) { await opts.responseValidator(data); } @@ -193,7 +211,7 @@ export const createClient = (config: Config = {}): Client => { } } - return opts.responseStyle === 'data' + return opts.responseStyle === "data" ? data : { data, @@ -226,7 +244,7 @@ export const createClient = (config: Config = {}): Client => { } // TODO: we probably want to return error and improve types - return opts.responseStyle === 'data' + return opts.responseStyle === "data" ? undefined : { error: finalError, @@ -234,55 +252,60 @@ export const createClient = (config: Config = {}): Client => { }; }; - const makeMethodFn = (method: Uppercase) => (options: RequestOptions) => - request({ ...options, method }); + const makeMethodFn = + (method: Uppercase) => (options: RequestOptions) => + request({ ...options, method }); - const makeSseFn = (method: Uppercase) => async (options: RequestOptions) => { - const { opts, url } = await beforeRequest(options); - return createSseClient({ - ...opts, - body: opts.body as BodyInit | null | undefined, - headers: opts.headers as unknown as Record, - method, - onRequest: async (url, init) => { - let request = new Request(url, init); - for (const fn of interceptors.request.fns) { - if (fn) { - request = await fn(request, opts); + const makeSseFn = + (method: Uppercase) => async (options: RequestOptions) => { + const { opts, url } = await beforeRequest(options); + return createSseClient({ + ...opts, + body: opts.body as BodyInit | null | undefined, + headers: opts.headers as unknown as Record, + method, + onRequest: async (url, init) => { + let request = new Request(url, init); + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } } - } - return request; - }, - serializedBody: getValidRequestBody(opts) as BodyInit | null | undefined, - url, - }); - }; + return request; + }, + serializedBody: getValidRequestBody(opts) as + | BodyInit + | null + | undefined, + url, + }); + }; return { buildUrl, - connect: makeMethodFn('CONNECT'), - delete: makeMethodFn('DELETE'), - get: makeMethodFn('GET'), + connect: makeMethodFn("CONNECT"), + delete: makeMethodFn("DELETE"), + get: makeMethodFn("GET"), getConfig, - head: makeMethodFn('HEAD'), + head: makeMethodFn("HEAD"), interceptors, - options: makeMethodFn('OPTIONS'), - patch: makeMethodFn('PATCH'), - post: makeMethodFn('POST'), - put: makeMethodFn('PUT'), + options: makeMethodFn("OPTIONS"), + patch: makeMethodFn("PATCH"), + post: makeMethodFn("POST"), + put: makeMethodFn("PUT"), request, setConfig, sse: { - connect: makeSseFn('CONNECT'), - delete: makeSseFn('DELETE'), - get: makeSseFn('GET'), - head: makeSseFn('HEAD'), - options: makeSseFn('OPTIONS'), - patch: makeSseFn('PATCH'), - post: makeSseFn('POST'), - put: makeSseFn('PUT'), - trace: makeSseFn('TRACE'), + connect: makeSseFn("CONNECT"), + delete: makeSseFn("DELETE"), + get: makeSseFn("GET"), + head: makeSseFn("HEAD"), + options: makeSseFn("OPTIONS"), + patch: makeSseFn("PATCH"), + post: makeSseFn("POST"), + put: makeSseFn("PUT"), + trace: makeSseFn("TRACE"), }, - trace: makeMethodFn('TRACE'), + trace: makeMethodFn("TRACE"), } as Client; }; diff --git a/packages/api/src/ai-client/client/index.ts b/packages/api/src/ai-client/client/index.ts index b295ede..4602312 100644 --- a/packages/api/src/ai-client/client/index.ts +++ b/packages/api/src/ai-client/client/index.ts @@ -1,15 +1,15 @@ // This file is auto-generated by @hey-api/openapi-ts -export type { Auth } from '../core/auth.gen'; -export type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +export type { Auth } from "../core/auth.gen"; +export type { QuerySerializerOptions } from "../core/bodySerializer.gen"; export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, -} from '../core/bodySerializer.gen'; -export { buildClientParams } from '../core/params.gen'; -export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; -export { createClient } from './client.gen'; +} from "../core/bodySerializer.gen"; +export { buildClientParams } from "../core/params.gen"; +export { serializeQueryKeyValue } from "../core/queryKeySerializer.gen"; +export { createClient } from "./client.gen"; export type { Client, ClientOptions, @@ -21,5 +21,5 @@ export type { ResolvedRequestOptions, ResponseStyle, TDataShape, -} from './types.gen'; -export { createConfig, mergeHeaders } from './utils.gen'; +} from "./types.gen"; +export { createConfig, mergeHeaders } from "./utils.gen"; diff --git a/packages/api/src/ai-client/client/types.gen.ts b/packages/api/src/ai-client/client/types.gen.ts index cb6d0d5..5ff95be 100644 --- a/packages/api/src/ai-client/client/types.gen.ts +++ b/packages/api/src/ai-client/client/types.gen.ts @@ -1,21 +1,25 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Auth } from '../core/auth.gen'; +import type { Auth } from "../core/auth.gen"; import type { ServerSentEventsOptions, ServerSentEventsResult, -} from '../core/serverSentEvents.gen'; -import type { Client as CoreClient, Config as CoreConfig } from '../core/types.gen'; -import type { Middleware } from './utils.gen'; +} from "../core/serverSentEvents.gen"; +import type { + Client as CoreClient, + Config as CoreConfig, +} from "../core/types.gen"; +import type { Middleware } from "./utils.gen"; -export type ResponseStyle = 'data' | 'fields'; +export type ResponseStyle = "data" | "fields"; export interface Config - extends Omit, CoreConfig { + extends Omit, + CoreConfig { /** * Base URL for all requests made by this client. */ - baseUrl?: T['baseUrl']; + baseUrl?: T["baseUrl"]; /** * Fetch API implementation. You can use this option to provide a custom * fetch instance. @@ -38,7 +42,14 @@ export interface Config * * @default 'auto' */ - parseAs?: 'arrayBuffer' | 'auto' | 'blob' | 'formData' | 'json' | 'stream' | 'text'; + parseAs?: + | "arrayBuffer" + | "auto" + | "blob" + | "formData" + | "json" + | "stream" + | "text"; /** * Should we return only data or multiple fields (data, error, response, etc.)? * @@ -50,27 +61,25 @@ export interface Config * * @default false */ - throwOnError?: T['throwOnError']; + throwOnError?: T["throwOnError"]; } export interface RequestOptions< TData = unknown, - TResponseStyle extends ResponseStyle = 'fields', + TResponseStyle extends ResponseStyle = "fields", ThrowOnError extends boolean = boolean, Url extends string = string, -> - extends - Config<{ +> extends Config<{ responseStyle: TResponseStyle; throwOnError: ThrowOnError; }>, Pick< ServerSentEventsOptions, - | 'onSseError' - | 'onSseEvent' - | 'sseDefaultRetryDelay' - | 'sseMaxRetryAttempts' - | 'sseMaxRetryDelay' + | "onSseError" + | "onSseEvent" + | "sseDefaultRetryDelay" + | "sseMaxRetryAttempts" + | "sseMaxRetryDelay" > { /** * Any body that you want to add to your request. @@ -88,7 +97,7 @@ export interface RequestOptions< } export interface ResolvedRequestOptions< - TResponseStyle extends ResponseStyle = 'fields', + TResponseStyle extends ResponseStyle = "fields", ThrowOnError extends boolean = boolean, Url extends string = string, > extends RequestOptions { @@ -99,30 +108,40 @@ export type RequestResult< TData = unknown, TError = unknown, ThrowOnError extends boolean = boolean, - TResponseStyle extends ResponseStyle = 'fields', + TResponseStyle extends ResponseStyle = "fields", > = ThrowOnError extends true ? Promise< - TResponseStyle extends 'data' + TResponseStyle extends "data" ? TData extends Record ? TData[keyof TData] : TData : { - data: TData extends Record ? TData[keyof TData] : TData; + data: TData extends Record + ? TData[keyof TData] + : TData; request: Request; response: Response; } > : Promise< - TResponseStyle extends 'data' - ? (TData extends Record ? TData[keyof TData] : TData) | undefined + TResponseStyle extends "data" + ? + | (TData extends Record + ? TData[keyof TData] + : TData) + | undefined : ( | { - data: TData extends Record ? TData[keyof TData] : TData; + data: TData extends Record + ? TData[keyof TData] + : TData; error: undefined; } | { data: undefined; - error: TError extends Record ? TError[keyof TError] : TError; + error: TError extends Record + ? TError[keyof TError] + : TError; } ) & { request: Request; @@ -140,28 +159,31 @@ type MethodFn = < TData = unknown, TError = unknown, ThrowOnError extends boolean = false, - TResponseStyle extends ResponseStyle = 'fields', + TResponseStyle extends ResponseStyle = "fields", >( - options: Omit, 'method'>, + options: Omit, "method">, ) => RequestResult; type SseFn = < TData = unknown, TError = unknown, ThrowOnError extends boolean = false, - TResponseStyle extends ResponseStyle = 'fields', + TResponseStyle extends ResponseStyle = "fields", >( - options: Omit, 'method'>, + options: Omit, "method">, ) => Promise>; type RequestFn = < TData = unknown, TError = unknown, ThrowOnError extends boolean = false, - TResponseStyle extends ResponseStyle = 'fields', + TResponseStyle extends ResponseStyle = "fields", >( - options: Omit, 'method'> & - Pick>, 'method'>, + options: Omit, "method"> & + Pick< + Required>, + "method" + >, ) => RequestResult; type BuildUrlFn = < @@ -175,7 +197,13 @@ type BuildUrlFn = < options: TData & Options, ) => string; -export type Client = CoreClient & { +export type Client = CoreClient< + RequestFn, + Config, + MethodFn, + BuildUrlFn, + SseFn +> & { interceptors: Middleware; }; @@ -205,9 +233,9 @@ export type Options< TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean, TResponse = unknown, - TResponseStyle extends ResponseStyle = 'fields', + TResponseStyle extends ResponseStyle = "fields", > = OmitKeys< RequestOptions, - 'body' | 'path' | 'query' | 'url' + "body" | "path" | "query" | "url" > & - ([TData] extends [never] ? unknown : Omit); + ([TData] extends [never] ? unknown : Omit); diff --git a/packages/api/src/ai-client/client/utils.gen.ts b/packages/api/src/ai-client/client/utils.gen.ts index b4bd243..0aecd39 100644 --- a/packages/api/src/ai-client/client/utils.gen.ts +++ b/packages/api/src/ai-client/client/utils.gen.ts @@ -1,15 +1,20 @@ // This file is auto-generated by @hey-api/openapi-ts -import { getAuthToken } from '../core/auth.gen'; -import type { QuerySerializerOptions } from '../core/bodySerializer.gen'; -import { jsonBodySerializer } from '../core/bodySerializer.gen'; +import type { QuerySerializerOptions } from "../core/bodySerializer.gen"; +import type { + Client, + ClientOptions, + Config, + RequestOptions, +} from "./types.gen"; +import { getAuthToken } from "../core/auth.gen"; +import { jsonBodySerializer } from "../core/bodySerializer.gen"; import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam, -} from '../core/pathSerializer.gen'; -import { getUrl } from '../core/utils.gen'; -import type { Client, ClientOptions, Config, RequestOptions } from './types.gen'; +} from "../core/pathSerializer.gen"; +import { getUrl } from "../core/utils.gen"; export const createQuerySerializer = ({ parameters = {}, @@ -17,7 +22,7 @@ export const createQuerySerializer = ({ }: QuerySerializerOptions = {}) => { const querySerializer = (queryParams: T) => { const search: string[] = []; - if (queryParams && typeof queryParams === 'object') { + if (queryParams && typeof queryParams === "object") { for (const name in queryParams) { const value = queryParams[name]; @@ -32,17 +37,17 @@ export const createQuerySerializer = ({ allowReserved: options.allowReserved, explode: true, name, - style: 'form', + style: "form", value, ...options.array, }); if (serializedArray) search.push(serializedArray); - } else if (typeof value === 'object') { + } else if (typeof value === "object") { const serializedObject = serializeObjectParam({ allowReserved: options.allowReserved, explode: true, name, - style: 'deepObject', + style: "deepObject", value: value as Record, ...options.object, }); @@ -57,7 +62,7 @@ export const createQuerySerializer = ({ } } } - return search.join('&'); + return search.join("&"); }; return querySerializer; }; @@ -65,42 +70,49 @@ export const createQuerySerializer = ({ /** * Infers parseAs value from provided Content-Type header. */ -export const getParseAs = (contentType: string | null): Exclude => { +export const getParseAs = ( + contentType: string | null, +): Exclude => { if (!contentType) { // If no Content-Type header is provided, the best we can do is return the raw response body, // which is effectively the same as the 'stream' option. - return 'stream'; + return "stream"; } - const cleanContent = contentType.split(';')[0]?.trim(); + const cleanContent = contentType.split(";")[0]?.trim(); if (!cleanContent) { return; } - if (cleanContent.startsWith('application/json') || cleanContent.endsWith('+json')) { - return 'json'; + if ( + cleanContent.startsWith("application/json") || + cleanContent.endsWith("+json") + ) { + return "json"; } - if (cleanContent === 'multipart/form-data') { - return 'formData'; + if (cleanContent === "multipart/form-data") { + return "formData"; } if ( - ['application/', 'audio/', 'image/', 'video/'].some((type) => cleanContent.startsWith(type)) + ["application/", "audio/", "image/", "video/"].some((type) => + cleanContent.startsWith(type), + ) ) { - return 'blob'; + return "blob"; } - if (cleanContent.startsWith('text/')) { - return 'text'; + if (cleanContent.startsWith("text/")) { + return "text"; } return; }; const checkForExistence = ( - options: Pick & { + options: Pick & { headers: Headers; }, name?: string, @@ -111,7 +123,7 @@ const checkForExistence = ( if ( options.headers.has(name) || options.query?.[name] || - options.headers.get('Cookie')?.includes(`${name}=`) + options.headers.get("Cookie")?.includes(`${name}=`) ) { return true; } @@ -121,8 +133,8 @@ const checkForExistence = ( export const setAuthParams = async ({ security, ...options -}: Pick, 'security'> & - Pick & { +}: Pick, "security"> & + Pick & { headers: Headers; }) => { for (const auth of security) { @@ -136,19 +148,19 @@ export const setAuthParams = async ({ continue; } - const name = auth.name ?? 'Authorization'; + const name = auth.name ?? "Authorization"; switch (auth.in) { - case 'query': + case "query": if (!options.query) { options.query = {}; } options.query[name] = token; break; - case 'cookie': - options.headers.append('Cookie', `${name}=${token}`); + case "cookie": + options.headers.append("Cookie", `${name}=${token}`); break; - case 'header': + case "header": default: options.headers.set(name, token); break; @@ -156,13 +168,13 @@ export const setAuthParams = async ({ } }; -export const buildUrl: Client['buildUrl'] = (options) => +export const buildUrl: Client["buildUrl"] = (options) => getUrl({ baseUrl: options.baseUrl as string, path: options.path, query: options.query, querySerializer: - typeof options.querySerializer === 'function' + typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer), url: options.url, @@ -170,7 +182,7 @@ export const buildUrl: Client['buildUrl'] = (options) => export const mergeConfigs = (a: Config, b: Config): Config => { const config = { ...a, ...b }; - if (config.baseUrl?.endsWith('/')) { + if (config.baseUrl?.endsWith("/")) { config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); } config.headers = mergeHeaders(a.headers, b.headers); @@ -186,7 +198,7 @@ const headersEntries = (headers: Headers): Array<[string, string]> => { }; export const mergeHeaders = ( - ...headers: Array['headers'] | undefined> + ...headers: Array["headers"] | undefined> ): Headers => { const mergedHeaders = new Headers(); for (const header of headers) { @@ -194,7 +206,10 @@ export const mergeHeaders = ( continue; } - const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header); + const iterator = + header instanceof Headers + ? headersEntries(header) + : Object.entries(header); for (const [key, value] of iterator) { if (value === null) { @@ -208,7 +223,7 @@ export const mergeHeaders = ( // content value in OpenAPI specification is 'application/json' mergedHeaders.set( key, - typeof value === 'object' ? JSON.stringify(value) : (value as string), + typeof value === "object" ? JSON.stringify(value) : (value as string), ); } } @@ -223,7 +238,10 @@ type ErrInterceptor = ( options: Options, ) => Err | Promise; -type ReqInterceptor = (request: Req, options: Options) => Req | Promise; +type ReqInterceptor = ( + request: Req, + options: Options, +) => Req | Promise; type ResInterceptor = ( response: Res, @@ -251,13 +269,16 @@ class Interceptors { } getInterceptorIndex(id: number | Interceptor): number { - if (typeof id === 'number') { + if (typeof id === "number") { return this.fns[id] ? id : -1; } return this.fns.indexOf(id); } - update(id: number | Interceptor, fn: Interceptor): number | Interceptor | false { + update( + id: number | Interceptor, + fn: Interceptor, + ): number | Interceptor | false { const index = this.getInterceptorIndex(id); if (this.fns[index]) { this.fns[index] = fn; @@ -293,16 +314,16 @@ const defaultQuerySerializer = createQuerySerializer({ allowReserved: false, array: { explode: true, - style: 'form', + style: "form", }, object: { explode: true, - style: 'deepObject', + style: "deepObject", }, }); const defaultHeaders = { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }; export const createConfig = ( @@ -310,7 +331,7 @@ export const createConfig = ( ): Config & T> => ({ ...jsonBodySerializer, headers: defaultHeaders, - parseAs: 'auto', + parseAs: "auto", querySerializer: defaultQuerySerializer, ...override, }); diff --git a/packages/api/src/ai-client/core/auth.gen.ts b/packages/api/src/ai-client/core/auth.gen.ts index 3ebf994..3d74115 100644 --- a/packages/api/src/ai-client/core/auth.gen.ts +++ b/packages/api/src/ai-client/core/auth.gen.ts @@ -8,32 +8,33 @@ export interface Auth { * * @default 'header' */ - in?: 'header' | 'query' | 'cookie'; + in?: "header" | "query" | "cookie"; /** * Header or query parameter name. * * @default 'Authorization' */ name?: string; - scheme?: 'basic' | 'bearer'; - type: 'apiKey' | 'http'; + scheme?: "basic" | "bearer"; + type: "apiKey" | "http"; } export const getAuthToken = async ( auth: Auth, callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, ): Promise => { - const token = typeof callback === 'function' ? await callback(auth) : callback; + const token = + typeof callback === "function" ? await callback(auth) : callback; if (!token) { return; } - if (auth.scheme === 'bearer') { + if (auth.scheme === "bearer") { return `Bearer ${token}`; } - if (auth.scheme === 'basic') { + if (auth.scheme === "basic") { return `Basic ${btoa(token)}`; } diff --git a/packages/api/src/ai-client/core/bodySerializer.gen.ts b/packages/api/src/ai-client/core/bodySerializer.gen.ts index 8ad92c9..2ed9d6d 100644 --- a/packages/api/src/ai-client/core/bodySerializer.gen.ts +++ b/packages/api/src/ai-client/core/bodySerializer.gen.ts @@ -1,6 +1,10 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { ArrayStyle, ObjectStyle, SerializerOptions } from './pathSerializer.gen'; +import type { + ArrayStyle, + ObjectStyle, + SerializerOptions, +} from "./pathSerializer.gen"; export type QuerySerializer = (query: Record) => string; @@ -20,8 +24,12 @@ export type QuerySerializerOptions = QuerySerializerOptionsObject & { parameters?: Record; }; -const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => { - if (typeof value === 'string' || value instanceof Blob) { +const serializeFormDataPair = ( + data: FormData, + key: string, + value: unknown, +): void => { + if (typeof value === "string" || value instanceof Blob) { data.append(key, value); } else if (value instanceof Date) { data.append(key, value.toISOString()); @@ -30,8 +38,12 @@ const serializeFormDataPair = (data: FormData, key: string, value: unknown): voi } }; -const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value: unknown): void => { - if (typeof value === 'string') { +const serializeUrlSearchParamsPair = ( + data: URLSearchParams, + key: string, + value: unknown, +): void => { + if (typeof value === "string") { data.append(key, value); } else { data.append(key, JSON.stringify(value)); @@ -61,11 +73,15 @@ export const formDataBodySerializer = { export const jsonBodySerializer = { bodySerializer: (body: T): string => - JSON.stringify(body, (_key, value) => (typeof value === 'bigint' ? value.toString() : value)), + JSON.stringify(body, (_key, value) => + typeof value === "bigint" ? value.toString() : value, + ), }; export const urlSearchParamsBodySerializer = { - bodySerializer: | Array>>(body: T): string => { + bodySerializer: | Array>>( + body: T, + ): string => { const data = new URLSearchParams(); Object.entries(body).forEach(([key, value]) => { diff --git a/packages/api/src/ai-client/core/params.gen.ts b/packages/api/src/ai-client/core/params.gen.ts index 6099cab..fb9f992 100644 --- a/packages/api/src/ai-client/core/params.gen.ts +++ b/packages/api/src/ai-client/core/params.gen.ts @@ -1,10 +1,10 @@ // This file is auto-generated by @hey-api/openapi-ts -type Slot = 'body' | 'headers' | 'path' | 'query'; +type Slot = "body" | "headers" | "path" | "query"; export type Field = | { - in: Exclude; + in: Exclude; /** * Field name. This is the name we want the user to see and use. */ @@ -16,7 +16,7 @@ export type Field = map?: string; } | { - in: Extract; + in: Extract; /** * Key isn't required for bodies. */ @@ -43,10 +43,10 @@ export interface Fields { export type FieldsConfig = ReadonlyArray; const extraPrefixesMap: Record = { - $body_: 'body', - $headers_: 'headers', - $path_: 'path', - $query_: 'query', + $body_: "body", + $headers_: "headers", + $path_: "path", + $query_: "query", }; const extraPrefixes = Object.entries(extraPrefixesMap); @@ -68,14 +68,14 @@ const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { } for (const config of fields) { - if ('in' in config) { + if ("in" in config) { if (config.key) { map.set(config.key, { in: config.in, map: config.map, }); } - } else if ('key' in config) { + } else if ("key" in config) { map.set(config.key, { map: config.map, }); @@ -96,13 +96,16 @@ interface Params { const stripEmptySlots = (params: Params) => { for (const [slot, value] of Object.entries(params)) { - if (value && typeof value === 'object' && !Object.keys(value).length) { + if (value && typeof value === "object" && !Object.keys(value).length) { delete params[slot as Slot]; } } }; -export const buildClientParams = (args: ReadonlyArray, fields: FieldsConfig) => { +export const buildClientParams = ( + args: ReadonlyArray, + fields: FieldsConfig, +) => { const params: Params = { body: {}, headers: {}, @@ -123,7 +126,7 @@ export const buildClientParams = (args: ReadonlyArray, fields: FieldsCo continue; } - if ('in' in config) { + if ("in" in config) { if (config.key) { const field = map.get(config.key)!; const name = field.map || config.key; @@ -145,12 +148,16 @@ export const buildClientParams = (args: ReadonlyArray, fields: FieldsCo params[field.map] = value; } } else { - const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix)); + const extra = extraPrefixes.find(([prefix]) => + key.startsWith(prefix), + ); if (extra) { const [prefix, slot] = extra; - (params[slot] as Record)[key.slice(prefix.length)] = value; - } else if ('allowExtra' in config && config.allowExtra) { + (params[slot] as Record)[ + key.slice(prefix.length) + ] = value; + } else if ("allowExtra" in config && config.allowExtra) { for (const [slot, allowed] of Object.entries(config.allowExtra)) { if (allowed) { (params[slot as Slot] as Record)[key] = value; diff --git a/packages/api/src/ai-client/core/pathSerializer.gen.ts b/packages/api/src/ai-client/core/pathSerializer.gen.ts index 994b284..034aa68 100644 --- a/packages/api/src/ai-client/core/pathSerializer.gen.ts +++ b/packages/api/src/ai-client/core/pathSerializer.gen.ts @@ -1,6 +1,8 @@ // This file is auto-generated by @hey-api/openapi-ts -interface SerializeOptions extends SerializePrimitiveOptions, SerializerOptions {} +interface SerializeOptions + extends SerializePrimitiveOptions, + SerializerOptions {} interface SerializePrimitiveOptions { allowReserved?: boolean; @@ -15,10 +17,10 @@ export interface SerializerOptions { style: T; } -export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +export type ArrayStyle = "form" | "spaceDelimited" | "pipeDelimited"; export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; -type MatrixStyle = 'label' | 'matrix' | 'simple'; -export type ObjectStyle = 'form' | 'deepObject'; +type MatrixStyle = "label" | "matrix" | "simple"; +export type ObjectStyle = "form" | "deepObject"; type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; interface SerializePrimitiveParam extends SerializePrimitiveOptions { @@ -27,40 +29,40 @@ interface SerializePrimitiveParam extends SerializePrimitiveOptions { export const separatorArrayExplode = (style: ArraySeparatorStyle) => { switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; + case "label": + return "."; + case "matrix": + return ";"; + case "simple": + return ","; default: - return '&'; + return "&"; } }; export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { switch (style) { - case 'form': - return ','; - case 'pipeDelimited': - return '|'; - case 'spaceDelimited': - return '%20'; + case "form": + return ","; + case "pipeDelimited": + return "|"; + case "spaceDelimited": + return "%20"; default: - return ','; + return ","; } }; export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; + case "label": + return "."; + case "matrix": + return ";"; + case "simple": + return ","; default: - return '&'; + return "&"; } }; @@ -78,11 +80,11 @@ export const serializeArrayParam = ({ allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) ).join(separatorArrayNoExplode(style)); switch (style) { - case 'label': + case "label": return `.${joinedValues}`; - case 'matrix': + case "matrix": return `;${name}=${joinedValues}`; - case 'simple': + case "simple": return joinedValues; default: return `${name}=${joinedValues}`; @@ -92,7 +94,7 @@ export const serializeArrayParam = ({ const separator = separatorArrayExplode(style); const joinedValues = value .map((v) => { - if (style === 'label' || style === 'simple') { + if (style === "label" || style === "simple") { return allowReserved ? v : encodeURIComponent(v as string); } @@ -103,7 +105,9 @@ export const serializeArrayParam = ({ }); }) .join(separator); - return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues; + return style === "label" || style === "matrix" + ? separator + joinedValues + : joinedValues; }; export const serializePrimitiveParam = ({ @@ -112,12 +116,12 @@ export const serializePrimitiveParam = ({ value, }: SerializePrimitiveParam) => { if (value === undefined || value === null) { - return ''; + return ""; } - if (typeof value === 'object') { + if (typeof value === "object") { throw new Error( - 'Deeply-nested arrays/objects arenโ€™t supported. Provide your own `querySerializer()` to handle these.', + "Deeply-nested arrays/objects arenโ€™t supported. Provide your own `querySerializer()` to handle these.", ); } @@ -139,18 +143,22 @@ export const serializeObjectParam = ({ return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; } - if (style !== 'deepObject' && !explode) { + if (style !== "deepObject" && !explode) { let values: string[] = []; Object.entries(value).forEach(([key, v]) => { - values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)]; + values = [ + ...values, + key, + allowReserved ? (v as string) : encodeURIComponent(v as string), + ]; }); - const joinedValues = values.join(','); + const joinedValues = values.join(","); switch (style) { - case 'form': + case "form": return `${name}=${joinedValues}`; - case 'label': + case "label": return `.${joinedValues}`; - case 'matrix': + case "matrix": return `;${name}=${joinedValues}`; default: return joinedValues; @@ -162,10 +170,12 @@ export const serializeObjectParam = ({ .map(([key, v]) => serializePrimitiveParam({ allowReserved, - name: style === 'deepObject' ? `${name}[${key}]` : key, + name: style === "deepObject" ? `${name}[${key}]` : key, value: v as string, }), ) .join(separator); - return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues; + return style === "label" || style === "matrix" + ? separator + joinedValues + : joinedValues; }; diff --git a/packages/api/src/ai-client/core/queryKeySerializer.gen.ts b/packages/api/src/ai-client/core/queryKeySerializer.gen.ts index 5000df6..0713164 100644 --- a/packages/api/src/ai-client/core/queryKeySerializer.gen.ts +++ b/packages/api/src/ai-client/core/queryKeySerializer.gen.ts @@ -15,10 +15,14 @@ export type JsonValue = * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. */ export const queryKeyJsonReplacer = (_key: string, value: unknown) => { - if (value === undefined || typeof value === 'function' || typeof value === 'symbol') { + if ( + value === undefined || + typeof value === "function" || + typeof value === "symbol" + ) { return undefined; } - if (typeof value === 'bigint') { + if (typeof value === "bigint") { return value.toString(); } if (value instanceof Date) { @@ -46,7 +50,7 @@ export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { * Detects plain objects (including objects with a null prototype). */ const isPlainObject = (value: unknown): value is Record => { - if (value === null || typeof value !== 'object') { + if (value === null || typeof value !== "object") { return false; } const prototype = Object.getPrototypeOf(value as object); @@ -57,7 +61,9 @@ const isPlainObject = (value: unknown): value is Record => { * Turns URLSearchParams into a sorted JSON object for deterministic keys. */ const serializeSearchParams = (params: URLSearchParams): JsonValue => { - const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b)); + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); const result: Record = {}; for (const [key, value] of entries) { @@ -80,20 +86,30 @@ const serializeSearchParams = (params: URLSearchParams): JsonValue => { /** * Normalizes any accepted value into a JSON-friendly shape for query keys. */ -export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => { +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { if (value === null) { return null; } - if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + if ( + typeof value === "string" || + typeof value === "number" || + typeof value === "boolean" + ) { return value; } - if (value === undefined || typeof value === 'function' || typeof value === 'symbol') { + if ( + value === undefined || + typeof value === "function" || + typeof value === "symbol" + ) { return undefined; } - if (typeof value === 'bigint') { + if (typeof value === "bigint") { return value.toString(); } @@ -105,7 +121,10 @@ export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => return stringifyToJsonValue(value); } - if (typeof URLSearchParams !== 'undefined' && value instanceof URLSearchParams) { + if ( + typeof URLSearchParams !== "undefined" && + value instanceof URLSearchParams + ) { return serializeSearchParams(value); } diff --git a/packages/api/src/ai-client/core/serverSentEvents.gen.ts b/packages/api/src/ai-client/core/serverSentEvents.gen.ts index 6aa6cf0..60f825c 100644 --- a/packages/api/src/ai-client/core/serverSentEvents.gen.ts +++ b/packages/api/src/ai-client/core/serverSentEvents.gen.ts @@ -1,9 +1,12 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Config } from './types.gen'; +import type { Config } from "./types.gen"; -export type ServerSentEventsOptions = Omit & - Pick & { +export type ServerSentEventsOptions = Omit< + RequestInit, + "method" +> & + Pick & { /** * Fetch API implementation. You can use this option to provide a custom * fetch instance. @@ -32,7 +35,7 @@ export type ServerSentEventsOptions = Omit) => void; - serializedBody?: RequestInit['body']; + serializedBody?: RequestInit["body"]; /** * Default retry delay in milliseconds. * @@ -71,7 +74,11 @@ export interface StreamEvent { retry?: number; } -export type ServerSentEventsResult = { +export type ServerSentEventsResult< + TData = unknown, + TReturn = void, + TNext = unknown, +> = { stream: AsyncGenerator< TData extends Record ? TData[keyof TData] : TData, TReturn, @@ -94,7 +101,9 @@ export const createSseClient = ({ }: ServerSentEventsOptions): ServerSentEventsResult => { let lastEventId: string | undefined; - const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))); + const sleep = + sseSleepFn ?? + ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))); const createStream = async function* () { let retryDelay: number = sseDefaultRetryDelay ?? 3000; @@ -112,12 +121,12 @@ export const createSseClient = ({ : new Headers(options.headers as Record | undefined); if (lastEventId !== undefined) { - headers.set('Last-Event-ID', lastEventId); + headers.set("Last-Event-ID", lastEventId); } try { const requestInit: RequestInit = { - redirect: 'follow', + redirect: "follow", ...options, body: options.serializedBody, headers, @@ -132,13 +141,18 @@ export const createSseClient = ({ const _fetch = options.fetch ?? globalThis.fetch; const response = await _fetch(request); - if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`); + if (!response.ok) + throw new Error( + `SSE failed: ${response.status} ${response.statusText}`, + ); - if (!response.body) throw new Error('No body in SSE response'); + if (!response.body) throw new Error("No body in SSE response"); - const reader = response.body.pipeThrough(new TextDecoderStream()).getReader(); + const reader = response.body + .pipeThrough(new TextDecoderStream()) + .getReader(); - let buffer = ''; + let buffer = ""; const abortHandler = () => { try { @@ -148,7 +162,7 @@ export const createSseClient = ({ } }; - signal.addEventListener('abort', abortHandler); + signal.addEventListener("abort", abortHandler); try { while (true) { @@ -156,25 +170,28 @@ export const createSseClient = ({ if (done) break; buffer += value; // Normalize line endings: CRLF -> LF, then CR -> LF - buffer = buffer.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + buffer = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n"); - const chunks = buffer.split('\n\n'); - buffer = chunks.pop() ?? ''; + const chunks = buffer.split("\n\n"); + buffer = chunks.pop() ?? ""; for (const chunk of chunks) { - const lines = chunk.split('\n'); + const lines = chunk.split("\n"); const dataLines: Array = []; let eventName: string | undefined; for (const line of lines) { - if (line.startsWith('data:')) { - dataLines.push(line.replace(/^data:\s*/, '')); - } else if (line.startsWith('event:')) { - eventName = line.replace(/^event:\s*/, ''); - } else if (line.startsWith('id:')) { - lastEventId = line.replace(/^id:\s*/, ''); - } else if (line.startsWith('retry:')) { - const parsed = Number.parseInt(line.replace(/^retry:\s*/, ''), 10); + if (line.startsWith("data:")) { + dataLines.push(line.replace(/^data:\s*/, "")); + } else if (line.startsWith("event:")) { + eventName = line.replace(/^event:\s*/, ""); + } else if (line.startsWith("id:")) { + lastEventId = line.replace(/^id:\s*/, ""); + } else if (line.startsWith("retry:")) { + const parsed = Number.parseInt( + line.replace(/^retry:\s*/, ""), + 10, + ); if (!Number.isNaN(parsed)) { retryDelay = parsed; } @@ -185,7 +202,7 @@ export const createSseClient = ({ let parsedJson = false; if (dataLines.length) { - const rawData = dataLines.join('\n'); + const rawData = dataLines.join("\n"); try { data = JSON.parse(rawData); parsedJson = true; @@ -217,7 +234,7 @@ export const createSseClient = ({ } } } finally { - signal.removeEventListener('abort', abortHandler); + signal.removeEventListener("abort", abortHandler); reader.releaseLock(); } @@ -226,12 +243,18 @@ export const createSseClient = ({ // connection failed or aborted; retry after delay onSseError?.(error); - if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) { + if ( + sseMaxRetryAttempts !== undefined && + attempt >= sseMaxRetryAttempts + ) { break; // stop after firing error } // exponential backoff: double retry each attempt, cap at 30s - const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000); + const backoff = Math.min( + retryDelay * 2 ** (attempt - 1), + sseMaxRetryDelay ?? 30000, + ); await sleep(backoff); } } diff --git a/packages/api/src/ai-client/core/types.gen.ts b/packages/api/src/ai-client/core/types.gen.ts index 9746325..6b23d10 100644 --- a/packages/api/src/ai-client/core/types.gen.ts +++ b/packages/api/src/ai-client/core/types.gen.ts @@ -1,18 +1,22 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Auth, AuthToken } from './auth.gen'; -import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from './bodySerializer.gen'; +import type { Auth, AuthToken } from "./auth.gen"; +import type { + BodySerializer, + QuerySerializer, + QuerySerializerOptions, +} from "./bodySerializer.gen"; export type HttpMethod = - | 'connect' - | 'delete' - | 'get' - | 'head' - | 'options' - | 'patch' - | 'post' - | 'put' - | 'trace'; + | "connect" + | "delete" + | "get" + | "head" + | "options" + | "patch" + | "post" + | "put" + | "trace"; export type Client< RequestFn = never, @@ -30,7 +34,9 @@ export type Client< setConfig: (config: Config) => Config; } & { [K in HttpMethod]: MethodFn; -} & ([SseFn] extends [never] ? { sse?: never } : { sse: { [K in HttpMethod]: SseFn } }); +} & ([SseFn] extends [never] + ? { sse?: never } + : { sse: { [K in HttpMethod]: SseFn } }); export interface Config { /** @@ -50,10 +56,16 @@ export interface Config { * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} */ headers?: - | RequestInit['headers'] + | RequestInit["headers"] | Record< string, - string | number | boolean | (string | number | boolean)[] | null | undefined | unknown + | string + | number + | boolean + | (string | number | boolean)[] + | null + | undefined + | unknown >; /** * The request method. @@ -100,5 +112,7 @@ type IsExactlyNeverOrNeverUndefined = [T] extends [never] : false; export type OmitNever> = { - [K in keyof T as IsExactlyNeverOrNeverUndefined extends true ? never : K]: T[K]; + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true + ? never + : K]: T[K]; }; diff --git a/packages/api/src/ai-client/core/utils.gen.ts b/packages/api/src/ai-client/core/utils.gen.ts index e7ddbe3..f6d82a9 100644 --- a/packages/api/src/ai-client/core/utils.gen.ts +++ b/packages/api/src/ai-client/core/utils.gen.ts @@ -1,12 +1,12 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { BodySerializer, QuerySerializer } from './bodySerializer.gen'; +import type { BodySerializer, QuerySerializer } from "./bodySerializer.gen"; +import type { ArraySeparatorStyle } from "./pathSerializer.gen"; import { - type ArraySeparatorStyle, serializeArrayParam, serializeObjectParam, serializePrimitiveParam, -} from './pathSerializer.gen'; +} from "./pathSerializer.gen"; export interface PathSerializer { path: Record; @@ -22,19 +22,19 @@ export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { for (const match of matches) { let explode = false; let name = match.substring(1, match.length - 1); - let style: ArraySeparatorStyle = 'simple'; + let style: ArraySeparatorStyle = "simple"; - if (name.endsWith('*')) { + if (name.endsWith("*")) { explode = true; name = name.substring(0, name.length - 1); } - if (name.startsWith('.')) { + if (name.startsWith(".")) { name = name.substring(1); - style = 'label'; - } else if (name.startsWith(';')) { + style = "label"; + } else if (name.startsWith(";")) { name = name.substring(1); - style = 'matrix'; + style = "matrix"; } const value = path[name]; @@ -44,11 +44,14 @@ export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { } if (Array.isArray(value)) { - url = url.replace(match, serializeArrayParam({ explode, name, style, value })); + url = url.replace( + match, + serializeArrayParam({ explode, name, style, value }), + ); continue; } - if (typeof value === 'object') { + if (typeof value === "object") { url = url.replace( match, serializeObjectParam({ @@ -62,7 +65,7 @@ export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { continue; } - if (style === 'matrix') { + if (style === "matrix") { url = url.replace( match, `;${serializePrimitiveParam({ @@ -74,7 +77,7 @@ export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { } const replaceValue = encodeURIComponent( - style === 'label' ? `.${value as string}` : (value as string), + style === "label" ? `.${value as string}` : (value as string), ); url = url.replace(match, replaceValue); } @@ -95,13 +98,13 @@ export const getUrl = ({ querySerializer: QuerySerializer; url: string; }) => { - const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; - let url = (baseUrl ?? '') + pathUrl; + const pathUrl = _url.startsWith("/") ? _url : `/${_url}`; + let url = (baseUrl ?? "") + pathUrl; if (path) { url = defaultPathSerializer({ path, url }); } - let search = query ? querySerializer(query) : ''; - if (search.startsWith('?')) { + let search = query ? querySerializer(query) : ""; + if (search.startsWith("?")) { search = search.substring(1); } if (search) { @@ -119,15 +122,15 @@ export function getValidRequestBody(options: { const isSerializedBody = hasBody && options.bodySerializer; if (isSerializedBody) { - if ('serializedBody' in options) { + if ("serializedBody" in options) { const hasSerializedBody = - options.serializedBody !== undefined && options.serializedBody !== ''; + options.serializedBody !== undefined && options.serializedBody !== ""; return hasSerializedBody ? options.serializedBody : null; } // not all clients implement a serializedBody property (i.e. client-axios) - return options.body !== '' ? options.body : null; + return options.body !== "" ? options.body : null; } // plain/text body diff --git a/packages/api/src/ai-client/index.ts b/packages/api/src/ai-client/index.ts index b34d48f..2e4a047 100644 --- a/packages/api/src/ai-client/index.ts +++ b/packages/api/src/ai-client/index.ts @@ -1,4 +1,31 @@ // This file is auto-generated by @hey-api/openapi-ts -export { healthHealthGet, type Options, setupFrameworkApiV1FrameworksSetupPost, submitEvaluationApiV1EvaluationsSubmitPost } from './sdk.gen'; -export type { BodySetupFrameworkApiV1FrameworksSetupPost, BodySubmitEvaluationApiV1EvaluationsSubmitPost, ClientOptions, ControlSummary, HealthHealthGetData, HealthHealthGetResponse, HealthHealthGetResponses, HttpValidationError, SetupFrameworkApiV1FrameworksSetupPostData, SetupFrameworkApiV1FrameworksSetupPostError, SetupFrameworkApiV1FrameworksSetupPostErrors, SetupFrameworkApiV1FrameworksSetupPostResponse, SetupFrameworkApiV1FrameworksSetupPostResponses, SetupFrameworkResponse, SubmitEvaluationApiV1EvaluationsSubmitPostData, SubmitEvaluationApiV1EvaluationsSubmitPostError, SubmitEvaluationApiV1EvaluationsSubmitPostErrors, SubmitEvaluationApiV1EvaluationsSubmitPostResponse, SubmitEvaluationApiV1EvaluationsSubmitPostResponses, SubmitEvaluationResponse, ValidationError } from './types.gen'; +export { + healthHealthGet, + type Options, + setupFrameworkApiV1FrameworksSetupPost, + submitEvaluationApiV1EvaluationsSubmitPost, +} from "./sdk.gen"; +export type { + BodySetupFrameworkApiV1FrameworksSetupPost, + BodySubmitEvaluationApiV1EvaluationsSubmitPost, + ClientOptions, + ControlSummary, + HealthHealthGetData, + HealthHealthGetResponse, + HealthHealthGetResponses, + HttpValidationError, + SetupFrameworkApiV1FrameworksSetupPostData, + SetupFrameworkApiV1FrameworksSetupPostError, + SetupFrameworkApiV1FrameworksSetupPostErrors, + SetupFrameworkApiV1FrameworksSetupPostResponse, + SetupFrameworkApiV1FrameworksSetupPostResponses, + SetupFrameworkResponse, + SubmitEvaluationApiV1EvaluationsSubmitPostData, + SubmitEvaluationApiV1EvaluationsSubmitPostError, + SubmitEvaluationApiV1EvaluationsSubmitPostErrors, + SubmitEvaluationApiV1EvaluationsSubmitPostResponse, + SubmitEvaluationApiV1EvaluationsSubmitPostResponses, + SubmitEvaluationResponse, + ValidationError, +} from "./types.gen"; diff --git a/packages/api/src/ai-client/sdk.gen.ts b/packages/api/src/ai-client/sdk.gen.ts index 0963048..420f804 100644 --- a/packages/api/src/ai-client/sdk.gen.ts +++ b/packages/api/src/ai-client/sdk.gen.ts @@ -1,21 +1,34 @@ -// This file is auto-generated by @hey-api/openapi-ts +import type { Client, Options as Options2, TDataShape } from "./client"; +import type { + HealthHealthGetData, + HealthHealthGetResponses, + SetupFrameworkApiV1FrameworksSetupPostData, + SetupFrameworkApiV1FrameworksSetupPostErrors, + SetupFrameworkApiV1FrameworksSetupPostResponses, + SubmitEvaluationApiV1EvaluationsSubmitPostData, + SubmitEvaluationApiV1EvaluationsSubmitPostErrors, + SubmitEvaluationApiV1EvaluationsSubmitPostResponses, +} from "./types.gen"; +import { formDataBodySerializer } from "./client"; +import { client } from "./client.gen"; -import { type Client, formDataBodySerializer, type Options as Options2, type TDataShape } from './client'; -import { client } from './client.gen'; -import type { HealthHealthGetData, HealthHealthGetResponses, SetupFrameworkApiV1FrameworksSetupPostData, SetupFrameworkApiV1FrameworksSetupPostErrors, SetupFrameworkApiV1FrameworksSetupPostResponses, SubmitEvaluationApiV1EvaluationsSubmitPostData, SubmitEvaluationApiV1EvaluationsSubmitPostErrors, SubmitEvaluationApiV1EvaluationsSubmitPostResponses } from './types.gen'; +// This file is auto-generated by @hey-api/openapi-ts -export type Options = Options2 & { - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; - /** - * You can pass arbitrary values through the `meta` object. This can be - * used to access values that aren't defined as part of the SDK function. - */ - meta?: Record; +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, +> = Options2 & { + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + /** + * You can pass arbitrary values through the `meta` object. This can be + * used to access values that aren't defined as part of the SDK function. + */ + meta?: Record; }; /** @@ -23,7 +36,14 @@ export type Options(options?: Options) => (options?.client ?? client).get({ url: '/health', ...options }); +export const healthHealthGet = ( + options?: Options, +) => + (options?.client ?? client).get< + HealthHealthGetResponses, + unknown, + ThrowOnError + >({ url: "/health", ...options }); /** * Setup Framework @@ -32,15 +52,24 @@ export const healthHealthGet = (options?: * Each PDF has a section name; controls are saved as config/frameworks/{framework_name}/{section_name}.json. * Send section_names as one string: comma-separated names, one per file, in the same order as files. */ -export const setupFrameworkApiV1FrameworksSetupPost = (options: Options) => (options.client ?? client).post({ +export const setupFrameworkApiV1FrameworksSetupPost = < + ThrowOnError extends boolean = false, +>( + options: Options, +) => + (options.client ?? client).post< + SetupFrameworkApiV1FrameworksSetupPostResponses, + SetupFrameworkApiV1FrameworksSetupPostErrors, + ThrowOnError + >({ ...formDataBodySerializer, - url: '/api/v1/frameworks/setup', + url: "/api/v1/frameworks/setup", ...options, headers: { - 'Content-Type': null, - ...options.headers - } -}); + "Content-Type": null, + ...options.headers, + }, + }); /** * Submit Evaluation @@ -49,12 +78,24 @@ export const setupFrameworkApiV1FrameworksSetupPost = (options: Options) => (options.client ?? client).post({ +export const submitEvaluationApiV1EvaluationsSubmitPost = < + ThrowOnError extends boolean = false, +>( + options: Options< + SubmitEvaluationApiV1EvaluationsSubmitPostData, + ThrowOnError + >, +) => + (options.client ?? client).post< + SubmitEvaluationApiV1EvaluationsSubmitPostResponses, + SubmitEvaluationApiV1EvaluationsSubmitPostErrors, + ThrowOnError + >({ ...formDataBodySerializer, - url: '/api/v1/evaluations/submit', + url: "/api/v1/evaluations/submit", ...options, headers: { - 'Content-Type': null, - ...options.headers - } -}); + "Content-Type": null, + ...options.headers, + }, + }); diff --git a/packages/api/src/ai-client/types.gen.ts b/packages/api/src/ai-client/types.gen.ts index d6108f5..ab8a48a 100644 --- a/packages/api/src/ai-client/types.gen.ts +++ b/packages/api/src/ai-client/types.gen.ts @@ -1,169 +1,55 @@ // This file is auto-generated by @hey-api/openapi-ts export type ClientOptions = { - baseUrl: 'http://localhost:8000' | (string & {}); + baseUrl: "http://localhost:8000" | (string & {}); }; /** * Body_setup_framework_api_v1_frameworks_setup_post */ export type BodySetupFrameworkApiV1FrameworksSetupPost = { - /** - * Framework Name - * - * Framework identifier - */ - framework_name: string; - /** - * Section Names - * - * Comma-separated section names (one per PDF, same order as files). Example: policies,procedures,controls - */ - section_names: string; - /** - * Files - * - * PDF files (same order as section_names) - */ - files: Array; + /** + * Framework Name + * + * Framework identifier + */ + framework_name: string; + /** + * Section Names + * + * Comma-separated section names (one per PDF, same order as files). Example: policies,procedures,controls + */ + section_names: string; + /** + * Files + * + * PDF files (same order as section_names) + */ + files: Array; }; /** * Body_submit_evaluation_api_v1_evaluations_submit_post */ export type BodySubmitEvaluationApiV1EvaluationsSubmitPost = { - /** - * Framework Name - * - * Framework identifier (e.g. NDI) - */ - framework_name: string; - /** - * Files - * - * Files (PDF, DOCX, PPTX, CSV, XLSX); 1 or more - */ - files: Array; - /** - * Control Ids 1 - * - * Comma-separated control IDs for file 1 - */ - control_ids_1?: string; - /** - * Control Ids 2 - * - * Comma-separated control IDs for file 2 - */ - control_ids_2?: string; - /** - * Control Ids 3 - * - * Comma-separated control IDs for file 3 - */ - control_ids_3?: string; - /** - * Control Ids 4 - * - * Comma-separated control IDs for file 4 - */ - control_ids_4?: string; - /** - * Control Ids 5 - * - * Comma-separated control IDs for file 5 - */ - control_ids_5?: string; - /** - * Control Ids 6 - * - * Comma-separated control IDs for file 6 - */ - control_ids_6?: string; - /** - * Control Ids 7 - * - * Comma-separated control IDs for file 7 - */ - control_ids_7?: string; - /** - * Control Ids 8 - * - * Comma-separated control IDs for file 8 - */ - control_ids_8?: string; - /** - * Control Ids 9 - * - * Comma-separated control IDs for file 9 - */ - control_ids_9?: string; - /** - * Control Ids 10 - * - * Comma-separated control IDs for file 10 - */ - control_ids_10?: string; - /** - * Control Ids 11 - * - * Comma-separated control IDs for file 11 - */ - control_ids_11?: string; - /** - * Control Ids 12 - * - * Comma-separated control IDs for file 12 - */ - control_ids_12?: string; - /** - * Control Ids 13 - * - * Comma-separated control IDs for file 13 - */ - control_ids_13?: string; - /** - * Control Ids 14 - * - * Comma-separated control IDs for file 14 - */ - control_ids_14?: string; - /** - * Control Ids 15 - * - * Comma-separated control IDs for file 15 - */ - control_ids_15?: string; - /** - * Control Ids 16 - * - * Comma-separated control IDs for file 16 - */ - control_ids_16?: string; - /** - * Control Ids 17 - * - * Comma-separated control IDs for file 17 - */ - control_ids_17?: string; - /** - * Control Ids 18 - * - * Comma-separated control IDs for file 18 - */ - control_ids_18?: string; - /** - * Control Ids 19 - * - * Comma-separated control IDs for file 19 - */ - control_ids_19?: string; - /** - * Control Ids 20 - * - * Comma-separated control IDs for file 20 - */ - control_ids_20?: string; + /** + * Framework Name + * + * Framework identifier (e.g. NDI) + */ + framework_name: string; + /** + * Files + * + * Files (PDF, DOCX, PPTX, CSV, XLSX); 1 or more + */ + files: Array; + /** + * Domain IDs + * + * Comma-separated domain IDs, one per file (e.g. '1_Data_Governance,1_Data_Governance,2_Data_Catalog') + */ + domain_ids?: string; }; /** @@ -172,34 +58,34 @@ export type BodySubmitEvaluationApiV1EvaluationsSubmitPost = { * Summary of extracted controls for one section (PDF). */ export type ControlSummary = { - /** - * Section Name - * - * Name of the section (PDF) - */ - section_name: string; - /** - * Controls Count - * - * Number of controls extracted - */ - controls_count: number; - /** - * Json Path - * - * Relative path to the saved JSON file - */ - json_path: string; + /** + * Section Name + * + * Name of the section (PDF) + */ + section_name: string; + /** + * Controls Count + * + * Number of controls extracted + */ + controls_count: number; + /** + * Json Path + * + * Relative path to the saved JSON file + */ + json_path: string; }; /** * HTTPValidationError */ export type HttpValidationError = { - /** - * Detail - */ - detail?: Array; + /** + * Detail + */ + detail?: Array; }; /** @@ -208,30 +94,30 @@ export type HttpValidationError = { * Response after successfully setting up a framework from multiple PDFs. */ export type SetupFrameworkResponse = { - /** - * Framework Name - * - * Framework identifier - */ - framework_name: string; - /** - * Total Controls - * - * Total controls across all sections - */ - total_controls: number; - /** - * Sections - * - * Per-section summary - */ - sections?: Array; - /** - * Created At - * - * When the setup completed - */ - created_at?: string; + /** + * Framework Name + * + * Framework identifier + */ + framework_name: string; + /** + * Total Controls + * + * Total controls across all sections + */ + total_controls: number; + /** + * Sections + * + * Per-section summary + */ + sections?: Array; + /** + * Created At + * + * When the setup completed + */ + created_at?: string; }; /** @@ -240,120 +126,125 @@ export type SetupFrameworkResponse = { * Response after submitting an evaluation (15 files + framework). */ export type SubmitEvaluationResponse = { - /** - * Evaluation Id - * - * Unique evaluation run ID - */ - evaluation_id: string; - /** - * Mimic Json - * - * DB-mimic: framework_name -> field_1..field_15 -> comma-separated control IDs - */ - mimic_json: { - [key: string]: unknown; - }; - /** - * Report Path - * - * Path to the generated report PDF - */ - report_path?: string; - /** - * File Evaluations - * - * Per-file assessment results - */ - file_evaluations?: Array<{ - [key: string]: unknown; - }>; + /** + * Evaluation Id + * + * Unique evaluation run ID + */ + evaluation_id: string; + /** + * Mimic Json + * + * DB-mimic: framework_name -> field_1..field_15 -> comma-separated control IDs + */ + mimic_json: { + [key: string]: unknown; + }; + /** + * Report Path + * + * Path to the generated report PDF + */ + report_path?: string; + /** + * File Evaluations + * + * Per-file assessment results + */ + file_evaluations?: Array<{ + [key: string]: unknown; + }>; }; /** * ValidationError */ export type ValidationError = { - /** - * Location - */ - loc: Array; - /** - * Message - */ - msg: string; - /** - * Error Type - */ - type: string; + /** + * Location + */ + loc: Array; + /** + * Message + */ + msg: string; + /** + * Error Type + */ + type: string; }; export type HealthHealthGetData = { - body?: never; - path?: never; - query?: never; - url: '/health'; + body?: never; + path?: never; + query?: never; + url: "/health"; }; export type HealthHealthGetResponses = { - /** - * Response Health Health Get - * - * Successful Response - */ - 200: { - [key: string]: string; - }; + /** + * Response Health Health Get + * + * Successful Response + */ + 200: { + [key: string]: string; + }; }; -export type HealthHealthGetResponse = HealthHealthGetResponses[keyof HealthHealthGetResponses]; +export type HealthHealthGetResponse = + HealthHealthGetResponses[keyof HealthHealthGetResponses]; export type SetupFrameworkApiV1FrameworksSetupPostData = { - body: BodySetupFrameworkApiV1FrameworksSetupPost; - path?: never; - query?: never; - url: '/api/v1/frameworks/setup'; + body: BodySetupFrameworkApiV1FrameworksSetupPost; + path?: never; + query?: never; + url: "/api/v1/frameworks/setup"; }; export type SetupFrameworkApiV1FrameworksSetupPostErrors = { - /** - * Validation Error - */ - 422: HttpValidationError; + /** + * Validation Error + */ + 422: HttpValidationError; }; -export type SetupFrameworkApiV1FrameworksSetupPostError = SetupFrameworkApiV1FrameworksSetupPostErrors[keyof SetupFrameworkApiV1FrameworksSetupPostErrors]; +export type SetupFrameworkApiV1FrameworksSetupPostError = + SetupFrameworkApiV1FrameworksSetupPostErrors[keyof SetupFrameworkApiV1FrameworksSetupPostErrors]; export type SetupFrameworkApiV1FrameworksSetupPostResponses = { - /** - * Successful Response - */ - 200: SetupFrameworkResponse; + /** + * Successful Response + */ + 200: SetupFrameworkResponse; }; -export type SetupFrameworkApiV1FrameworksSetupPostResponse = SetupFrameworkApiV1FrameworksSetupPostResponses[keyof SetupFrameworkApiV1FrameworksSetupPostResponses]; +export type SetupFrameworkApiV1FrameworksSetupPostResponse = + SetupFrameworkApiV1FrameworksSetupPostResponses[keyof SetupFrameworkApiV1FrameworksSetupPostResponses]; export type SubmitEvaluationApiV1EvaluationsSubmitPostData = { - body: BodySubmitEvaluationApiV1EvaluationsSubmitPost; - path?: never; - query?: never; - url: '/api/v1/evaluations/submit'; + body: BodySubmitEvaluationApiV1EvaluationsSubmitPost; + path?: never; + query?: never; + url: "/api/v1/evaluations/submit"; }; export type SubmitEvaluationApiV1EvaluationsSubmitPostErrors = { - /** - * Validation Error - */ - 422: HttpValidationError; + /** + * Validation Error + */ + 422: HttpValidationError; }; -export type SubmitEvaluationApiV1EvaluationsSubmitPostError = SubmitEvaluationApiV1EvaluationsSubmitPostErrors[keyof SubmitEvaluationApiV1EvaluationsSubmitPostErrors]; +export type SubmitEvaluationApiV1EvaluationsSubmitPostError = + SubmitEvaluationApiV1EvaluationsSubmitPostErrors[keyof SubmitEvaluationApiV1EvaluationsSubmitPostErrors]; export type SubmitEvaluationApiV1EvaluationsSubmitPostResponses = { - /** - * Successful Response - */ - 200: SubmitEvaluationResponse; + /** + * Successful Response + */ + 200: SubmitEvaluationResponse; }; -export type SubmitEvaluationApiV1EvaluationsSubmitPostResponse = SubmitEvaluationApiV1EvaluationsSubmitPostResponses[keyof SubmitEvaluationApiV1EvaluationsSubmitPostResponses]; +export type SubmitEvaluationApiV1EvaluationsSubmitPostResponse = + SubmitEvaluationApiV1EvaluationsSubmitPostResponses[keyof SubmitEvaluationApiV1EvaluationsSubmitPostResponses]; diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 090ff68..7921ca1 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -18,6 +18,11 @@ type RouterInputs = inferRouterInputs; */ type RouterOutputs = inferRouterOutputs; +export type { RouterInputs, RouterOutputs }; + export { type AppRouter, appRouter } from "./root"; export { createTRPCContext } from "./trpc"; -export type { RouterInputs, RouterOutputs }; + +// Export WorkOS utilities and permissions +export { workos, SUBMISSION_PERMISSIONS } from "./lib/workos"; +export type { SubmissionPermission } from "./lib/workos"; diff --git a/packages/api/src/lib/ai.ts b/packages/api/src/lib/ai.ts index d59ae9f..dd8116b 100644 --- a/packages/api/src/lib/ai.ts +++ b/packages/api/src/lib/ai.ts @@ -1,19 +1,18 @@ - -import { createClient, createConfig } from "../ai-client/client"; import type { ClientOptions } from "../ai-client/types.gen"; +import { createClient, createConfig } from "../ai-client/client"; export const getAIClient = () => { - const baseUrl = process.env.AI_SERVICE_URL || "http://localhost:8000"; - const apiKey = process.env.AI_SERVICE_KEY; + const baseUrl = process.env.AI_SERVICE_URL || "http://localhost:8000"; + const apiKey = process.env.AI_SERVICE_KEY; - return createClient( - createConfig({ - baseUrl, - headers: apiKey - ? { - Authorization: apiKey, - } - : undefined, - }) - ); + return createClient( + createConfig({ + baseUrl, + headers: apiKey + ? { + Authorization: apiKey, + } + : undefined, + }), + ); }; diff --git a/packages/api/src/lib/workos.ts b/packages/api/src/lib/workos.ts new file mode 100644 index 0000000..9061f62 --- /dev/null +++ b/packages/api/src/lib/workos.ts @@ -0,0 +1,21 @@ +import { WorkOS } from "@workos-inc/node"; + +const workosApiKey = process.env.WORKOS_API_KEY; + +if (!workosApiKey) { + throw new Error("WORKOS_API_KEY is not set"); +} + +export const workos = new WorkOS(workosApiKey); + +/** + * Submission permissions for RBAC + */ +export const SUBMISSION_PERMISSIONS = { + READ: "submitions:read", + WRITE: "submitions:write", + DELETE: "submitions:delete", +} as const; + +export type SubmissionPermission = + (typeof SUBMISSION_PERMISSIONS)[keyof typeof SUBMISSION_PERMISSIONS]; diff --git a/packages/api/src/router/submission.ts b/packages/api/src/router/submission.ts index c745d0b..a2bd118 100644 --- a/packages/api/src/router/submission.ts +++ b/packages/api/src/router/submission.ts @@ -7,10 +7,11 @@ import * as schema from "@governance/db/schema"; import { submitEvaluationApiV1EvaluationsSubmitPost } from "../ai-client/sdk.gen"; import { getAIClient } from "../lib/ai"; -import { protectedProcedure } from "../trpc"; +import { SUBMISSION_PERMISSIONS } from "../lib/workos"; +import { createPermissionProcedure } from "../trpc"; export const submissionRouter = { - create: protectedProcedure + create: createPermissionProcedure([SUBMISSION_PERMISSIONS.WRITE]) .input( z.object({ companyName: z.string().min(1), @@ -54,6 +55,8 @@ export const submissionRouter = { domainName: f.domainName, fileName: f.name, filePath: "ai-service-upload", // Placeholder + fileContent: f.content, // Store base64 content for rerun capability + fileType: f.type, // Store MIME type fileSize: f.size, })); @@ -67,18 +70,21 @@ export const submissionRouter = { return new File([buffer], f.name, { type: f.type }); }); - // 4. Call AI Service + // 4. Prepare domain_ids (one per file, comma-separated) + const domainIds = files.map((f) => f.domainId).join(","); + + // 5. Call AI Service try { const response = await submitEvaluationApiV1EvaluationsSubmitPost({ client, body: { framework_name: "NDI", files: blobs, + domain_ids: domainIds, }, }); if (response.error) { - // @ts-ignore throw new Error(JSON.stringify(response.error)); } @@ -112,18 +118,24 @@ export const submissionRouter = { .update(schema.Submission) .set({ status: "failed" }) .where(eq(schema.Submission.id, submission.id)); - return { success: true, submissionId: submission.id, evaluationFailed: true }; + return { + success: true, + submissionId: submission.id, + evaluationFailed: true, + }; } }), - getAll: protectedProcedure.query(async ({ ctx }) => { - return ctx.db - .select() - .from(schema.Submission) - .orderBy(desc(schema.Submission.createdAt)); - }), + getAll: createPermissionProcedure([SUBMISSION_PERMISSIONS.READ]).query( + async ({ ctx }) => { + return ctx.db + .select() + .from(schema.Submission) + .orderBy(desc(schema.Submission.createdAt)); + }, + ), - getById: protectedProcedure + getById: createPermissionProcedure([SUBMISSION_PERMISSIONS.READ]) .input(z.object({ id: z.string().uuid() })) .query(async ({ ctx, input }) => { const [submission] = await ctx.db @@ -133,7 +145,10 @@ export const submissionRouter = { .limit(1); if (!submission) { - throw new TRPCError({ code: "NOT_FOUND", message: "Submission not found" }); + throw new TRPCError({ + code: "NOT_FOUND", + message: "Submission not found", + }); } const files = await ctx.db @@ -149,4 +164,160 @@ export const submissionRouter = { return { submission, files, report: report ?? null }; }), + + delete: createPermissionProcedure([SUBMISSION_PERMISSIONS.DELETE]) + .input(z.object({ id: z.string().uuid() })) + .mutation(async ({ ctx, input }) => { + // Check if submission exists and belongs to the user + const [submission] = await ctx.db + .select() + .from(schema.Submission) + .where(eq(schema.Submission.id, input.id)) + .limit(1); + + if (!submission) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Submission not found", + }); + } + + // Delete related records first (cascade delete) + await ctx.db + .delete(schema.EvaluationReport) + .where(eq(schema.EvaluationReport.submissionId, input.id)); + + await ctx.db + .delete(schema.SubmissionFile) + .where(eq(schema.SubmissionFile.submissionId, input.id)); + + // Delete the submission + await ctx.db + .delete(schema.Submission) + .where(eq(schema.Submission.id, input.id)); + + return { success: true }; + }), + + rerunEvaluation: createPermissionProcedure([SUBMISSION_PERMISSIONS.WRITE]) + .input(z.object({ id: z.string().uuid() })) + .mutation(async ({ ctx, input }) => { + const client = getAIClient(); + + // 1. Get submission + const [submission] = await ctx.db + .select() + .from(schema.Submission) + .where(eq(schema.Submission.id, input.id)) + .limit(1); + + if (!submission) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Submission not found", + }); + } + + // 2. Get existing files with content + const files = await ctx.db + .select() + .from(schema.SubmissionFile) + .where(eq(schema.SubmissionFile.submissionId, input.id)); + + if (files.length === 0) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "No files found for this submission", + }); + } + + // Check if files have content stored + const filesWithoutContent = files.filter( + (f) => !(f as unknown as { fileContent?: string }).fileContent + ); + if (filesWithoutContent.length > 0) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: + "Some files do not have content stored. This submission was created before the rerun feature was implemented. Please create a new submission.", + }); + } + + // Update submission status to processing + await ctx.db + .update(schema.Submission) + .set({ status: "processing", updatedAt: new Date() }) + .where(eq(schema.Submission.id, submission.id)); + + // 3. Delete old evaluation report if exists + await ctx.db + .delete(schema.EvaluationReport) + .where(eq(schema.EvaluationReport.submissionId, input.id)); + + // 4. Prepare files for AI Service + const blobs = files.map((f) => { + const fileContent = (f as unknown as { fileContent?: string }).fileContent; + const fileType = (f as unknown as { fileType?: string }).fileType; + const buffer = Buffer.from(fileContent!, "base64"); + return new File([buffer], f.fileName, { + type: fileType ?? "application/octet-stream", + }); + }); + + // 5. Prepare domain_ids (one per file, comma-separated) + const domainIds = files.map((f) => f.domainId).join(","); + + // 6. Call AI Service + try { + const response = await submitEvaluationApiV1EvaluationsSubmitPost({ + client, + body: { + framework_name: "NDI", + files: blobs, + domain_ids: domainIds, + }, + }); + + if (response.error) { + throw new Error(JSON.stringify(response.error)); + } + + const data = response.data; + if (!data) throw new Error("No data returned from AI service"); + + // 6. Create new Evaluation Report + await ctx.db.insert(schema.EvaluationReport).values({ + submissionId: submission.id, + aiServiceReportId: data.evaluation_id, + reportPath: data.report_path, + reportData: JSON.stringify({ + evaluation_id: data.evaluation_id, + mimic_json: data.mimic_json, + file_evaluations: data.file_evaluations, + }), + status: "completed", + completedAt: new Date(), + }); + + // Update submission status to completed + await ctx.db + .update(schema.Submission) + .set({ status: "completed", updatedAt: new Date() }) + .where(eq(schema.Submission.id, submission.id)); + + return { success: true }; + } catch (e) { + console.error("AI Service Rerun Failed", e); + await ctx.db + .update(schema.Submission) + .set({ status: "failed", updatedAt: new Date() }) + .where(eq(schema.Submission.id, submission.id)); + + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: + "Failed to rerun evaluation. The AI service may be unavailable.", + }); + } + }), } satisfies TRPCRouterRecord; diff --git a/packages/api/src/trpc.ts b/packages/api/src/trpc.ts index 7a1734c..ff89a2a 100644 --- a/packages/api/src/trpc.ts +++ b/packages/api/src/trpc.ts @@ -10,7 +10,7 @@ import { initTRPC, TRPCError } from "@trpc/server"; import superjson from "superjson"; import { z, ZodError } from "zod/v4"; -import type { UserInfo, NoUserInfo } from "@governance/auth"; +import type { NoUserInfo, UserInfo } from "@governance/auth"; import { db } from "@governance/db/client"; /** @@ -26,13 +26,30 @@ import { db } from "@governance/db/client"; * @see https://trpc.io/docs/server/context */ +/** + * Extract permissions from the user session + * Permissions come from the access token JWT in the session + */ +function getPermissionsFromSession(session: UserInfo | NoUserInfo): string[] { + if (!session.user) { + return []; + } + + // WorkOS includes permissions in the access token + // They are available in the session.permissions field or decoded from the JWT + return session.permissions ?? []; +} + export const createTRPCContext = async (opts: { headers: Headers; auth: UserInfo | NoUserInfo; }) => { const session = opts.auth; + const permissions = getPermissionsFromSession(session); + return { session, + permissions, db, }; }; @@ -119,6 +136,40 @@ export const protectedProcedure = t.procedure ctx: { // infers the `session` as non-nullable session: ctx.session as UserInfo, + permissions: ctx.permissions, }, }); }); + +/** + * Permission-based procedure + * + * Use this to create procedures that require specific permissions. + * The permission check is based on the user's role permissions from WorkOS. + * + * @example + * ```ts + * const myProcedure = createPermissionProcedure(["submissions:read"]) + * .query(async ({ ctx }) => { + * // User has submissions:read permission + * }); + * ``` + */ +export const createPermissionProcedure = (requiredPermissions: string[]) => { + return protectedProcedure.use(({ ctx, next }) => { + const hasPermission = requiredPermissions.every((permission) => + ctx.permissions.includes(permission), + ); + + if (!hasPermission) { + throw new TRPCError({ + code: "FORBIDDEN", + message: `Missing required permissions: ${requiredPermissions.join(", ")}`, + }); + } + + return next({ + ctx, + }); + }); +}; diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index cbcec5c..a635b50 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -1,16 +1,8 @@ { "extends": "@governance/tsconfig/compiled-package.json", "compilerOptions": { - "lib": [ - "ESNext", - "DOM", - "DOM.Iterable" - ] + "lib": ["ESNext", "DOM", "DOM.Iterable"] }, - "include": [ - "src" - ], - "exclude": [ - "node_modules" - ] -} \ No newline at end of file + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/db/src/schema/submissions.ts b/packages/db/src/schema/submissions.ts index 6a1d59c..a70c0a2 100644 --- a/packages/db/src/schema/submissions.ts +++ b/packages/db/src/schema/submissions.ts @@ -1,9 +1,21 @@ -import { pgTable, uuid, varchar, text, timestamp, integer } from "drizzle-orm/pg-core"; +import { + integer, + pgTable, + text, + timestamp, + uuid, + varchar, +} from "drizzle-orm/pg-core"; import { createInsertSchema, createSelectSchema } from "drizzle-zod"; import { z } from "zod/v4"; // Submission status enum -export const submissionStatus = ["pending", "processing", "completed", "failed"] as const; +export const submissionStatus = [ + "pending", + "processing", + "completed", + "failed", +] as const; // Main submissions table export const Submission = pgTable("submission", (t) => ({ @@ -18,11 +30,16 @@ export const Submission = pgTable("submission", (t) => ({ // Submission files table - stores uploaded files per domain export const SubmissionFile = pgTable("submission_file", (t) => ({ id: t.uuid().notNull().primaryKey().defaultRandom(), - submissionId: t.uuid().notNull().references(() => Submission.id, { onDelete: "cascade" }), + submissionId: t + .uuid() + .notNull() + .references(() => Submission.id, { onDelete: "cascade" }), domainId: t.varchar({ length: 100 }).notNull(), domainName: t.varchar({ length: 255 }).notNull(), fileName: t.varchar({ length: 255 }).notNull(), filePath: t.text().notNull(), + fileContent: t.text(), // Base64 encoded file content for rerun capability + fileType: t.varchar({ length: 100 }), // MIME type of the file fileSize: t.integer().notNull(), uploadedAt: t.timestamp().defaultNow().notNull(), })); @@ -30,7 +47,10 @@ export const SubmissionFile = pgTable("submission_file", (t) => ({ // Evaluation reports table - stores AI service evaluation results export const EvaluationReport = pgTable("evaluation_report", (t) => ({ id: t.uuid().notNull().primaryKey().defaultRandom(), - submissionId: t.uuid().notNull().references(() => Submission.id, { onDelete: "cascade" }), + submissionId: t + .uuid() + .notNull() + .references(() => Submission.id, { onDelete: "cascade" }), aiServiceReportId: t.varchar({ length: 255 }), reportPath: t.text(), reportData: t.text(), // JSON string of evaluation results @@ -41,7 +61,10 @@ export const EvaluationReport = pgTable("evaluation_report", (t) => ({ // Zod schemas for validation export const CreateSubmissionSchema = createInsertSchema(Submission, { - companyName: z.string().min(2, "Company name must be at least 2 characters").max(255), + companyName: z + .string() + .min(2, "Company name must be at least 2 characters") + .max(255), }).omit({ id: true, status: true, @@ -61,7 +84,8 @@ export const CreateSubmissionFileSchema = createInsertSchema(SubmissionFile, { export const SelectSubmissionSchema = createSelectSchema(Submission); export const SelectSubmissionFileSchema = createSelectSchema(SubmissionFile); -export const SelectEvaluationReportSchema = createSelectSchema(EvaluationReport); +export const SelectEvaluationReportSchema = + createSelectSchema(EvaluationReport); // Type exports export type Submission = typeof Submission.$inferSelect; diff --git a/packages/ui/src/theme.tsx b/packages/ui/src/theme.tsx index 9d0b4c4..886ef60 100644 --- a/packages/ui/src/theme.tsx +++ b/packages/ui/src/theme.tsx @@ -134,7 +134,6 @@ export function ThemeProvider({ children }: React.PropsWithChildren) { toggleMode, }} > - {children} ); diff --git a/packages/validators/package.json b/packages/validators/package.json index e36802f..eea0ca8 100644 --- a/packages/validators/package.json +++ b/packages/validators/package.json @@ -29,4 +29,4 @@ "typescript": "catalog:" }, "prettier": "@governance/prettier-config" -} \ No newline at end of file +} diff --git a/packages/validators/src/ndi-domains.ts b/packages/validators/src/ndi-domains.ts index 974eb70..700bf91 100644 --- a/packages/validators/src/ndi-domains.ts +++ b/packages/validators/src/ndi-domains.ts @@ -4,82 +4,82 @@ */ export const NDI_DOMAINS = [ - { - id: "DG", - name: "Data Governance", - order: 1, - }, - { - id: "DCM", - name: "Data Catalog & Metadata", - order: 2, - }, - { - id: "DQ", - name: "Data Quality", - order: 3, - }, - { - id: "DO", - name: "Data Operations", - order: 4, - }, - { - id: "DM", - name: "Document Management", - order: 5, - }, - { - id: "DA", - name: "Data Architecture", - order: 6, - }, - { - id: "DS", - name: "Data Sharing", - order: 7, - }, - { - id: "MD", - name: "Master Data", - order: 8, - }, - { - id: "ABI", - name: "Analytics & BI", - order: 9, - }, - { - id: "DV", - name: "Data Value", - order: 10, - }, - { - id: "OD", - name: "Open Data", - order: 11, - }, - { - id: "FOI", - name: "Freedom of Information", - order: 12, - }, - { - id: "DC", - name: "Data Classification", - order: 13, - }, - { - id: "PDP", - name: "Personal Data Protection", - order: 14, - }, - { - id: "DSec", - name: "Data Security", - order: 15, - }, + { + id: "DG", + name: "Data Governance", + order: 1, + }, + { + id: "DCM", + name: "Data Catalog & Metadata", + order: 2, + }, + { + id: "DQ", + name: "Data Quality", + order: 3, + }, + { + id: "DO", + name: "Data Operations", + order: 4, + }, + { + id: "DM", + name: "Document Management", + order: 5, + }, + { + id: "DA", + name: "Data Architecture", + order: 6, + }, + { + id: "DS", + name: "Data Sharing", + order: 7, + }, + { + id: "MD", + name: "Master Data", + order: 8, + }, + { + id: "ABI", + name: "Analytics & BI", + order: 9, + }, + { + id: "DV", + name: "Data Value", + order: 10, + }, + { + id: "OD", + name: "Open Data", + order: 11, + }, + { + id: "FOI", + name: "Freedom of Information", + order: 12, + }, + { + id: "DC", + name: "Data Classification", + order: 13, + }, + { + id: "PDP", + name: "Personal Data Protection", + order: 14, + }, + { + id: "DSec", + name: "Data Security", + order: 15, + }, ] as const; -export type NDIDomainId = typeof NDI_DOMAINS[number]["id"]; -export type NDIDomain = typeof NDI_DOMAINS[number]; +export type NDIDomainId = (typeof NDI_DOMAINS)[number]["id"]; +export type NDIDomain = (typeof NDI_DOMAINS)[number]; diff --git a/packages/validators/tsconfig.json b/packages/validators/tsconfig.json index 5d2d619..21a6764 100644 --- a/packages/validators/tsconfig.json +++ b/packages/validators/tsconfig.json @@ -8,10 +8,6 @@ "emitDeclarationOnly": false, "noEmit": false }, - "include": [ - "src" - ], - "exclude": [ - "node_modules" - ] -} \ No newline at end of file + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f36c1f..14a2dce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ catalogs: specifier: ^1.23.8 version: 1.23.8 '@tanstack/react-query': - specifier: ^5.90.8 + specifier: 5.90.8 version: 5.90.8 '@trpc/client': specifier: ^11.7.1 @@ -113,6 +113,9 @@ importers: '@governance/ui': specifier: workspace:* version: link:../../packages/ui + '@radix-ui/themes': + specifier: ^3.3.0 + version: 3.3.0(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@t3-oss/env-core': specifier: ^0.13.8 version: 0.13.8(arktype@2.1.20)(typescript@5.9.3)(valibot@1.0.0-beta.15(typescript@5.9.3))(zod@4.1.12) @@ -143,6 +146,12 @@ importers: '@trpc/tanstack-react-query': specifier: 'catalog:' version: 11.7.1(@tanstack/react-query@5.90.8(react@19.1.4))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(typescript@5.9.3) + '@workos-inc/widgets': + specifier: ^1.8.0 + version: 1.8.0(@radix-ui/themes@3.3.0(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(@tanstack/react-query@5.90.8(react@19.1.4))(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@workos-inc/widgets-i18n': + specifier: ^0.1.0 + version: 0.1.0(@workos-inc/widgets@1.8.0(@radix-ui/themes@3.3.0(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(@tanstack/react-query@5.90.8(react@19.1.4))(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(react@19.1.4)(typescript@5.9.3) '@workos/authkit-tanstack-react-start': specifier: ^0.5.0 version: 0.5.0(@tanstack/react-router@1.135.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(@tanstack/react-start@1.135.2(crossws@0.4.1(srvx@0.9.6))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(vite@7.1.12(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) @@ -225,6 +234,9 @@ importers: '@trpc/server': specifier: 'catalog:' version: 11.7.1(typescript@5.9.3) + '@workos-inc/node': + specifier: ^8.1.0 + version: 8.1.0 drizzle-orm: specifier: ^0.44.7 version: 0.44.7(@neondatabase/serverless@0.10.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.11.10)(@vercel/postgres@0.10.0(utf-8-validate@6.0.4))(better-sqlite3@12.2.0)(gel@2.0.0)(kysely@0.28.5)(mysql2@3.11.3)(postgres@3.4.8)(prisma@5.22.0) @@ -1052,6 +1064,29 @@ packages: '@fontsource-variable/geist@5.2.8': resolution: {integrity: sha512-cJ6m9e+8MQ5dCYJsLylfZrgBh6KkG4bOLckB35Tr9J/EqdkEM6QllH5PxqP1dhTvFup+HtMRPuz9xOjxXJggxw==} + '@formatjs/ecma402-abstract@2.3.6': + resolution: {integrity: sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==} + + '@formatjs/fast-memoize@2.2.7': + resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==} + + '@formatjs/icu-messageformat-parser@2.11.4': + resolution: {integrity: sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==} + + '@formatjs/icu-skeleton-parser@1.8.16': + resolution: {integrity: sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==} + + '@formatjs/intl-localematcher@0.6.2': + resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} + + '@formatjs/intl@3.1.8': + resolution: {integrity: sha512-LWXgwI5zTMatvR8w8kCNh/priDTOF/ZssokMBHJ7ZWXFoYLVOYo0EJERD9Eajv+xsfQO1QkuAt77KWQ1OI4mOQ==} + peerDependencies: + typescript: ^5.6.0 + peerDependenciesMeta: + typescript: + optional: true + '@hey-api/client-fetch@0.13.1': resolution: {integrity: sha512-29jBRYNdxVGlx5oewFgOrkulZckpIpBIRHth3uHFn1PrL2ucMy52FvWOY3U3dVx2go1Z3kUmMi6lr07iOpUqqA==} deprecated: Starting with v0.73.0, this package is bundled directly inside @hey-api/openapi-ts. @@ -1506,6 +1541,9 @@ packages: '@prisma/get-platform@5.22.0': resolution: {integrity: sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==} + '@radix-ui/colors@3.0.0': + resolution: {integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==} + '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -2201,6 +2239,19 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@radix-ui/themes@3.3.0': + resolution: {integrity: sha512-I0/h2CRNTpYNB7Mi3xFIvSsQq5a108d7kK8dTO5zp5b9HR5QJXKag6B8tjpz2ITkVYkFdkGk45doNkSr7OxwNw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: 16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@rolldown/pluginutils@1.0.0-beta.40': resolution: {integrity: sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w==} @@ -2679,6 +2730,11 @@ packages: '@types/glob@7.2.0': resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} + '@types/hoist-non-react-statics@3.3.7': + resolution: {integrity: sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==} + peerDependencies: + '@types/react': '*' + '@types/inquirer@6.5.0': resolution: {integrity: sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==} @@ -2806,6 +2862,24 @@ packages: resolution: {integrity: sha512-D8VDfx0GXeiVm8vccAl0rElW7taebRnrteKPJzZwehwzI9W/Usa4qKfmwxj+7Lh1Z1deEocDRCpZpV7ml4GpWQ==} engines: {node: '>=20.15.0'} + '@workos-inc/node@8.1.0': + resolution: {integrity: sha512-Ep2QSP43y4ZdJIOuL4Hjaq5f0u8Z0qZe7QWzrrBV6cHc/kcicDBcB0AanMP6eB9x3x6FaHfevLbkbjPF4+TCYQ==} + engines: {node: '>=20.15.0'} + + '@workos-inc/widgets-i18n@0.1.0': + resolution: {integrity: sha512-AFDuNTxg/0QwcG/z0kBZkiXXzqgDY7/60s7jVfbNn3f3G4k4HZUKrYcXe/CBNru54StaQsw+8SueHPSd/VAK0A==} + peerDependencies: + '@workos-inc/widgets': ^1.8.0 + react: '>=18' + + '@workos-inc/widgets@1.8.0': + resolution: {integrity: sha512-UyDzWIaty5XH+Gh1bGoyBn0mzO4vLu6WM2XFTFojHFdyyxkvuUt8LkXPxarG3VmBHSkNffr4bKbEyDJ0LAh7MA==} + peerDependencies: + '@radix-ui/themes': ^3.1.0 + '@tanstack/react-query': ^5.0.0 + react: '>=18' + react-dom: '>=18' + '@workos/authkit-session@0.3.4': resolution: {integrity: sha512-lbLP1y8MHWL1Op9athZ3SrzKLcL0+xBVpADCMQLI39mPgSQj+/lopVdOx0Cku96hYnJBOJTLVTK3Zox4FbZl4A==} engines: {node: '>=20.0.0'} @@ -2993,6 +3067,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bowser@2.12.1: + resolution: {integrity: sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -3104,6 +3181,9 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -3269,6 +3349,9 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-formdata@0.9.0: resolution: {integrity: sha512-q5uwOjR3Um5YD+ZWPOF/1sGHVW9A5rCrRwITQChRXlmPkxDFBqCm4jNTIVdGHNH9OnR+V9MoZVgRhsFb+ARbUw==} @@ -3860,7 +3943,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + deprecated: Glob versions prior to v9 are no longer supported globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} @@ -3959,6 +4042,9 @@ packages: hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + htmlparser2@10.0.0: resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} @@ -4031,6 +4117,9 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + intl-messageformat@10.7.18: + resolution: {integrity: sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==} + ip-address@10.0.1: resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} engines: {node: '>= 12'} @@ -4884,6 +4973,15 @@ packages: peerDependencies: react: ^19.2.3 + react-intl@7.1.14: + resolution: {integrity: sha512-VE/0Wi/lHJlBC7APQpCzLUdIt3GB5B0GZrRW8Q+ACbkHI4j+Wwgg9J1TniN6zmLHmPH5gxXcMy+fkSPfw5p1WQ==} + peerDependencies: + react: 16 || 17 || 18 || 19 + typescript: ^5.6.0 + peerDependenciesMeta: + typescript: + optional: true + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -5563,6 +5661,12 @@ packages: '@types/react': optional: true + use-debounce@10.1.0: + resolution: {integrity: sha512-lu87Za35V3n/MyMoEpD5zJv0k7hCn0p+V/fK2kWD+3k2u3kOCwO593UArbczg1fhfs2rqPEnHpULJ3KmGdDzvg==} + engines: {node: '>= 16.0.0'} + peerDependencies: + react: '*' + use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} @@ -6214,6 +6318,12 @@ snapshots: '@floating-ui/core': 1.7.3 '@floating-ui/utils': 0.2.10 + '@floating-ui/react-dom@2.1.6(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + '@floating-ui/react-dom@2.1.6(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@floating-ui/dom': 1.7.4 @@ -6226,6 +6336,42 @@ snapshots: '@fontsource-variable/geist@5.2.8': {} + '@formatjs/ecma402-abstract@2.3.6': + dependencies: + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/intl-localematcher': 0.6.2 + decimal.js: 10.6.0 + tslib: 2.8.1 + + '@formatjs/fast-memoize@2.2.7': + dependencies: + tslib: 2.8.1 + + '@formatjs/icu-messageformat-parser@2.11.4': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/icu-skeleton-parser': 1.8.16 + tslib: 2.8.1 + + '@formatjs/icu-skeleton-parser@1.8.16': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.6.2': + dependencies: + tslib: 2.8.1 + + '@formatjs/intl@3.1.8(typescript@5.9.3)': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/icu-messageformat-parser': 2.11.4 + intl-messageformat: 10.7.18 + tslib: 2.8.1 + optionalDependencies: + typescript: 5.9.3 + '@hey-api/client-fetch@0.13.1(@hey-api/openapi-ts@0.91.1(typescript@5.9.3))': dependencies: '@hey-api/openapi-ts': 0.91.1(typescript@5.9.3) @@ -6589,10 +6735,21 @@ snapshots: '@prisma/debug': 5.22.0 optional: true + '@radix-ui/colors@3.0.0': {} + '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.1.3': {} + '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) @@ -6602,6 +6759,23 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6619,6 +6793,20 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6633,6 +6821,15 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) @@ -6642,6 +6839,15 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) @@ -6651,6 +6857,19 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) @@ -6664,6 +6883,22 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6680,6 +6915,22 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6696,6 +6947,18 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) @@ -6714,6 +6977,20 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6734,6 +7011,28 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + aria-hidden: 1.2.6 + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6762,6 +7061,19 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6775,6 +7087,21 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6796,6 +7123,17 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) @@ -6807,6 +7145,20 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-form@0.1.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-form@0.1.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6821,6 +7173,23 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6849,6 +7218,15 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 + '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) @@ -6858,22 +7236,48 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.12)(react@19.1.4) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + aria-hidden: 1.2.6 + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) aria-hidden: 1.2.6 @@ -6884,6 +7288,24 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6902,6 +7324,28 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6924,6 +7368,26 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/number': 1.1.1 @@ -6944,6 +7408,22 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6960,6 +7440,29 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + aria-hidden: 1.2.6 + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6983,6 +7486,24 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/rect': 1.1.1 + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.1.4))(react@19.1.4) @@ -7001,6 +7522,16 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) @@ -7011,6 +7542,16 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) @@ -7021,6 +7562,15 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) @@ -7030,6 +7580,16 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) @@ -7040,6 +7600,24 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7058,6 +7636,23 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7075,6 +7670,23 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/number': 1.1.1 @@ -7092,6 +7704,35 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-select@2.2.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + aria-hidden: 1.2.6 + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-select@2.2.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/number': 1.1.1 @@ -7121,6 +7762,15 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) @@ -7130,6 +7780,25 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-slider@1.3.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-slider@1.3.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/number': 1.1.1 @@ -7156,6 +7825,21 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7171,6 +7855,22 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7187,6 +7887,26 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7207,6 +7927,21 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7222,6 +7957,17 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7233,6 +7979,21 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7248,6 +8009,26 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -7329,6 +8110,15 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4) @@ -7340,6 +8130,18 @@ snapshots: '@radix-ui/rect@1.1.1': {} + '@radix-ui/themes@3.3.0(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/colors': 3.0.0 + classnames: 2.5.1 + radix-ui: 1.4.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + react-remove-scroll-bar: 2.3.8(@types/react@19.1.12)(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@rolldown/pluginutils@1.0.0-beta.40': {} '@rolldown/pluginutils@1.0.0-beta.43': {} @@ -8025,6 +8827,11 @@ snapshots: '@types/minimatch': 5.1.2 '@types/node': 24.9.1 + '@types/hoist-non-react-statics@3.3.7(@types/react@19.1.12)': + dependencies: + '@types/react': 19.1.12 + hoist-non-react-statics: 3.3.2 + '@types/inquirer@6.5.0': dependencies: '@types/through': 0.0.33 @@ -8228,6 +9035,37 @@ snapshots: iron-webcrypto: 2.0.0 jose: 6.1.3 + '@workos-inc/node@8.1.0': + dependencies: + iron-webcrypto: 2.0.0 + jose: 6.1.3 + + '@workos-inc/widgets-i18n@0.1.0(@workos-inc/widgets@1.8.0(@radix-ui/themes@3.3.0(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(@tanstack/react-query@5.90.8(react@19.1.4))(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(react@19.1.4)(typescript@5.9.3)': + dependencies: + '@workos-inc/widgets': 1.8.0(@radix-ui/themes@3.3.0(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(@tanstack/react-query@5.90.8(react@19.1.4))(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-intl: 7.1.14(react@19.1.4)(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + '@workos-inc/widgets@1.8.0(@radix-ui/themes@3.3.0(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(@tanstack/react-query@5.90.8(react@19.1.4))(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-form': 0.1.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-icons': 1.3.2(react@19.1.4) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/themes': 3.3.0(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@tanstack/react-query': 5.90.8(react@19.1.4) + bowser: 2.12.1 + clsx: 2.1.1 + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + use-debounce: 10.1.0(react@19.1.4) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + '@workos/authkit-session@0.3.4': dependencies: '@workos-inc/node': 8.0.0 @@ -8447,6 +9285,8 @@ snapshots: boolbase@1.0.0: {} + bowser@2.12.1: {} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -8623,6 +9463,8 @@ snapshots: dependencies: clsx: 2.1.1 + classnames@2.5.1: {} + clean-stack@2.2.0: {} cli-cursor@3.1.0: @@ -8742,6 +9584,8 @@ snapshots: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + decode-formdata@0.9.0: {} decompress-response@6.0.0: @@ -9577,6 +10421,10 @@ snapshots: dependencies: hermes-estree: 0.25.1 + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + htmlparser2@10.0.0: dependencies: domelementtype: 2.3.0 @@ -9678,6 +10526,13 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + intl-messageformat@10.7.18: + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/icu-messageformat-parser': 2.11.4 + tslib: 2.8.1 + ip-address@10.0.1: {} iron-webcrypto@2.0.0: @@ -10544,6 +11399,69 @@ snapshots: queue-microtask@1.2.3: {} + radix-ui@1.4.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4): + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-form': 0.1.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-select': 2.2.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.12)(react@19.1.4) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + react: 19.1.4 + react-dom: 19.1.4(react@19.1.4) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + radix-ui@1.4.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.2.3(react@19.1.4))(react@19.1.4): dependencies: '@radix-ui/primitive': 1.1.3 @@ -10629,6 +11547,20 @@ snapshots: react: 19.1.4 scheduler: 0.27.0 + react-intl@7.1.14(react@19.1.4)(typescript@5.9.3): + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/icu-messageformat-parser': 2.11.4 + '@formatjs/intl': 3.1.8(typescript@5.9.3) + '@types/hoist-non-react-statics': 3.3.7(@types/react@19.1.12) + '@types/react': 19.1.12 + hoist-non-react-statics: 3.3.2 + intl-messageformat: 10.7.18 + react: 19.1.4 + tslib: 2.8.1 + optionalDependencies: + typescript: 5.9.3 + react-is@16.13.1: {} react-refresh@0.18.0: {} @@ -11313,6 +12245,10 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 + use-debounce@10.1.0(react@19.1.4): + dependencies: + react: 19.1.4 + use-sidecar@1.1.3(@types/react@19.1.12)(react@19.1.4): dependencies: detect-node-es: 1.1.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1f7fc66..db52ccf 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,43 +4,43 @@ packages: - tooling/* catalog: - "@better-auth/cli": 1.4.0-beta.9 - "@eslint/js": ^9.38.0 - "@tailwindcss/postcss": ^4.1.16 - "@tailwindcss/vite": ^4.1.16 - "@tanstack/react-form": ^1.23.8 - "@tanstack/react-query": ^5.90.8 - "@trpc/client": ^11.7.1 - "@trpc/server": ^11.7.1 - "@trpc/tanstack-react-query": ^11.7.1 - "@types/node": ^22.18.12 - "@vitejs/plugin-react": 5.1.0 + '@better-auth/cli': 1.4.0-beta.9 + '@eslint/js': ^9.38.0 + '@tailwindcss/postcss': ^4.1.16 + '@tailwindcss/vite': ^4.1.16 + '@tanstack/react-form': ^1.23.8 + '@tanstack/react-query': 5.90.8 + '@trpc/client': ^11.7.1 + '@trpc/server': ^11.7.1 + '@trpc/tanstack-react-query': ^11.7.1 + '@types/node': ^22.18.12 + '@vitejs/plugin-react': 5.1.0 better-auth: 1.4.0-beta.9 eslint: ^9.38.0 prettier: ^3.6.2 tailwindcss: ^4.1.16 typescript: ^5.9.3 - zod: ^4.1.12 vite: 7.1.12 + zod: ^4.1.12 catalogs: react19: - "@types/react": ~19.1.0 - "@types/react-dom": ~19.1.0 + '@types/react': ~19.1.0 + '@types/react-dom': ~19.1.0 react: 19.1.4 react-dom: 19.1.4 linkWorkspacePackages: true onlyBuiltDependencies: - - "@tailwindcss/oxide" + - '@tailwindcss/oxide' - esbuild overrides: - "@types/minimatch": 5.1.2 + '@types/minimatch': 5.1.2 lightningcss: 1.30.1 vite: 7.1.12 publicHoistPattern: - - "@ianvs/prettier-plugin-sort-imports" + - '@ianvs/prettier-plugin-sort-imports' - prettier-plugin-tailwindcss diff --git a/services/ai-service/pyproject.toml b/services/ai-service/pyproject.toml index ea7689e..036fbd3 100644 --- a/services/ai-service/pyproject.toml +++ b/services/ai-service/pyproject.toml @@ -9,12 +9,12 @@ dependencies = [ "pdfplumber>=0.10.0", "python-dotenv>=1.0.0", "pydantic>=2.0.0", - "pypdf>=3.0.0", - "ipykernel>=7.1.0", + # Removed pypdf (redundant with pdfplumber) + # Removed ipykernel (only needed for dev/notebooks) "qdrant-client>=1.8.0", "haystack-ai>=2.0.0", "transformers>=4.35.0", - "torch>=2.0.0", + # torch installed separately as CPU-only in Dockerfile "sentence-transformers>=2.2.0", "accelerate>=0.24.0", "python-multipart>=0.0.9", @@ -32,4 +32,10 @@ dependencies = [ "uvicorn>=0.40.0", ] +[tool.uv] +# Exclude torch from uv sync since we install CPU version manually +override-dependencies = [ + "torch==2.5.1", +] + diff --git a/services/ai-service/run.py b/services/ai-service/run.py index f7d10af..3f054b6 100644 --- a/services/ai-service/run.py +++ b/services/ai-service/run.py @@ -1,8 +1,14 @@ """Run the API: python run.py (from ai-service directory). Use --reload for auto-restart on code changes.""" + import os import uvicorn +env_requirements = ["OPENROUTER_API_KEY", "HF_TOKEN", "GROQ_API_KEY"] + +for var in env_requirements: + if not os.getenv(var): + raise EnvironmentError(f"Environment variable {var} is not set.") host = os.getenv("SERVER_HOST", "0.0.0.0") port = int(os.getenv("SERVER_PORT", "8000")) uvicorn.run("src.api.app:app", host=host, port=port) diff --git a/services/ai-service/src/agent/run.py b/services/ai-service/src/agent/run.py index 7cc97ce..7ce01eb 100644 --- a/services/ai-service/src/agent/run.py +++ b/services/ai-service/src/agent/run.py @@ -19,7 +19,7 @@ def _project_root() -> Path: def run_evaluation_agent( framework_name: str, files: list[tuple[str, bytes]], - control_ids_per_file: list[str] | None = None, + domain_ids: list[str] | None = None, ) -> dict[str, Any]: """ Run the evaluation agent: persist files, build mimic JSON, run graph, return final state. @@ -27,8 +27,8 @@ def run_evaluation_agent( Args: framework_name: Framework name (e.g. NDI). files: List of (filename, bytes); 1 or more files. - control_ids_per_file: List of comma-separated control ID strings (one per file). - Must have the same length as files. Can be empty strings if not provided. + domain_ids: List of domain IDs (one per file). Each domain ID maps to a set of controls. + Must have the same length as files if provided. Returns: dict with mimic_json, evaluation_id, report_path, file_evaluations. @@ -36,15 +36,15 @@ def run_evaluation_agent( if not files: raise ValueError("At least one file is required") n = len(files) - if control_ids_per_file is not None and len(control_ids_per_file) != n: - raise ValueError(f"control_ids_per_file has {len(control_ids_per_file)} entries but {n} files; they must match") + if domain_ids is not None and len(domain_ids) != n: + raise ValueError(f"domain_ids has {len(domain_ids)} entries but {n} files; they must match") evaluation_id = str(uuid.uuid4()) root = _project_root() eval_dir = root / "data" / "evaluations" / evaluation_id eval_dir.mkdir(parents=True, exist_ok=True) - # Persist files and build file list with path and field_id + # Persist files and build file list with path, field_id, and domain_id file_list = [] for i, (filename, body) in enumerate(files): raw_name = Path(filename or f"file_{i+1}").name @@ -52,14 +52,20 @@ def run_evaluation_agent( path = eval_dir / safe_name path.write_bytes(body) field_id = f"field_{i + 1}" - file_list.append({"path": str(path), "extracted_text": "", "field_id": field_id}) + domain_id = domain_ids[i] if domain_ids else "" + file_list.append({ + "path": str(path), + "extracted_text": "", + "field_id": field_id, + "domain_id": domain_id, + }) - # Build mimic JSON: from client control_ids (must match file count) - if control_ids_per_file: - field_control_ids = [(f"field_{i+1}", s.strip()) for i, s in enumerate(control_ids_per_file)] + # Build mimic JSON from domain_ids + if domain_ids: + field_domain_ids = [(f"field_{i+1}", d) for i, d in enumerate(domain_ids)] else: - field_control_ids = [(f"field_{i+1}", "") for i in range(n)] - mimic_json = build_mimic_json(framework_name, field_control_ids) + field_domain_ids = [(f"field_{i+1}", "") for i in range(n)] + mimic_json = build_mimic_json(framework_name, field_domain_ids) initial_state: EvaluationState = { "evaluation_id": evaluation_id, diff --git a/services/ai-service/src/api/routers/evaluations.py b/services/ai-service/src/api/routers/evaluations.py index 17e14d0..c0917d2 100644 --- a/services/ai-service/src/api/routers/evaluations.py +++ b/services/ai-service/src/api/routers/evaluations.py @@ -1,6 +1,6 @@ """ Evaluation endpoints: submit files + framework, get mimic JSON + report PDF path. -Number of files is flexible; one control_ids field per file (control_ids_1, control_ids_2, ...). +Supports domain_ids parameter to specify which domain each file belongs to. """ import os @@ -14,10 +14,10 @@ router = APIRouter(prefix="/api/v1/evaluations", tags=["evaluations"]) -_MAX_FILES = 20 +_MAX_FILES = 120 # 12 domains ร— 10 files per domain _MAX_UPLOAD_BYTES = int(os.getenv("MAX_UPLOAD_SIZE_MB", "50")) * 1024 * 1024 _FRAMEWORK_NAME_RE = re.compile(r"^[a-zA-Z0-9_\-]+$") -_CONTROL_IDS_RE = re.compile(r"^[a-zA-Z0-9.,\-_\s]*$") +_DOMAIN_IDS_RE = re.compile(r"^[a-zA-Z0-9_,\-\s]+$") def _sanitize_filename(filename: str) -> str: @@ -28,38 +28,23 @@ def _sanitize_filename(filename: str) -> str: return (name or "file")[:255] -def _control_ids_form(i: int, default: str = ""): - return Form(default=default, description=f"Comma-separated control IDs for file {i}") - - @router.post("/submit", response_model=SubmitEvaluationResponse) async def submit_evaluation( - framework_name: str = Form(..., min_length=1, max_length=64, description="Framework identifier (e.g. NDI)"), - files: list[UploadFile] = File(..., description="Files (PDF, DOCX, PPTX, CSV, XLSX); 1 or more"), - control_ids_1: str = _control_ids_form(1), - control_ids_2: str = _control_ids_form(2), - control_ids_3: str = _control_ids_form(3), - control_ids_4: str = _control_ids_form(4), - control_ids_5: str = _control_ids_form(5), - control_ids_6: str = _control_ids_form(6), - control_ids_7: str = _control_ids_form(7), - control_ids_8: str = _control_ids_form(8), - control_ids_9: str = _control_ids_form(9), - control_ids_10: str = _control_ids_form(10), - control_ids_11: str = _control_ids_form(11), - control_ids_12: str = _control_ids_form(12), - control_ids_13: str = _control_ids_form(13), - control_ids_14: str = _control_ids_form(14), - control_ids_15: str = _control_ids_form(15), - control_ids_16: str = _control_ids_form(16), - control_ids_17: str = _control_ids_form(17), - control_ids_18: str = _control_ids_form(18), - control_ids_19: str = _control_ids_form(19), - control_ids_20: str = _control_ids_form(20), + framework_name: str = Form( + ..., min_length=1, max_length=64, description="Framework identifier (e.g. NDI)" + ), + files: list[UploadFile] = File( + ..., description="Files (PDF, DOCX, PPTX, CSV, XLSX); 1 or more" + ), + domain_ids: str = Form( + default="", + description="Comma-separated domain IDs, one per file (e.g. '1_Data_Governance,1_Data_Governance,2_Data_Catalog')" + ), ) -> SubmitEvaluationResponse: """ - Submit an evaluation: files + framework name. Each file has its own control_ids field - (control_ids_1 for file 1, control_ids_2 for file 2, etc.). Each value = comma-separated IDs. + Submit an evaluation: files + framework name + domain_ids. + domain_ids is a comma-separated list of domain IDs (one per file). + Files are evaluated against the controls for their respective domains. Returns DB-mimic JSON + evaluation_id + report_path. """ if not _FRAMEWORK_NAME_RE.match(framework_name): @@ -73,25 +58,30 @@ async def submit_evaluation( n_files = len(files) if n_files > _MAX_FILES: - raise HTTPException(status_code=400, detail=f"Maximum {_MAX_FILES} files allowed") + raise HTTPException( + status_code=400, detail=f"Maximum {_MAX_FILES} files allowed" + ) - control_ids_fields = [ - control_ids_1, control_ids_2, control_ids_3, control_ids_4, control_ids_5, - control_ids_6, control_ids_7, control_ids_8, control_ids_9, control_ids_10, - control_ids_11, control_ids_12, control_ids_13, control_ids_14, control_ids_15, - control_ids_16, control_ids_17, control_ids_18, control_ids_19, control_ids_20, - ] - control_ids_per_file = [] - for s in control_ids_fields[:n_files]: - ids = s.strip() - if ids and not _CONTROL_IDS_RE.match(ids): - raise HTTPException(status_code=400, detail=f"Invalid control ID format: {ids[:100]}") - control_ids_per_file.append(ids) + # Parse domain_ids + domain_ids_list = [] + if domain_ids.strip(): + if not _DOMAIN_IDS_RE.match(domain_ids): + raise HTTPException( + status_code=400, detail="Invalid domain_ids format" + ) + domain_ids_list = [d.strip() for d in domain_ids.split(",")] + if len(domain_ids_list) != n_files: + raise HTTPException( + status_code=400, + detail=f"domain_ids has {len(domain_ids_list)} entries but {n_files} files; they must match" + ) file_tuples: list[tuple[str, bytes]] = [] for u in files: if not u.filename: - raise HTTPException(status_code=400, detail="Each file must have a filename") + raise HTTPException( + status_code=400, detail="Each file must have a filename" + ) body = await u.read() if not body: raise HTTPException(status_code=400, detail=f"File '{u.filename}' is empty") @@ -106,7 +96,7 @@ async def submit_evaluation( result = service.submit_evaluation( framework_name=framework_name, files=file_tuples, - control_ids_per_file=control_ids_per_file, + domain_ids=domain_ids_list if domain_ids_list else None, ) return SubmitEvaluationResponse( diff --git a/services/ai-service/src/services/evaluation_service.py b/services/ai-service/src/services/evaluation_service.py index b39ebeb..9c0e972 100644 --- a/services/ai-service/src/services/evaluation_service.py +++ b/services/ai-service/src/services/evaluation_service.py @@ -1,5 +1,5 @@ """ -Evaluation service: submit 15 files + framework, run agent, return mimic JSON + report path. +Evaluation service: submit files + framework, run agent, return mimic JSON + report path. No DB; files and report saved to filesystem. """ @@ -15,16 +15,16 @@ def submit_evaluation( self, framework_name: str, files: list[tuple[str, bytes]], - control_ids_per_file: list[str] | None = None, + domain_ids: list[str] | None = None, ) -> dict[str, Any]: """ - Submit an evaluation: files (1+) + framework name. control_ids_per_file must have the same - length as files (one comma-separated control ID string per file). + Submit an evaluation: files (1+) + framework name + domain_ids. + domain_ids must have the same length as files (one domain ID per file). Runs the LangGraph agent, saves report PDF to data/reports/{evaluation_id}.pdf. Returns mimic_json, evaluation_id, report_path, file_evaluations. """ return run_evaluation_agent( framework_name=framework_name, files=files, - control_ids_per_file=control_ids_per_file, + domain_ids=domain_ids, ) diff --git a/services/ai-service/uv.lock b/services/ai-service/uv.lock index 42d15ed..838a05f 100644 --- a/services/ai-service/uv.lock +++ b/services/ai-service/uv.lock @@ -10,6 +10,9 @@ resolution-markers = [ "python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] +[manifest] +overrides = [{ name = "torch", specifier = "==2.5.1" }] + [[package]] name = "accelerate" version = "1.12.0" @@ -38,7 +41,6 @@ dependencies = [ { name = "fastapi" }, { name = "groq" }, { name = "haystack-ai" }, - { name = "ipykernel" }, { name = "langchain-core" }, { name = "langchain-groq" }, { name = "langgraph" }, @@ -47,7 +49,6 @@ dependencies = [ { name = "pandas" }, { name = "pdfplumber" }, { name = "pydantic" }, - { name = "pypdf" }, { name = "python-docx" }, { name = "python-dotenv" }, { name = "python-multipart" }, @@ -55,7 +56,6 @@ dependencies = [ { name = "qdrant-client" }, { name = "reportlab" }, { name = "sentence-transformers" }, - { name = "torch" }, { name = "transformers" }, { name = "uvicorn" }, ] @@ -67,7 +67,6 @@ requires-dist = [ { name = "fastapi", specifier = ">=0.128.0" }, { name = "groq", specifier = ">=0.4.0" }, { name = "haystack-ai", specifier = ">=2.0.0" }, - { name = "ipykernel", specifier = ">=7.1.0" }, { name = "langchain-core", specifier = ">=0.3.0" }, { name = "langchain-groq", specifier = ">=0.2.0" }, { name = "langgraph", specifier = ">=0.2.0" }, @@ -76,7 +75,6 @@ requires-dist = [ { name = "pandas", specifier = ">=2.0.0" }, { name = "pdfplumber", specifier = ">=0.10.0" }, { name = "pydantic", specifier = ">=2.0.0" }, - { name = "pypdf", specifier = ">=3.0.0" }, { name = "python-docx", specifier = ">=1.0.0" }, { name = "python-dotenv", specifier = ">=1.0.0" }, { name = "python-multipart", specifier = ">=0.0.9" }, @@ -84,7 +82,6 @@ requires-dist = [ { name = "qdrant-client", specifier = ">=1.8.0" }, { name = "reportlab", specifier = ">=4.0.0" }, { name = "sentence-transformers", specifier = ">=2.2.0" }, - { name = "torch", specifier = ">=2.0.0" }, { name = "transformers", specifier = ">=4.35.0" }, { name = "uvicorn", specifier = ">=0.40.0" }, ] @@ -128,24 +125,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, ] -[[package]] -name = "appnope" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, -] - -[[package]] -name = "asttokens" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, -] - [[package]] name = "attrs" version = "25.4.0" @@ -280,15 +259,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "comm" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, -] - [[package]] name = "cryptography" version = "46.0.3" @@ -345,54 +315,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, ] -[[package]] -name = "cuda-bindings" -version = "12.9.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-pathfinder", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/56/e465c31dc9111be3441a9ba7df1941fe98f4aa6e71e8788a3fb4534ce24d/cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f", size = 11906628, upload-time = "2025-10-21T14:51:49.905Z" }, - { url = "https://files.pythonhosted.org/packages/a3/84/1e6be415e37478070aeeee5884c2022713c1ecc735e6d82d744de0252eee/cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb", size = 11925991, upload-time = "2025-10-21T14:51:56.535Z" }, - { url = "https://files.pythonhosted.org/packages/d1/af/6dfd8f2ed90b1d4719bc053ff8940e494640fe4212dc3dd72f383e4992da/cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686", size = 11922703, upload-time = "2025-10-21T14:52:03.585Z" }, - { url = "https://files.pythonhosted.org/packages/6c/19/90ac264acc00f6df8a49378eedec9fd2db3061bf9263bf9f39fd3d8377c3/cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee", size = 11924658, upload-time = "2025-10-21T14:52:10.411Z" }, -] - -[[package]] -name = "cuda-pathfinder" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/02/4dbe7568a42e46582248942f54dc64ad094769532adbe21e525e4edf7bc4/cuda_pathfinder-1.3.3-py3-none-any.whl", hash = "sha256:9984b664e404f7c134954a771be8775dfd6180ea1e1aef4a5a37d4be05d9bbb1", size = 27154, upload-time = "2025-12-04T22:35:08.996Z" }, -] - -[[package]] -name = "debugpy" -version = "1.8.19" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/73/75/9e12d4d42349b817cd545b89247696c67917aab907012ae5b64bbfea3199/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb", size = 1644590, upload-time = "2025-12-15T21:53:28.044Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/3d/388035a31a59c26f1ecc8d86af607d0c42e20ef80074147cd07b180c4349/debugpy-1.8.19-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:91e35db2672a0abaf325f4868fcac9c1674a0d9ad9bb8a8c849c03a5ebba3e6d", size = 2538859, upload-time = "2025-12-15T21:53:50.478Z" }, - { url = "https://files.pythonhosted.org/packages/4a/19/c93a0772d0962294f083dbdb113af1a7427bb632d36e5314297068f55db7/debugpy-1.8.19-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:85016a73ab84dea1c1f1dcd88ec692993bcbe4532d1b49ecb5f3c688ae50c606", size = 4292575, upload-time = "2025-12-15T21:53:51.821Z" }, - { url = "https://files.pythonhosted.org/packages/5c/56/09e48ab796b0a77e3d7dc250f95251832b8bf6838c9632f6100c98bdf426/debugpy-1.8.19-cp313-cp313-win32.whl", hash = "sha256:b605f17e89ba0ecee994391194285fada89cee111cfcd29d6f2ee11cbdc40976", size = 5286209, upload-time = "2025-12-15T21:53:53.602Z" }, - { url = "https://files.pythonhosted.org/packages/fb/4e/931480b9552c7d0feebe40c73725dd7703dcc578ba9efc14fe0e6d31cfd1/debugpy-1.8.19-cp313-cp313-win_amd64.whl", hash = "sha256:c30639998a9f9cd9699b4b621942c0179a6527f083c72351f95c6ab1728d5b73", size = 5328206, upload-time = "2025-12-15T21:53:55.433Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b9/cbec520c3a00508327476c7fce26fbafef98f412707e511eb9d19a2ef467/debugpy-1.8.19-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:1e8c4d1bd230067bf1bbcdbd6032e5a57068638eb28b9153d008ecde288152af", size = 2537372, upload-time = "2025-12-15T21:53:57.318Z" }, - { url = "https://files.pythonhosted.org/packages/88/5e/cf4e4dc712a141e10d58405c58c8268554aec3c35c09cdcda7535ff13f76/debugpy-1.8.19-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d40c016c1f538dbf1762936e3aeb43a89b965069d9f60f9e39d35d9d25e6b809", size = 4268729, upload-time = "2025-12-15T21:53:58.712Z" }, - { url = "https://files.pythonhosted.org/packages/82/a3/c91a087ab21f1047db328c1d3eb5d1ff0e52de9e74f9f6f6fa14cdd93d58/debugpy-1.8.19-cp314-cp314-win32.whl", hash = "sha256:0601708223fe1cd0e27c6cce67a899d92c7d68e73690211e6788a4b0e1903f5b", size = 5286388, upload-time = "2025-12-15T21:54:00.687Z" }, - { url = "https://files.pythonhosted.org/packages/17/b8/bfdc30b6e94f1eff09f2dc9cc1f9cd1c6cde3d996bcbd36ce2d9a4956e99/debugpy-1.8.19-cp314-cp314-win_amd64.whl", hash = "sha256:8e19a725f5d486f20e53a1dde2ab8bb2c9607c40c00a42ab646def962b41125f", size = 5327741, upload-time = "2025-12-15T21:54:02.148Z" }, - { url = "https://files.pythonhosted.org/packages/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38", size = 5292321, upload-time = "2025-12-15T21:54:16.024Z" }, -] - -[[package]] -name = "decorator" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, -] - [[package]] name = "distro" version = "1.9.0" @@ -420,15 +342,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, ] -[[package]] -name = "executing" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, -] - [[package]] name = "fastapi" version = "0.128.0" @@ -691,75 +604,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] -[[package]] -name = "ipykernel" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "appnope", marker = "sys_platform == 'darwin'" }, - { name = "comm" }, - { name = "debugpy" }, - { name = "ipython" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "matplotlib-inline" }, - { name = "nest-asyncio" }, - { name = "packaging" }, - { name = "psutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/a4/4948be6eb88628505b83a1f2f40d90254cab66abf2043b3c40fa07dfce0f/ipykernel-7.1.0.tar.gz", hash = "sha256:58a3fc88533d5930c3546dc7eac66c6d288acde4f801e2001e65edc5dc9cf0db", size = 174579, upload-time = "2025-10-27T09:46:39.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/17/20c2552266728ceba271967b87919664ecc0e33efca29c3efc6baf88c5f9/ipykernel-7.1.0-py3-none-any.whl", hash = "sha256:763b5ec6c5b7776f6a8d7ce09b267693b4e5ce75cb50ae696aaefb3c85e1ea4c", size = 117968, upload-time = "2025-10-27T09:46:37.805Z" }, -] - -[[package]] -name = "ipython" -version = "9.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "ipython-pygments-lexers" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/46/dd/fb08d22ec0c27e73c8bc8f71810709870d51cadaf27b7ddd3f011236c100/ipython-9.9.0.tar.gz", hash = "sha256:48fbed1b2de5e2c7177eefa144aba7fcb82dac514f09b57e2ac9da34ddb54220", size = 4425043, upload-time = "2026-01-05T12:36:46.233Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.0-py3-none-any.whl", hash = "sha256:b457fe9165df2b84e8ec909a97abcf2ed88f565970efba16b1f7229c283d252b", size = 621431, upload-time = "2026-01-05T12:36:44.669Z" }, -] - -[[package]] -name = "ipython-pygments-lexers" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, -] - -[[package]] -name = "jedi" -version = "0.19.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "parso" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, -] - [[package]] name = "jinja2" version = "3.1.6" @@ -880,35 +724,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] -[[package]] -name = "jupyter-client" -version = "8.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-core" }, - { name = "python-dateutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020, upload-time = "2026-01-08T13:55:47.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371, upload-time = "2026-01-08T13:55:45.562Z" }, -] - -[[package]] -name = "jupyter-core" -version = "5.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "platformdirs" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, -] - [[package]] name = "langchain-core" version = "1.2.7" @@ -1151,18 +966,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] -[[package]] -name = "matplotlib-inline" -version = "0.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, -] - [[package]] name = "mdurl" version = "0.1.2" @@ -1190,15 +993,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] -[[package]] -name = "nest-asyncio" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, -] - [[package]] name = "networkx" version = "3.6.1" @@ -1260,77 +1054,69 @@ wheels = [ [[package]] name = "nvidia-cublas-cu12" -version = "12.8.4.1" +version = "12.4.5.8" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" }, + { url = "https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b", size = 363438805, upload-time = "2024-04-03T20:57:06.025Z" }, ] [[package]] name = "nvidia-cuda-cupti-cu12" -version = "12.8.90" +version = "12.4.127" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" }, + { url = "https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb", size = 13813957, upload-time = "2024-04-03T20:55:01.564Z" }, ] [[package]] name = "nvidia-cuda-nvrtc-cu12" -version = "12.8.93" +version = "12.4.127" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" }, + { url = "https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338", size = 24640306, upload-time = "2024-04-03T20:56:01.463Z" }, ] [[package]] name = "nvidia-cuda-runtime-cu12" -version = "12.8.90" +version = "12.4.127" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" }, + { url = "https://files.pythonhosted.org/packages/ea/27/1795d86fe88ef397885f2e580ac37628ed058a92ed2c39dc8eac3adf0619/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5", size = 883737, upload-time = "2024-04-03T20:54:51.355Z" }, ] [[package]] name = "nvidia-cudnn-cu12" -version = "9.10.2.21" +version = "9.1.0.70" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nvidia-cublas-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, + { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741, upload-time = "2024-04-22T15:24:15.253Z" }, ] [[package]] name = "nvidia-cufft-cu12" -version = "11.3.3.83" +version = "11.2.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.13.1.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" }, + { url = "https://files.pythonhosted.org/packages/27/94/3266821f65b92b3138631e9c8e7fe1fb513804ac934485a8d05776e1dd43/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9", size = 211459117, upload-time = "2024-04-03T20:57:40.402Z" }, ] [[package]] name = "nvidia-curand-cu12" -version = "10.3.9.90" +version = "10.3.5.147" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6d/44ad094874c6f1b9c654f8ed939590bdc408349f137f9b98a3a23ccec411/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b", size = 56305206, upload-time = "2024-04-03T20:58:08.722Z" }, ] [[package]] name = "nvidia-cusolver-cu12" -version = "11.7.3.90" +version = "11.6.1.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nvidia-cublas-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, @@ -1338,58 +1124,42 @@ dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, + { url = "https://files.pythonhosted.org/packages/3a/e1/5b9089a4b2a4790dfdea8b3a006052cfecff58139d5a4e34cb1a51df8d6f/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260", size = 127936057, upload-time = "2024-04-03T20:58:28.735Z" }, ] [[package]] name = "nvidia-cusparse-cu12" -version = "12.5.8.93" +version = "12.3.1.170" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nvidia-nvjitlink-cu12", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" }, + { url = "https://files.pythonhosted.org/packages/db/f7/97a9ea26ed4bbbfc2d470994b8b4f338ef663be97b8f677519ac195e113d/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1", size = 207454763, upload-time = "2024-04-03T20:58:59.995Z" }, ] [[package]] name = "nvidia-nccl-cu12" -version = "2.27.5" +version = "2.21.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" }, + { url = "https://files.pythonhosted.org/packages/df/99/12cd266d6233f47d00daf3a72739872bdc10267d0383508b0b9c84a18bb6/nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0", size = 188654414, upload-time = "2024-04-03T15:32:57.427Z" }, ] [[package]] name = "nvidia-nvjitlink-cu12" -version = "12.8.93" +version = "12.4.127" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" }, -] - -[[package]] -name = "nvidia-nvshmem-cu12" -version = "3.4.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/09/6ea3ea725f82e1e76684f0708bbedd871fc96da89945adeba65c3835a64c/nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd", size = 139103095, upload-time = "2025-09-06T00:32:31.266Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57", size = 21066810, upload-time = "2024-04-03T20:59:46.957Z" }, ] [[package]] name = "nvidia-nvtx-cu12" -version = "12.8.90" +version = "12.4.127" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a", size = 99144, upload-time = "2024-04-03T20:56:12.406Z" }, ] [[package]] @@ -1544,15 +1314,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e6/3f/a80ac00acbc6b35166b42850e98a4f466e2c0d9c64054161ba9620f95680/pandas-3.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1c39eab3ad38f2d7a249095f0a3d8f8c22cc0f847e98ccf5bbe732b272e2d9fa", size = 9441003, upload-time = "2026-01-21T15:52:02.281Z" }, ] -[[package]] -name = "parso" -version = "0.8.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, -] - [[package]] name = "pdfminer-six" version = "20251230" @@ -1580,18 +1341,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8b/c8/cdbc975f5b634e249cfa6597e37c50f3078412474f21c015e508bfbfe3c3/pdfplumber-0.11.9-py3-none-any.whl", hash = "sha256:33ec5580959ba524e9100138746e090879504c42955df1b8a997604dd326c443", size = 60045, upload-time = "2026-01-05T08:10:27.512Z" }, ] -[[package]] -name = "pexpect" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, -] - [[package]] name = "pillow" version = "11.3.0" @@ -1647,15 +1396,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, ] -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - [[package]] name = "portalocker" version = "3.2.0" @@ -1685,18 +1425,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/f6/8d4a2d1b67368fec425f32911e2f3638d5ac9e8abfebc698ac426fcf65db/posthog-7.6.0-py3-none-any.whl", hash = "sha256:c4dd78cf77c4fecceb965f86066e5ac37886ef867d68ffe75a1db5d681d7d9ad", size = 168426, upload-time = "2026-01-19T16:23:02.71Z" }, ] -[[package]] -name = "prompt-toolkit" -version = "3.0.52" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, -] - [[package]] name = "protobuf" version = "6.33.4" @@ -1740,24 +1468,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/73/2ce007f4198c80fcf2cb24c169884f833fe93fbc03d55d302627b094ee91/psutil-7.2.1-cp37-abi3-win_arm64.whl", hash = "sha256:0d67c1822c355aa6f7314d92018fb4268a76668a536f133599b91edd48759442", size = 133836, upload-time = "2025-12-29T08:26:43.086Z" }, ] -[[package]] -name = "ptyprocess" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, -] - -[[package]] -name = "pure-eval" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, -] - [[package]] name = "pycparser" version = "3.0" @@ -1844,15 +1554,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] -[[package]] -name = "pypdf" -version = "6.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/f4/801632a8b62a805378b6af2b5a3fcbfd8923abf647e0ed1af846a83433b2/pypdf-6.6.0.tar.gz", hash = "sha256:4c887ef2ea38d86faded61141995a3c7d068c9d6ae8477be7ae5de8a8e16592f", size = 5281063, upload-time = "2026-01-09T11:20:11.786Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/ba/96f99276194f720e74ed99905a080f6e77810558874e8935e580331b46de/pypdf-6.6.0-py3-none-any.whl", hash = "sha256:bca9091ef6de36c7b1a81e09327c554b7ce51e88dad68f5890c2b4a4417f1fd7", size = 328963, upload-time = "2026-01-09T11:20:09.278Z" }, -] - [[package]] name = "pypdfium2" version = "5.3.0" @@ -1989,49 +1690,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] -[[package]] -name = "pyzmq" -version = "27.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "implementation_name == 'pypy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, - { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, - { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, - { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, - { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, - { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, - { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, - { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436, upload-time = "2025-09-08T23:08:20.801Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301, upload-time = "2025-09-08T23:08:22.47Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197, upload-time = "2025-09-08T23:08:24.286Z" }, - { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275, upload-time = "2025-09-08T23:08:26.063Z" }, - { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469, upload-time = "2025-09-08T23:08:27.623Z" }, - { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961, upload-time = "2025-09-08T23:08:29.672Z" }, - { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282, upload-time = "2025-09-08T23:08:31.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468, upload-time = "2025-09-08T23:08:33.543Z" }, - { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394, upload-time = "2025-09-08T23:08:35.51Z" }, - { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964, upload-time = "2025-09-08T23:08:37.178Z" }, - { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029, upload-time = "2025-09-08T23:08:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541, upload-time = "2025-09-08T23:08:42.668Z" }, - { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197, upload-time = "2025-09-08T23:08:44.973Z" }, - { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175, upload-time = "2025-09-08T23:08:46.601Z" }, - { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427, upload-time = "2025-09-08T23:08:48.187Z" }, - { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929, upload-time = "2025-09-08T23:08:49.76Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193, upload-time = "2025-09-08T23:08:51.7Z" }, - { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388, upload-time = "2025-09-08T23:08:53.393Z" }, - { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316, upload-time = "2025-09-08T23:08:55.702Z" }, - { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472, upload-time = "2025-09-08T23:08:58.18Z" }, - { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401, upload-time = "2025-09-08T23:08:59.802Z" }, - { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170, upload-time = "2025-09-08T23:09:01.418Z" }, -] - [[package]] name = "qdrant-client" version = "1.16.2" @@ -2410,20 +2068,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] -[[package]] -name = "stack-data" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asttokens" }, - { name = "executing" }, - { name = "pure-eval" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, -] - [[package]] name = "starlette" version = "0.50.0" @@ -2438,14 +2082,14 @@ wheels = [ [[package]] name = "sympy" -version = "1.14.0" +version = "1.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/99/5a5b6f19ff9f083671ddf7b9632028436167cd3d33e11015754e41b249a4/sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f", size = 7533040, upload-time = "2024-07-19T09:26:51.238Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8", size = 6189177, upload-time = "2024-07-19T09:26:48.863Z" }, ] [[package]] @@ -2494,10 +2138,9 @@ wheels = [ [[package]] name = "torch" -version = "2.10.0" +version = "2.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-bindings", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "filelock" }, { name = "fsspec" }, { name = "jinja2" }, @@ -2508,56 +2151,18 @@ dependencies = [ { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "setuptools" }, { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/6f/f2e91e34e3fcba2e3fc8d8f74e7d6c22e74e480bbd1db7bc8900fdf3e95c/torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b", size = 146004247, upload-time = "2026-01-21T16:24:29.335Z" }, - { url = "https://files.pythonhosted.org/packages/98/fb/5160261aeb5e1ee12ee95fe599d0541f7c976c3701d607d8fc29e623229f/torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738", size = 915716445, upload-time = "2026-01-21T16:22:45.353Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/502fb1b41e6d868e8deb5b0e3ae926bbb36dab8ceb0d1b769b266ad7b0c3/torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57", size = 113757050, upload-time = "2026-01-21T16:24:19.204Z" }, - { url = "https://files.pythonhosted.org/packages/1a/0b/39929b148f4824bc3ad6f9f72a29d4ad865bcf7ebfc2fa67584773e083d2/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382", size = 79851305, upload-time = "2026-01-21T16:24:09.209Z" }, - { url = "https://files.pythonhosted.org/packages/d8/14/21fbce63bc452381ba5f74a2c0a959fdf5ad5803ccc0c654e752e0dbe91a/torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8", size = 146005472, upload-time = "2026-01-21T16:22:29.022Z" }, - { url = "https://files.pythonhosted.org/packages/54/fd/b207d1c525cb570ef47f3e9f836b154685011fce11a2f444ba8a4084d042/torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f", size = 915612644, upload-time = "2026-01-21T16:21:47.019Z" }, - { url = "https://files.pythonhosted.org/packages/36/53/0197f868c75f1050b199fe58f9bf3bf3aecac9b4e85cc9c964383d745403/torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8", size = 113997015, upload-time = "2026-01-21T16:23:00.767Z" }, - { url = "https://files.pythonhosted.org/packages/0e/13/e76b4d9c160e89fff48bf16b449ea324bda84745d2ab30294c37c2434c0d/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f", size = 79498248, upload-time = "2026-01-21T16:23:09.315Z" }, - { url = "https://files.pythonhosted.org/packages/4f/93/716b5ac0155f1be70ed81bacc21269c3ece8dba0c249b9994094110bfc51/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a", size = 79464992, upload-time = "2026-01-21T16:23:05.162Z" }, - { url = "https://files.pythonhosted.org/packages/69/2b/51e663ff190c9d16d4a8271203b71bc73a16aa7619b9f271a69b9d4a936b/torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60", size = 146018567, upload-time = "2026-01-21T16:22:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/5e/cd/4b95ef7f293b927c283db0b136c42be91c8ec6845c44de0238c8c23bdc80/torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5", size = 915721646, upload-time = "2026-01-21T16:21:16.983Z" }, - { url = "https://files.pythonhosted.org/packages/56/97/078a007208f8056d88ae43198833469e61a0a355abc0b070edd2c085eb9a/torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c", size = 113752373, upload-time = "2026-01-21T16:22:13.471Z" }, - { url = "https://files.pythonhosted.org/packages/d8/94/71994e7d0d5238393df9732fdab607e37e2b56d26a746cb59fdb415f8966/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28", size = 79850324, upload-time = "2026-01-21T16:22:09.494Z" }, - { url = "https://files.pythonhosted.org/packages/e2/65/1a05346b418ea8ccd10360eef4b3e0ce688fba544e76edec26913a8d0ee0/torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63", size = 146006482, upload-time = "2026-01-21T16:22:18.42Z" }, - { url = "https://files.pythonhosted.org/packages/1d/b9/5f6f9d9e859fc3235f60578fa64f52c9c6e9b4327f0fe0defb6de5c0de31/torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6", size = 915613050, upload-time = "2026-01-21T16:20:49.035Z" }, - { url = "https://files.pythonhosted.org/packages/66/4d/35352043ee0eaffdeff154fad67cd4a31dbed7ff8e3be1cc4549717d6d51/torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185", size = 113995816, upload-time = "2026-01-21T16:22:05.312Z" }, -] - -[[package]] -name = "tornado" -version = "6.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632, upload-time = "2025-12-15T19:21:03.836Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909, upload-time = "2025-12-15T19:20:48.382Z" }, - { url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163, upload-time = "2025-12-15T19:20:49.791Z" }, - { url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746, upload-time = "2025-12-15T19:20:51.491Z" }, - { url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083, upload-time = "2025-12-15T19:20:52.778Z" }, - { url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315, upload-time = "2025-12-15T19:20:53.996Z" }, - { url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003, upload-time = "2025-12-15T19:20:56.101Z" }, - { url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412, upload-time = "2025-12-15T19:20:57.398Z" }, - { url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392, upload-time = "2025-12-15T19:20:58.692Z" }, - { url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481, upload-time = "2025-12-15T19:21:00.008Z" }, - { url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886, upload-time = "2025-12-15T19:21:01.287Z" }, - { url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910, upload-time = "2025-12-15T19:21:02.571Z" }, + { url = "https://files.pythonhosted.org/packages/69/72/20cb30f3b39a9face296491a86adb6ff8f1a47a897e4d14667e6cf89d5c3/torch-2.5.1-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:9b61edf3b4f6e3b0e0adda8b3960266b9009d02b37555971f4d1c8f7a05afed7", size = 906393265, upload-time = "2024-10-29T17:35:06.866Z" }, ] [[package]] @@ -2572,15 +2177,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] -[[package]] -name = "traitlets" -version = "5.14.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, -] - [[package]] name = "transformers" version = "4.57.6" @@ -2602,17 +2198,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/03/b8/e484ef633af3887baeeb4b6ad12743363af7cce68ae51e938e00aaa0529d/transformers-4.57.6-py3-none-any.whl", hash = "sha256:4c9e9de11333ddfe5114bc872c9f370509198acf0b87a832a0ab9458e2bd0550", size = 11993498, upload-time = "2026-01-16T10:38:31.289Z" }, ] -[[package]] -name = "triton" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, - { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, - { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, - { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, -] - [[package]] name = "typing-extensions" version = "4.15.0" @@ -2687,15 +2272,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, ] -[[package]] -name = "wcwidth" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/75/2144b65e4fba12a2d9868e9a3f99db7fa0760670d064603634bef9ff1709/wcwidth-0.3.0.tar.gz", hash = "sha256:af1a2fb0b83ef4a7fc0682a4c95ca2576e14d0280bca2a9e67b7dc9f2733e123", size = 172238, upload-time = "2026-01-21T17:44:09.508Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/0e/a5f0257ab47492b7afb5fb60347d14ba19445e2773fc8352d4be6bd2f6f8/wcwidth-0.3.0-py3-none-any.whl", hash = "sha256:073a1acb250e4add96cfd5ef84e0036605cd6e0d0782c8c15c80e42202348458", size = 85520, upload-time = "2026-01-21T17:44:08.002Z" }, -] - [[package]] name = "xlsxwriter" version = "3.2.9" diff --git a/tooling/tailwind/theme.css b/tooling/tailwind/theme.css index 95af4cb..5003557 100644 --- a/tooling/tailwind/theme.css +++ b/tooling/tailwind/theme.css @@ -158,4 +158,4 @@ --tracking-wide: calc(var(--tracking-normal) + 0.025em); --tracking-wider: calc(var(--tracking-normal) + 0.05em); --tracking-widest: calc(var(--tracking-normal) + 0.1em); -} \ No newline at end of file +}