From ea742c93869fb33b155a6a14920ec88568a0df2a Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Sun, 18 Jan 2026 09:31:31 +1000 Subject: [PATCH] feat: add scenarios system for collection testing Implements the complete scenarios feature for TPMJS: API endpoints: - GET/POST /api/scenarios - list/create scenarios - GET/PATCH/DELETE /api/scenarios/:id - scenario CRUD - POST /api/scenarios/:id/run - execute scenario - GET /api/scenarios/:id/runs - run history - POST /api/scenarios/check-similarity - vector similarity check - GET /api/scenarios/featured - featured scenarios - POST /api/collections/:id/scenarios/generate - AI scenario generation - GET /api/collections/:id/scenarios - list collection scenarios Services: - generate-prompt.ts - AI prompt generation using GPT-4o-mini - similarity.ts - vector embedding and cosine similarity - evaluate.ts - LLM-based success evaluation - execute.ts - scenario execution orchestration CLI commands: - tpm scenario list [collection] - list scenarios - tpm scenario run - run all scenarios - tpm scenario test - run single scenario - tpm scenario generate - generate scenarios - tpm scenario info - scenario details Database: - Scenario, ScenarioEmbedding, ScenarioRun, ScenarioQuota models - Streak-based quality scoring - Daily quota management Migration script included for converting existing useCases. --- apps/web/next-env.d.ts | 2 +- .../[id]/scenarios/generate/route.ts | 208 ++ .../api/collections/[id]/scenarios/route.ts | 141 + apps/web/src/app/api/scenarios/[id]/route.ts | 405 +++ .../src/app/api/scenarios/[id]/run/route.ts | 178 ++ .../src/app/api/scenarios/[id]/runs/route.ts | 196 ++ .../api/scenarios/check-similarity/route.ts | 147 + .../src/app/api/scenarios/featured/route.ts | 168 ++ apps/web/src/app/api/scenarios/route.ts | 313 ++ apps/web/src/lib/scenarios/evaluate.ts | 151 + apps/web/src/lib/scenarios/execute.ts | 311 ++ apps/web/src/lib/scenarios/generate-prompt.ts | 143 + apps/web/src/lib/scenarios/similarity.ts | 170 ++ packages/cli/oclif.manifest.json | 520 ++-- packages/cli/package.json | 5 +- packages/cli/src/commands/auth/login.ts | 50 +- .../cli/src/commands/scenario/generate.ts | 164 + packages/cli/src/commands/scenario/info.ts | 190 ++ packages/cli/src/commands/scenario/list.ts | 181 ++ packages/cli/src/commands/scenario/run.ts | 208 ++ packages/cli/src/commands/scenario/test.ts | 168 ++ packages/cli/src/lib/api-client.ts | 165 +- packages/cli/src/lib/output.ts | 21 + packages/db/prisma/schema.prisma | 134 + pnpm-lock.yaml | 2681 ++++++++++++++++- scripts/migrate-use-cases-to-scenarios.ts | 184 ++ 26 files changed, 6922 insertions(+), 282 deletions(-) create mode 100644 apps/web/src/app/api/collections/[id]/scenarios/generate/route.ts create mode 100644 apps/web/src/app/api/collections/[id]/scenarios/route.ts create mode 100644 apps/web/src/app/api/scenarios/[id]/route.ts create mode 100644 apps/web/src/app/api/scenarios/[id]/run/route.ts create mode 100644 apps/web/src/app/api/scenarios/[id]/runs/route.ts create mode 100644 apps/web/src/app/api/scenarios/check-similarity/route.ts create mode 100644 apps/web/src/app/api/scenarios/featured/route.ts create mode 100644 apps/web/src/app/api/scenarios/route.ts create mode 100644 apps/web/src/lib/scenarios/evaluate.ts create mode 100644 apps/web/src/lib/scenarios/execute.ts create mode 100644 apps/web/src/lib/scenarios/generate-prompt.ts create mode 100644 apps/web/src/lib/scenarios/similarity.ts create mode 100644 packages/cli/src/commands/scenario/generate.ts create mode 100644 packages/cli/src/commands/scenario/info.ts create mode 100644 packages/cli/src/commands/scenario/list.ts create mode 100644 packages/cli/src/commands/scenario/run.ts create mode 100644 packages/cli/src/commands/scenario/test.ts create mode 100644 scripts/migrate-use-cases-to-scenarios.ts diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/apps/web/next-env.d.ts +++ b/apps/web/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/web/src/app/api/collections/[id]/scenarios/generate/route.ts b/apps/web/src/app/api/collections/[id]/scenarios/generate/route.ts new file mode 100644 index 0000000..e8ff866 --- /dev/null +++ b/apps/web/src/app/api/collections/[id]/scenarios/generate/route.ts @@ -0,0 +1,208 @@ +/** + * Scenario Generation API + * + * POST /api/collections/[id]/scenarios/generate Generate AI-powered scenarios for a collection + */ + +import { prisma } from '@tpmjs/db'; +import type { NextRequest } from 'next/server'; +import { z } from 'zod'; +import { authenticateRequest } from '~/lib/api-keys/middleware'; +import { + apiForbidden, + apiInternalError, + apiNotFound, + apiSuccess, + apiValidationError, +} from '~/lib/api-response'; +import { AI_GENERATION_RATE_LIMIT, checkRateLimitDistributed } from '~/lib/rate-limit'; +import { generateScenarios } from '~/lib/scenarios/generate-prompt'; +import { checkSimilarity, generateAndStoreEmbedding } from '~/lib/scenarios/similarity'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; +export const maxDuration = 120; // AI generation + embeddings can take time + +type RouteContext = { + params: Promise<{ id: string }>; +}; + +const GenerateRequestSchema = z.object({ + count: z.number().int().min(1).max(10).default(1), + skipSimilarityCheck: z.boolean().default(false), +}); + +/** + * POST /api/collections/[id]/scenarios/generate + * Generate AI-powered scenarios for a collection + * + * Body: + * - count: Number of scenarios to generate (1-10, default: 1) + * - skipSimilarityCheck: If true, skip duplicate warning (default: false) + * + * Returns generated scenarios with similarity warnings if applicable + */ +export async function POST(request: NextRequest, context: RouteContext) { + const requestId = crypto.randomUUID(); + + try { + // Check authentication + const authResult = await authenticateRequest(); + + if (!authResult.authenticated || !authResult.userId) { + return apiForbidden('Authentication required to generate scenarios', requestId); + } + + // Rate limit check (strict for AI operations) + const rateLimitResponse = await checkRateLimitDistributed(request, AI_GENERATION_RATE_LIMIT); + if (rateLimitResponse) { + return rateLimitResponse; + } + + const { id } = await context.params; + + // Parse request body + let body: { count?: number; skipSimilarityCheck?: boolean } = {}; + try { + body = await request.json(); + } catch { + // Empty body is fine, use defaults + } + + const parseResult = GenerateRequestSchema.safeParse(body); + if (!parseResult.success) { + return apiValidationError( + 'Invalid request body', + { errors: parseResult.error.flatten().fieldErrors }, + requestId + ); + } + + const { count, skipSimilarityCheck } = parseResult.data; + + // Fetch collection with tools + const collection = await prisma.collection.findUnique({ + where: { id }, + include: { + tools: { + include: { + tool: { + select: { + name: true, + description: true, + }, + }, + }, + orderBy: { position: 'asc' }, + }, + }, + }); + + if (!collection) { + return apiNotFound('Collection', requestId); + } + + // User must own the collection to generate scenarios + if (collection.userId !== authResult.userId) { + return apiForbidden('You can only generate scenarios for your own collections', requestId); + } + + // Validate collection has tools + if (collection.tools.length === 0) { + return apiForbidden( + 'Collection must have at least one tool to generate scenarios', + requestId + ); + } + + // Prepare collection info for AI generation + const collectionInfo = { + name: collection.name, + description: collection.description, + tools: collection.tools.map((ct) => ({ + name: ct.tool.name, + description: ct.tool.description, + })), + }; + + // Generate scenarios with AI + const generatedScenarios = await generateScenarios(collectionInfo, count); + + // Process each generated scenario + const results: Array<{ + scenario: { + id: string; + prompt: string; + name: string | null; + tags: string[]; + }; + similarity?: { + hasSimilar: boolean; + maxSimilarity: number; + similar: Array<{ id: string; name: string | null; similarity: number }>; + }; + }> = []; + + for (const generated of generatedScenarios) { + // Check similarity unless skipped + let similarityResult: { + hasSimilar: boolean; + maxSimilarity: number; + similarScenarios: Array<{ + scenario: { id: string; name: string | null }; + similarity: number; + }>; + } | null = null; + + if (!skipSimilarityCheck) { + similarityResult = await checkSimilarity(generated.prompt, collection.id); + } + + // Create the scenario + const scenario = await prisma.scenario.create({ + data: { + collectionId: collection.id, + prompt: generated.prompt, + name: generated.name, + tags: generated.tags, + }, + }); + + // Generate and store embedding for future similarity checks + await generateAndStoreEmbedding(scenario.id, generated.prompt); + + results.push({ + scenario: { + id: scenario.id, + prompt: scenario.prompt, + name: scenario.name, + tags: scenario.tags, + }, + ...(similarityResult && similarityResult.hasSimilar + ? { + similarity: { + hasSimilar: true, + maxSimilarity: Math.round(similarityResult.maxSimilarity * 100), + similar: similarityResult.similarScenarios.slice(0, 3).map((s) => ({ + id: s.scenario.id, + name: s.scenario.name, + similarity: Math.round(s.similarity * 100), + })), + }, + } + : {}), + }); + } + + return apiSuccess( + { + scenarios: results, + generatedAt: new Date().toISOString(), + }, + { requestId, status: 201 } + ); + } catch (error) { + console.error('[API Error] POST /api/collections/[id]/scenarios/generate:', error); + return apiInternalError('Failed to generate scenarios', requestId); + } +} diff --git a/apps/web/src/app/api/collections/[id]/scenarios/route.ts b/apps/web/src/app/api/collections/[id]/scenarios/route.ts new file mode 100644 index 0000000..1dd7679 --- /dev/null +++ b/apps/web/src/app/api/collections/[id]/scenarios/route.ts @@ -0,0 +1,141 @@ +/** + * Collection Scenarios API + * + * GET /api/collections/[id]/scenarios List scenarios for a collection + */ + +import { prisma } from '@tpmjs/db'; +import type { NextRequest } from 'next/server'; +import { authenticateRequest } from '~/lib/api-keys/middleware'; +import { apiForbidden, apiInternalError, apiNotFound, apiSuccess } from '~/lib/api-response'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; +export const maxDuration = 30; + +type RouteContext = { + params: Promise<{ id: string }>; +}; + +/** + * GET /api/collections/[id]/scenarios + * List scenarios for a collection + * + * Query params: + * - limit: Max results (default: 50, max: 100) + * - offset: Pagination offset (default: 0) + * - status: Filter by lastRunStatus (optional) + */ +export async function GET(request: NextRequest, context: RouteContext) { + const requestId = crypto.randomUUID(); + + try { + const { id } = await context.params; + const { searchParams } = new URL(request.url); + const limit = Math.min( + Math.max(Number.parseInt(searchParams.get('limit') || '50', 10), 1), + 100 + ); + const offset = Math.max(Number.parseInt(searchParams.get('offset') || '0', 10), 0); + const statusFilter = searchParams.get('status'); + + // Check authentication (optional - affects what data is shown) + const authResult = await authenticateRequest(); + + // Fetch collection + const collection = await prisma.collection.findUnique({ + where: { id }, + select: { + id: true, + isPublic: true, + userId: true, + name: true, + }, + }); + + if (!collection) { + return apiNotFound('Collection', requestId); + } + + // Check access + const isOwner = authResult.authenticated && collection.userId === authResult.userId; + if (!collection.isPublic && !isOwner) { + return apiForbidden('Access denied', requestId); + } + + // Build where clause + const where: { + collectionId: string; + lastRunStatus?: string; + } = { collectionId: id }; + + if (statusFilter) { + where.lastRunStatus = statusFilter; + } + + // Fetch scenarios with pagination + const scenarios = await prisma.scenario.findMany({ + where, + orderBy: [{ qualityScore: 'desc' }, { createdAt: 'desc' }], + take: limit + 1, + skip: offset, + select: { + id: true, + prompt: true, + name: true, + description: true, + tags: true, + qualityScore: true, + totalRuns: true, + lastRunAt: true, + lastRunStatus: true, + consecutivePasses: true, + consecutiveFails: true, + createdAt: true, + updatedAt: true, + }, + }); + + const hasMore = scenarios.length > limit; + const data = hasMore ? scenarios.slice(0, limit) : scenarios; + + return apiSuccess( + { + collection: { + id: collection.id, + name: collection.name, + }, + scenarios: data.map((s) => ({ + id: s.id, + prompt: s.prompt.slice(0, 200) + (s.prompt.length > 200 ? '...' : ''), + name: s.name, + description: s.description, + tags: s.tags, + metrics: { + qualityScore: s.qualityScore, + totalRuns: s.totalRuns, + consecutivePasses: s.consecutivePasses, + consecutiveFails: s.consecutiveFails, + lastRunStatus: s.lastRunStatus, + lastRunAt: s.lastRunAt, + }, + timestamps: { + createdAt: s.createdAt, + updatedAt: s.updatedAt, + }, + })), + }, + { + requestId, + pagination: { + limit, + offset, + hasMore, + }, + } + ); + } catch (error) { + console.error('[API Error] GET /api/collections/[id]/scenarios:', error); + return apiInternalError('Failed to fetch scenarios', requestId); + } +} diff --git a/apps/web/src/app/api/scenarios/[id]/route.ts b/apps/web/src/app/api/scenarios/[id]/route.ts new file mode 100644 index 0000000..d402cc7 --- /dev/null +++ b/apps/web/src/app/api/scenarios/[id]/route.ts @@ -0,0 +1,405 @@ +/** + * Scenario API - Get, Update, Delete individual scenario + * + * GET /api/scenarios/[id] Get scenario details + * PATCH /api/scenarios/[id] Update scenario + * DELETE /api/scenarios/[id] Delete scenario + */ + +import { Prisma, prisma } from '@tpmjs/db'; +import { type NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; +import { authenticateRequest } from '~/lib/api-keys/middleware'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; +export const maxDuration = 60; + +const API_VERSION = '1.0.0'; + +interface ApiResponse { + success: boolean; + data?: T; + error?: { + code: string; + message: string; + details?: Record; + }; + meta: { + version: string; + timestamp: string; + requestId?: string; + }; +} + +interface RouteContext { + params: Promise<{ id: string }>; +} + +// Validation schema for updating a scenario +const UpdateScenarioSchema = z.object({ + prompt: z.string().min(10).optional(), + name: z.string().max(200).nullish(), + description: z.string().nullish(), + assertions: z + .object({ + regex: z.array(z.string()).optional(), + schema: z.record(z.string(), z.unknown()).optional(), + }) + .nullish(), + tags: z.array(z.string()).optional(), +}); + +/** + * GET /api/scenarios/[id] + * Get scenario details with recent runs + * + * Query params: + * - runsLimit: Max runs to return (default: 10, max: 50) + */ +export async function GET( + request: NextRequest, + context: RouteContext +): Promise> { + const requestId = crypto.randomUUID(); + const { id } = await context.params; + + try { + const { searchParams } = new URL(request.url); + const runsLimit = Math.min( + Math.max(Number.parseInt(searchParams.get('runsLimit') || '10', 10), 1), + 50 + ); + + // Check authentication (optional - affects what data is shown) + const authResult = await authenticateRequest(); + + const scenario = await prisma.scenario.findUnique({ + where: { id }, + include: { + collection: { + select: { + id: true, + name: true, + slug: true, + isPublic: true, + userId: true, + user: { + select: { + username: true, + }, + }, + }, + }, + runs: { + orderBy: { createdAt: 'desc' }, + take: runsLimit, + select: { + id: true, + status: true, + evaluatorVerdict: true, + executionTimeMs: true, + totalTokens: true, + startedAt: true, + completedAt: true, + createdAt: true, + }, + }, + _count: { + select: { runs: true }, + }, + }, + }); + + if (!scenario) { + return NextResponse.json( + { + success: false, + error: { code: 'NOT_FOUND', message: 'Scenario not found' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 404 } + ); + } + + // Check access - collection must be public OR user must own it + const isOwner = authResult.authenticated && scenario.collection?.userId === authResult.userId; + const isPublic = scenario.collection?.isPublic ?? false; + + if (!isOwner && !isPublic) { + return NextResponse.json( + { + success: false, + error: { code: 'FORBIDDEN', message: 'Access denied' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 403 } + ); + } + + return NextResponse.json({ + success: true, + data: { + id: scenario.id, + collectionId: scenario.collectionId, + prompt: scenario.prompt, + name: scenario.name, + description: scenario.description, + assertions: scenario.assertions, + tags: scenario.tags, + qualityScore: scenario.qualityScore, + consecutivePasses: scenario.consecutivePasses, + consecutiveFails: scenario.consecutiveFails, + totalRuns: scenario.totalRuns, + lastRunAt: scenario.lastRunAt, + lastRunStatus: scenario.lastRunStatus, + createdAt: scenario.createdAt, + updatedAt: scenario.updatedAt, + isOwner, + collection: scenario.collection + ? { + id: scenario.collection.id, + name: scenario.collection.name, + slug: scenario.collection.slug, + username: scenario.collection.user.username, + } + : null, + recentRuns: scenario.runs, + runCount: scenario._count.runs, + }, + meta: { + version: API_VERSION, + timestamp: new Date().toISOString(), + requestId, + }, + }); + } catch (error) { + console.error('[API Error] GET /api/scenarios/[id]:', error); + return NextResponse.json( + { + success: false, + error: { code: 'INTERNAL_ERROR', message: 'Failed to fetch scenario' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 500 } + ); + } +} + +/** + * PATCH /api/scenarios/[id] + * Update a scenario + * + * Requires authentication and ownership of the collection + */ +export async function PATCH( + request: NextRequest, + context: RouteContext +): Promise> { + const requestId = crypto.randomUUID(); + const { id } = await context.params; + + try { + // Check authentication + const authResult = await authenticateRequest(); + + if (!authResult.authenticated || !authResult.userId) { + return NextResponse.json( + { + success: false, + error: { code: 'UNAUTHORIZED', message: 'Authentication required' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 401 } + ); + } + + // Find scenario and check ownership + const existing = await prisma.scenario.findUnique({ + where: { id }, + include: { + collection: { + select: { userId: true }, + }, + }, + }); + + if (!existing) { + return NextResponse.json( + { + success: false, + error: { code: 'NOT_FOUND', message: 'Scenario not found' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 404 } + ); + } + + if (existing.collection?.userId !== authResult.userId) { + return NextResponse.json( + { + success: false, + error: { code: 'FORBIDDEN', message: 'Access denied' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 403 } + ); + } + + // Parse and validate request body + const body = await request.json(); + const parseResult = UpdateScenarioSchema.safeParse(body); + + if (!parseResult.success) { + return NextResponse.json( + { + success: false, + error: { + code: 'VALIDATION_ERROR', + message: 'Invalid request body', + details: { errors: parseResult.error.flatten().fieldErrors }, + }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 400 } + ); + } + + const { prompt, name, description, assertions, tags } = parseResult.data; + + // Update scenario (transform null to Prisma.JsonNull for JSON fields) + const scenario = await prisma.scenario.update({ + where: { id }, + data: { + ...(prompt !== undefined && { prompt }), + ...(name !== undefined && { name }), + ...(description !== undefined && { description }), + ...(assertions !== undefined && { + assertions: + assertions === null + ? Prisma.JsonNull + : (assertions as unknown as Prisma.InputJsonValue), + }), + ...(tags !== undefined && { tags }), + }, + }); + + return NextResponse.json({ + success: true, + data: { + id: scenario.id, + collectionId: scenario.collectionId, + prompt: scenario.prompt, + name: scenario.name, + description: scenario.description, + assertions: scenario.assertions, + tags: scenario.tags, + qualityScore: scenario.qualityScore, + totalRuns: scenario.totalRuns, + updatedAt: scenario.updatedAt, + }, + meta: { + version: API_VERSION, + timestamp: new Date().toISOString(), + requestId, + }, + }); + } catch (error) { + console.error('[API Error] PATCH /api/scenarios/[id]:', error); + return NextResponse.json( + { + success: false, + error: { code: 'INTERNAL_ERROR', message: 'Failed to update scenario' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 500 } + ); + } +} + +/** + * DELETE /api/scenarios/[id] + * Delete a scenario + * + * Requires authentication and ownership of the collection + */ +export async function DELETE( + _request: NextRequest, + context: RouteContext +): Promise> { + const requestId = crypto.randomUUID(); + const { id } = await context.params; + + try { + // Check authentication + const authResult = await authenticateRequest(); + + if (!authResult.authenticated || !authResult.userId) { + return NextResponse.json( + { + success: false, + error: { code: 'UNAUTHORIZED', message: 'Authentication required' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 401 } + ); + } + + // Find scenario and check ownership + const existing = await prisma.scenario.findUnique({ + where: { id }, + include: { + collection: { + select: { userId: true }, + }, + }, + }); + + if (!existing) { + return NextResponse.json( + { + success: false, + error: { code: 'NOT_FOUND', message: 'Scenario not found' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 404 } + ); + } + + if (existing.collection?.userId !== authResult.userId) { + return NextResponse.json( + { + success: false, + error: { code: 'FORBIDDEN', message: 'Access denied' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 403 } + ); + } + + // Delete scenario (cascade will delete runs and embedding) + await prisma.scenario.delete({ + where: { id }, + }); + + return NextResponse.json({ + success: true, + data: { deleted: true }, + meta: { + version: API_VERSION, + timestamp: new Date().toISOString(), + requestId, + }, + }); + } catch (error) { + console.error('[API Error] DELETE /api/scenarios/[id]:', error); + return NextResponse.json( + { + success: false, + error: { code: 'INTERNAL_ERROR', message: 'Failed to delete scenario' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 500 } + ); + } +} diff --git a/apps/web/src/app/api/scenarios/[id]/run/route.ts b/apps/web/src/app/api/scenarios/[id]/run/route.ts new file mode 100644 index 0000000..ccc364f --- /dev/null +++ b/apps/web/src/app/api/scenarios/[id]/run/route.ts @@ -0,0 +1,178 @@ +/** + * Scenario Run API - Trigger a scenario execution + * + * POST /api/scenarios/[id]/run Execute a scenario + */ + +import { prisma } from '@tpmjs/db'; +import { type NextRequest, NextResponse } from 'next/server'; +import { authenticateRequest } from '~/lib/api-keys/middleware'; +import { checkAndDecrementQuota, executeScenario, getQuotaStatus } from '~/lib/scenarios/execute'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; +export const maxDuration = 300; // 5 minutes for scenario execution + +const API_VERSION = '1.0.0'; + +interface ApiResponse { + success: boolean; + data?: T; + error?: { + code: string; + message: string; + details?: Record; + }; + meta: { + version: string; + timestamp: string; + requestId?: string; + }; +} + +interface RouteContext { + params: Promise<{ id: string }>; +} + +/** + * POST /api/scenarios/[id]/run + * Execute a scenario + * + * Requires authentication + * Subject to daily quota limits + */ +export async function POST( + _request: NextRequest, + context: RouteContext +): Promise> { + const requestId = crypto.randomUUID(); + const { id: scenarioId } = await context.params; + + try { + // Check authentication + const authResult = await authenticateRequest(); + + if (!authResult.authenticated || !authResult.userId) { + return NextResponse.json( + { + success: false, + error: { code: 'UNAUTHORIZED', message: 'Authentication required' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 401 } + ); + } + + // Check scenario exists + const scenario = await prisma.scenario.findUnique({ + where: { id: scenarioId }, + include: { + collection: { + select: { + id: true, + isPublic: true, + userId: true, + }, + }, + }, + }); + + if (!scenario) { + return NextResponse.json( + { + success: false, + error: { code: 'NOT_FOUND', message: 'Scenario not found' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 404 } + ); + } + + // Check access - must be owner or collection must be public + const isOwner = scenario.collection?.userId === authResult.userId; + const isPublic = scenario.collection?.isPublic ?? false; + + if (!isOwner && !isPublic) { + return NextResponse.json( + { + success: false, + error: { code: 'FORBIDDEN', message: 'Access denied' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 403 } + ); + } + + // Check quota + const quota = await checkAndDecrementQuota(authResult.userId); + + if (!quota.allowed) { + const quotaStatus = await getQuotaStatus(authResult.userId); + return NextResponse.json( + { + success: false, + error: { + code: 'QUOTA_EXCEEDED', + message: 'Daily scenario run quota exceeded. Try again tomorrow.', + details: { + used: quotaStatus.used, + limit: quotaStatus.limit, + resetsAt: quotaStatus.resetsAt.toISOString(), + }, + }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 429 } + ); + } + + // Execute the scenario + const { run, success } = await executeScenario(scenario, authResult.userId); + + return NextResponse.json( + { + success: true, + data: { + runId: run.id, + status: run.status, + success, + evaluator: { + model: run.evaluatorModel, + verdict: run.evaluatorVerdict, + reason: run.evaluatorReason, + }, + assertions: run.assertionResults, + usage: { + inputTokens: run.inputTokens, + outputTokens: run.outputTokens, + totalTokens: run.totalTokens, + executionTimeMs: run.executionTimeMs, + }, + timestamps: { + startedAt: run.startedAt, + completedAt: run.completedAt, + createdAt: run.createdAt, + }, + quotaRemaining: quota.remaining, + }, + meta: { + version: API_VERSION, + timestamp: new Date().toISOString(), + requestId, + note: 'Execution uses simulated agent. Full agent integration coming soon.', + }, + }, + { status: 201 } + ); + } catch (error) { + console.error('[API Error] POST /api/scenarios/[id]/run:', error); + return NextResponse.json( + { + success: false, + error: { code: 'INTERNAL_ERROR', message: 'Failed to execute scenario' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 500 } + ); + } +} diff --git a/apps/web/src/app/api/scenarios/[id]/runs/route.ts b/apps/web/src/app/api/scenarios/[id]/runs/route.ts new file mode 100644 index 0000000..78fd05b --- /dev/null +++ b/apps/web/src/app/api/scenarios/[id]/runs/route.ts @@ -0,0 +1,196 @@ +/** + * Scenario Runs API - List run history + * + * GET /api/scenarios/[id]/runs Get all runs for a scenario (paginated) + */ + +import { prisma } from '@tpmjs/db'; +import { type NextRequest, NextResponse } from 'next/server'; +import { authenticateRequest } from '~/lib/api-keys/middleware'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; +export const maxDuration = 60; + +const API_VERSION = '1.0.0'; + +interface ApiResponse { + success: boolean; + data?: T; + error?: { + code: string; + message: string; + details?: Record; + }; + meta: { + version: string; + timestamp: string; + requestId?: string; + }; +} + +interface RouteContext { + params: Promise<{ id: string }>; +} + +/** + * GET /api/scenarios/[id]/runs + * Get run history for a scenario + * + * Query params: + * - limit: Max results (default: 20, max: 100) + * - offset: Pagination offset (default: 0) + * - status: Filter by status (optional) + */ +export async function GET( + request: NextRequest, + context: RouteContext +): Promise> { + const requestId = crypto.randomUUID(); + const { id: scenarioId } = await context.params; + + try { + const { searchParams } = new URL(request.url); + const limit = Math.min( + Math.max(Number.parseInt(searchParams.get('limit') || '20', 10), 1), + 100 + ); + const offset = Math.max(Number.parseInt(searchParams.get('offset') || '0', 10), 0); + const statusFilter = searchParams.get('status'); + + // Check authentication (optional - affects what data is shown) + const authResult = await authenticateRequest(); + + // Verify scenario exists and check access + const scenario = await prisma.scenario.findUnique({ + where: { id: scenarioId }, + include: { + collection: { + select: { + isPublic: true, + userId: true, + }, + }, + }, + }); + + if (!scenario) { + return NextResponse.json( + { + success: false, + error: { code: 'NOT_FOUND', message: 'Scenario not found' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 404 } + ); + } + + // Check access + const isOwner = authResult.authenticated && scenario.collection?.userId === authResult.userId; + const isPublic = scenario.collection?.isPublic ?? false; + + if (!isOwner && !isPublic) { + return NextResponse.json( + { + success: false, + error: { code: 'FORBIDDEN', message: 'Access denied' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 403 } + ); + } + + // Build where clause + const where: { scenarioId: string; status?: string } = { scenarioId }; + if (statusFilter) { + where.status = statusFilter; + } + + // Fetch runs + const runs = await prisma.scenarioRun.findMany({ + where, + orderBy: { createdAt: 'desc' }, + take: limit + 1, + skip: offset, + select: { + id: true, + status: true, + retryCount: true, + evaluatorModel: true, + evaluatorVerdict: true, + evaluatorReason: true, + assertionResults: true, + inputTokens: true, + outputTokens: true, + totalTokens: true, + executionTimeMs: true, + estimatedCost: true, + startedAt: true, + completedAt: true, + createdAt: true, + // Only include sensitive data if owner + ...(isOwner && { + output: true, + errorLog: true, + conversation: true, + }), + }, + }); + + const hasMore = runs.length > limit; + const data = hasMore ? runs.slice(0, limit) : runs; + + return NextResponse.json({ + success: true, + data: data.map((run) => ({ + id: run.id, + status: run.status, + retryCount: run.retryCount, + evaluator: { + model: run.evaluatorModel, + verdict: run.evaluatorVerdict, + reason: run.evaluatorReason, + }, + assertions: run.assertionResults, + usage: { + inputTokens: run.inputTokens, + outputTokens: run.outputTokens, + totalTokens: run.totalTokens, + executionTimeMs: run.executionTimeMs, + estimatedCost: run.estimatedCost, + }, + timestamps: { + startedAt: run.startedAt, + completedAt: run.completedAt, + createdAt: run.createdAt, + }, + // Only include if owner + ...('output' in run && { + output: run.output, + errorLog: run.errorLog, + conversation: run.conversation, + }), + })), + meta: { + version: API_VERSION, + timestamp: new Date().toISOString(), + requestId, + }, + pagination: { + limit, + offset, + hasMore, + }, + }); + } catch (error) { + console.error('[API Error] GET /api/scenarios/[id]/runs:', error); + return NextResponse.json( + { + success: false, + error: { code: 'INTERNAL_ERROR', message: 'Failed to fetch runs' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 500 } + ); + } +} diff --git a/apps/web/src/app/api/scenarios/check-similarity/route.ts b/apps/web/src/app/api/scenarios/check-similarity/route.ts new file mode 100644 index 0000000..f24d8b5 --- /dev/null +++ b/apps/web/src/app/api/scenarios/check-similarity/route.ts @@ -0,0 +1,147 @@ +/** + * Scenario Similarity Check API + * + * POST /api/scenarios/check-similarity Check if a prompt is similar to existing scenarios + */ + +import { prisma } from '@tpmjs/db'; +import { type NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; +import { authenticateRequest } from '~/lib/api-keys/middleware'; +import { checkSimilarity } from '~/lib/scenarios/similarity'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; +export const maxDuration = 30; + +const API_VERSION = '1.0.0'; + +interface ApiResponse { + success: boolean; + data?: T; + error?: { + code: string; + message: string; + details?: Record; + }; + meta: { + version: string; + timestamp: string; + requestId?: string; + }; +} + +const CheckSimilaritySchema = z.object({ + prompt: z.string().min(10, 'Prompt must be at least 10 characters'), + collectionId: z.string().min(1, 'Collection ID is required'), + excludeScenarioId: z.string().optional(), // For updates, exclude the scenario being edited +}); + +/** + * POST /api/scenarios/check-similarity + * Check if a prompt is similar to existing scenarios + * + * Returns warning if similarity >= 70% + */ +export async function POST(request: NextRequest): Promise> { + const requestId = crypto.randomUUID(); + + try { + // Check authentication + const authResult = await authenticateRequest(); + + if (!authResult.authenticated || !authResult.userId) { + return NextResponse.json( + { + success: false, + error: { code: 'UNAUTHORIZED', message: 'Authentication required' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 401 } + ); + } + + // Parse and validate request body + const body = await request.json(); + const parseResult = CheckSimilaritySchema.safeParse(body); + + if (!parseResult.success) { + return NextResponse.json( + { + success: false, + error: { + code: 'VALIDATION_ERROR', + message: 'Invalid request body', + details: { errors: parseResult.error.flatten().fieldErrors }, + }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 400 } + ); + } + + const { prompt, collectionId, excludeScenarioId } = parseResult.data; + + // Verify collection exists and user has access + const collection = await prisma.collection.findUnique({ + where: { id: collectionId }, + select: { id: true, userId: true, isPublic: true }, + }); + + if (!collection) { + return NextResponse.json( + { + success: false, + error: { code: 'NOT_FOUND', message: 'Collection not found' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 404 } + ); + } + + // User must own the collection or it must be public + if (collection.userId !== authResult.userId && !collection.isPublic) { + return NextResponse.json( + { + success: false, + error: { code: 'FORBIDDEN', message: 'Access denied' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 403 } + ); + } + + // Check similarity + const result = await checkSimilarity(prompt, collectionId, excludeScenarioId); + + return NextResponse.json({ + success: true, + data: { + hasSimilar: result.hasSimilar, + maxSimilarity: Math.round(result.maxSimilarity * 100), // Return as percentage + warningThreshold: 70, + similar: result.similarScenarios.map((s) => ({ + id: s.scenario.id, + name: s.scenario.name, + prompt: s.scenario.prompt.slice(0, 200) + (s.scenario.prompt.length > 200 ? '...' : ''), + similarity: Math.round(s.similarity * 100), + })), + }, + meta: { + version: API_VERSION, + timestamp: new Date().toISOString(), + requestId, + }, + }); + } catch (error) { + console.error('[API Error] POST /api/scenarios/check-similarity:', error); + return NextResponse.json( + { + success: false, + error: { code: 'INTERNAL_ERROR', message: 'Failed to check similarity' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 500 } + ); + } +} diff --git a/apps/web/src/app/api/scenarios/featured/route.ts b/apps/web/src/app/api/scenarios/featured/route.ts new file mode 100644 index 0000000..d4f77b5 --- /dev/null +++ b/apps/web/src/app/api/scenarios/featured/route.ts @@ -0,0 +1,168 @@ +/** + * Featured Scenarios API + * + * GET /api/scenarios/featured Get featured scenarios for homepage showcase + */ + +import { prisma } from '@tpmjs/db'; +import { type NextRequest, NextResponse } from 'next/server'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; +export const maxDuration = 30; + +const API_VERSION = '1.0.0'; + +interface ApiResponse { + success: boolean; + data?: T; + error?: { + code: string; + message: string; + details?: Record; + }; + meta: { + version: string; + timestamp: string; + requestId?: string; + }; +} + +/** + * GET /api/scenarios/featured + * Get featured scenarios for homepage showcase + * + * Returns an algorithmic mix of: + * - High quality scenarios (by qualityScore) + * - Diverse scenarios (different collections/tags) + * - Fresh scenarios (recently created) + * + * Query params: + * - limit: Max results (default: 6, max: 20) + */ +export async function GET(request: NextRequest): Promise> { + const requestId = crypto.randomUUID(); + + try { + const { searchParams } = new URL(request.url); + const limit = Math.min(Math.max(Number.parseInt(searchParams.get('limit') || '6', 10), 1), 20); + + // Get high quality scenarios (top 40%) + const highQuality = await prisma.scenario.findMany({ + where: { + collection: { isPublic: true }, + qualityScore: { gte: 0.3 }, + totalRuns: { gte: 1 }, + }, + orderBy: { qualityScore: 'desc' }, + take: Math.ceil(limit * 0.4), + include: { + collection: { + select: { + id: true, + name: true, + slug: true, + user: { select: { username: true } }, + }, + }, + }, + }); + + // Get diverse scenarios - different collections (30%) + const seenCollections = new Set(highQuality.map((s) => s.collectionId)); + const diverse = await prisma.scenario.findMany({ + where: { + collection: { isPublic: true }, + collectionId: { notIn: Array.from(seenCollections).filter(Boolean) as string[] }, + totalRuns: { gte: 1 }, + }, + orderBy: { qualityScore: 'desc' }, + take: Math.ceil(limit * 0.3), + include: { + collection: { + select: { + id: true, + name: true, + slug: true, + user: { select: { username: true } }, + }, + }, + }, + }); + + // Get fresh scenarios (30%) + const seenIds = new Set([...highQuality, ...diverse].map((s) => s.id)); + const fresh = await prisma.scenario.findMany({ + where: { + collection: { isPublic: true }, + id: { notIn: Array.from(seenIds) }, + }, + orderBy: { createdAt: 'desc' }, + take: Math.ceil(limit * 0.3), + include: { + collection: { + select: { + id: true, + name: true, + slug: true, + user: { select: { username: true } }, + }, + }, + }, + }); + + // Combine and shuffle slightly for variety + const combined = [...highQuality, ...diverse, ...fresh]; + + // Simple shuffle to mix the categories + for (let i = combined.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + const temp = combined[i]; + const swapWith = combined[j]; + if (temp !== undefined && swapWith !== undefined) { + combined[i] = swapWith; + combined[j] = temp; + } + } + + // Take only the requested limit + const featured = combined.slice(0, limit); + + return NextResponse.json({ + success: true, + data: featured.map((s) => ({ + id: s.id, + name: s.name, + prompt: s.prompt.slice(0, 150) + (s.prompt.length > 150 ? '...' : ''), + tags: s.tags.slice(0, 3), + qualityScore: s.qualityScore, + totalRuns: s.totalRuns, + lastRunStatus: s.lastRunStatus, + collection: s.collection + ? { + id: s.collection.id, + name: s.collection.name, + slug: s.collection.slug, + username: s.collection.user.username, + } + : null, + })), + meta: { + version: API_VERSION, + timestamp: new Date().toISOString(), + requestId, + algorithm: 'mixed-quality-diversity-freshness', + }, + }); + } catch (error) { + console.error('[API Error] GET /api/scenarios/featured:', error); + return NextResponse.json( + { + success: false, + error: { code: 'INTERNAL_ERROR', message: 'Failed to fetch featured scenarios' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 500 } + ); + } +} diff --git a/apps/web/src/app/api/scenarios/route.ts b/apps/web/src/app/api/scenarios/route.ts new file mode 100644 index 0000000..110e09f --- /dev/null +++ b/apps/web/src/app/api/scenarios/route.ts @@ -0,0 +1,313 @@ +/** + * Scenarios API - List and Create + * + * GET /api/scenarios List all public scenarios (paginated) + * POST /api/scenarios Create a new scenario + */ + +import { prisma } from '@tpmjs/db'; +import { type NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; +import { authenticateRequest } from '~/lib/api-keys/middleware'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; +export const maxDuration = 60; + +const API_VERSION = '1.0.0'; + +interface ApiResponse { + success: boolean; + data?: T; + error?: { + code: string; + message: string; + details?: Record; + }; + meta: { + version: string; + timestamp: string; + requestId?: string; + }; +} + +// Validation schema for creating a scenario +const CreateScenarioSchema = z.object({ + collectionId: z.string().min(1, 'Collection ID is required'), + prompt: z.string().min(10, 'Prompt must be at least 10 characters'), + name: z.string().max(200).optional(), + description: z.string().optional(), + assertions: z + .object({ + regex: z.array(z.string()).optional(), + schema: z.record(z.string(), z.unknown()).optional(), + }) + .optional(), + tags: z.array(z.string()).optional(), +}); + +/** + * GET /api/scenarios + * List all public scenarios (paginated) + * + * Query params: + * - limit: Max results (default: 20, max: 100) + * - offset: Pagination offset (default: 0) + * - collectionId: Filter by collection (optional) + * - tags: Comma-separated tags to filter by (optional) + * - sortBy: Sort field (default: 'qualityScore') + */ +export async function GET(request: NextRequest): Promise> { + const requestId = crypto.randomUUID(); + + try { + const { searchParams } = new URL(request.url); + const limit = Math.min( + Math.max(Number.parseInt(searchParams.get('limit') || '20', 10), 1), + 100 + ); + const offset = Math.max(Number.parseInt(searchParams.get('offset') || '0', 10), 0); + const collectionId = searchParams.get('collectionId'); + const tagsParam = searchParams.get('tags'); + const sortBy = searchParams.get('sortBy') || 'qualityScore'; + + // Build where clause + const where: { + collectionId?: string; + tags?: { hasSome: string[] }; + collection?: { isPublic: boolean }; + } = {}; + + if (collectionId) { + where.collectionId = collectionId; + } else { + // Only show scenarios from public collections when not filtering by collectionId + where.collection = { isPublic: true }; + } + + if (tagsParam) { + where.tags = { hasSome: tagsParam.split(',').map((t) => t.trim()) }; + } + + // Build orderBy + const orderByMap: Record = { + qualityScore: { qualityScore: 'desc' }, + totalRuns: { totalRuns: 'desc' }, + createdAt: { createdAt: 'desc' }, + lastRunAt: { lastRunAt: 'desc' }, + }; + const orderBy = orderByMap[sortBy] || orderByMap.qualityScore; + + // Fetch scenarios with pagination + const scenarios = await prisma.scenario.findMany({ + where, + orderBy, + take: limit + 1, + skip: offset, + include: { + collection: { + select: { + id: true, + name: true, + slug: true, + user: { + select: { + username: true, + }, + }, + }, + }, + _count: { + select: { runs: true }, + }, + }, + }); + + const hasMore = scenarios.length > limit; + const data = hasMore ? scenarios.slice(0, limit) : scenarios; + + return NextResponse.json({ + success: true, + data: data.map((s) => ({ + id: s.id, + collectionId: s.collectionId, + prompt: s.prompt, + name: s.name, + description: s.description, + tags: s.tags, + qualityScore: s.qualityScore, + totalRuns: s.totalRuns, + lastRunAt: s.lastRunAt, + lastRunStatus: s.lastRunStatus, + createdAt: s.createdAt, + collection: s.collection + ? { + id: s.collection.id, + name: s.collection.name, + slug: s.collection.slug, + username: s.collection.user.username, + } + : null, + runCount: s._count.runs, + })), + meta: { + version: API_VERSION, + timestamp: new Date().toISOString(), + requestId, + }, + pagination: { + limit, + offset, + hasMore, + }, + }); + } catch (error) { + console.error('[API Error] GET /api/scenarios:', error); + return NextResponse.json( + { + success: false, + error: { code: 'INTERNAL_ERROR', message: 'Failed to fetch scenarios' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 500 } + ); + } +} + +/** + * POST /api/scenarios + * Create a new scenario + * + * Requires authentication + * User must own the collection + */ +export async function POST(request: NextRequest): Promise> { + const requestId = crypto.randomUUID(); + + try { + // Check authentication + const authResult = await authenticateRequest(); + + if (!authResult.authenticated || !authResult.userId) { + return NextResponse.json( + { + success: false, + error: { code: 'UNAUTHORIZED', message: 'Authentication required' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 401 } + ); + } + + // Parse and validate request body + const body = await request.json(); + const parseResult = CreateScenarioSchema.safeParse(body); + + if (!parseResult.success) { + return NextResponse.json( + { + success: false, + error: { + code: 'VALIDATION_ERROR', + message: 'Invalid request body', + details: { errors: parseResult.error.flatten().fieldErrors }, + }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 400 } + ); + } + + const { collectionId, prompt, name, description, assertions, tags } = parseResult.data; + + // Verify collection exists and user owns it + const collection = await prisma.collection.findUnique({ + where: { id: collectionId }, + select: { id: true, userId: true }, + }); + + if (!collection) { + return NextResponse.json( + { + success: false, + error: { code: 'NOT_FOUND', message: 'Collection not found' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 404 } + ); + } + + if (collection.userId !== authResult.userId) { + return NextResponse.json( + { + success: false, + error: { code: 'FORBIDDEN', message: 'You do not own this collection' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 403 } + ); + } + + // Create the scenario + const scenario = await prisma.scenario.create({ + data: { + collectionId, + prompt, + name, + description, + assertions: assertions ? (assertions as object) : undefined, + tags: tags || [], + }, + include: { + collection: { + select: { + id: true, + name: true, + slug: true, + }, + }, + }, + }); + + return NextResponse.json( + { + success: true, + data: { + id: scenario.id, + collectionId: scenario.collectionId, + prompt: scenario.prompt, + name: scenario.name, + description: scenario.description, + assertions: scenario.assertions, + tags: scenario.tags, + qualityScore: scenario.qualityScore, + totalRuns: scenario.totalRuns, + createdAt: scenario.createdAt, + collection: scenario.collection + ? { + id: scenario.collection.id, + name: scenario.collection.name, + slug: scenario.collection.slug, + } + : null, + }, + meta: { + version: API_VERSION, + timestamp: new Date().toISOString(), + requestId, + }, + }, + { status: 201 } + ); + } catch (error) { + console.error('[API Error] POST /api/scenarios:', error); + return NextResponse.json( + { + success: false, + error: { code: 'INTERNAL_ERROR', message: 'Failed to create scenario' }, + meta: { version: API_VERSION, timestamp: new Date().toISOString(), requestId }, + }, + { status: 500 } + ); + } +} diff --git a/apps/web/src/lib/scenarios/evaluate.ts b/apps/web/src/lib/scenarios/evaluate.ts new file mode 100644 index 0000000..a2f74bb --- /dev/null +++ b/apps/web/src/lib/scenarios/evaluate.ts @@ -0,0 +1,151 @@ +/** + * Scenario Evaluation Service + * + * Uses LLM judgment to evaluate if a scenario execution was successful. + */ + +import { anthropic } from '@ai-sdk/anthropic'; +import { openai } from '@ai-sdk/openai'; +import { generateObject } from 'ai'; +import { z } from 'zod'; + +const EvaluationSchema = z.object({ + verdict: z.enum(['pass', 'fail']).describe('Whether the scenario was completed successfully'), + reason: z.string().describe('Brief explanation of why the scenario passed or failed'), + confidence: z.number().min(0).max(1).describe('Confidence level in the evaluation (0-1)'), +}); + +export type EvaluationResult = z.infer; + +/** + * Supported evaluator model IDs + */ +export type EvaluatorModelId = + | 'claude-3-5-sonnet-latest' + | 'claude-3-5-haiku-latest' + | 'gpt-4o' + | 'gpt-4o-mini'; + +/** + * Get the model instance for an evaluator model ID + */ +function getEvaluatorModel(modelId: EvaluatorModelId) { + switch (modelId) { + case 'claude-3-5-sonnet-latest': + return anthropic('claude-3-5-sonnet-latest'); + case 'claude-3-5-haiku-latest': + return anthropic('claude-3-5-haiku-latest'); + case 'gpt-4o': + return openai('gpt-4o'); + case 'gpt-4o-mini': + return openai('gpt-4o-mini'); + default: + return anthropic('claude-3-5-haiku-latest'); + } +} + +const DEFAULT_EVALUATOR: EvaluatorModelId = 'claude-3-5-haiku-latest'; + +/** + * Evaluate if a scenario execution was successful + * + * @param scenarioPrompt The original scenario prompt/task + * @param agentOutput The output produced by the agent + * @param conversation Optional conversation history for context + * @param modelId Which model to use for evaluation + */ +export async function evaluateScenarioRun( + scenarioPrompt: string, + agentOutput: string, + conversation?: unknown[], + modelId: EvaluatorModelId = DEFAULT_EVALUATOR +): Promise { + const model = getEvaluatorModel(modelId); + + const conversationContext = conversation + ? `\n\nConversation history:\n${JSON.stringify(conversation, null, 2)}` + : ''; + + const { object } = await generateObject({ + model, + schema: EvaluationSchema, + prompt: `You are evaluating whether an AI agent successfully completed a task. + +## Task +${scenarioPrompt} + +## Agent Output +${agentOutput} +${conversationContext} + +## Instructions +Evaluate if the agent successfully completed the task described above. +- A "pass" means the core objective was achieved, even if some minor aspects weren't perfect +- A "fail" means the agent failed to accomplish the main goal +- Consider partial success as a pass if the primary task was completed +- Be fair but rigorous in your evaluation + +Provide your verdict, a brief reason, and your confidence level.`, + }); + + return object; +} + +/** + * Run assertions against the output + * + * @param output The agent output to check + * @param assertions The assertions to run + */ +export function runAssertions( + output: string, + assertions: { regex?: string[]; schema?: Record } +): { passed: string[]; failed: string[] } { + const passed: string[] = []; + const failed: string[] = []; + + // Check regex assertions + if (assertions.regex) { + for (const pattern of assertions.regex) { + try { + const regex = new RegExp(pattern, 'i'); + if (regex.test(output)) { + passed.push(`regex:${pattern}`); + } else { + failed.push(`regex:${pattern}`); + } + } catch { + failed.push(`regex:${pattern} (invalid pattern)`); + } + } + } + + // Schema assertions would require more complex validation + // For now, we'll just note if schema was provided + if (assertions.schema) { + // TODO: Implement JSON schema validation against parsed output + passed.push('schema:provided (validation pending)'); + } + + return { passed, failed }; +} + +/** + * Combine LLM evaluation and assertions into final verdict + */ +export function determineFinalVerdict( + evaluation: EvaluationResult, + assertions?: { passed: string[]; failed: string[] } | null +): 'pass' | 'fail' { + // If LLM says fail, it fails + if (evaluation.verdict === 'fail') { + return 'fail'; + } + + // If there are failed assertions, it fails + if (assertions && assertions.failed.length > 0) { + return 'fail'; + } + + return 'pass'; +} diff --git a/apps/web/src/lib/scenarios/execute.ts b/apps/web/src/lib/scenarios/execute.ts new file mode 100644 index 0000000..a9524ee --- /dev/null +++ b/apps/web/src/lib/scenarios/execute.ts @@ -0,0 +1,311 @@ +/** + * Scenario Execution Service + * + * Orchestrates scenario execution using ephemeral agents. + * Currently implements a simulated execution - full agent integration coming in Phase 3. + */ + +import type { Scenario, ScenarioRun } from '@prisma/client'; +import { prisma } from '@tpmjs/db'; +import { + determineFinalVerdict, + type EvaluatorModelId, + evaluateScenarioRun, + runAssertions, +} from './evaluate'; + +const DEFAULT_EVALUATOR: EvaluatorModelId = 'claude-3-5-haiku-latest'; +const MAX_RETRIES = 1; + +interface ExecutionOptions { + evaluatorModel?: EvaluatorModelId; +} + +interface ExecutionResult { + run: ScenarioRun; + success: boolean; +} + +/** + * Check and decrement user's daily quota + */ +export async function checkAndDecrementQuota( + userId: string +): Promise<{ allowed: boolean; remaining: number }> { + const now = new Date(); + const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + + // Get or create quota record + let quota = await prisma.scenarioQuota.findUnique({ + where: { userId }, + }); + + if (!quota) { + quota = await prisma.scenarioQuota.create({ + data: { + userId, + dailyLimit: 50, + dailyUsed: 0, + lastResetAt: startOfDay, + }, + }); + } + + // Reset quota if it's a new day + if (quota.lastResetAt < startOfDay) { + quota = await prisma.scenarioQuota.update({ + where: { userId }, + data: { + dailyUsed: 0, + lastResetAt: startOfDay, + }, + }); + } + + // Check if quota allows another run + if (quota.dailyUsed >= quota.dailyLimit) { + return { allowed: false, remaining: 0 }; + } + + // Decrement quota + await prisma.scenarioQuota.update({ + where: { userId }, + data: { + dailyUsed: { increment: 1 }, + }, + }); + + return { allowed: true, remaining: quota.dailyLimit - quota.dailyUsed - 1 }; +} + +/** + * Update scenario metrics based on run result + */ +export async function updateScenarioMetrics( + scenarioId: string, + status: 'pass' | 'fail' | 'error' +): Promise { + const scenario = await prisma.scenario.findUnique({ + where: { id: scenarioId }, + }); + + if (!scenario) return; + + let { consecutivePasses, consecutiveFails, qualityScore, totalRuns } = scenario; + + if (status === 'pass') { + consecutivePasses += 1; + consecutiveFails = 0; + // Bonus for streaks: +0.05 per pass + (streak bonus), max 1.0 + qualityScore = Math.min(1.0, qualityScore + 0.05 + consecutivePasses * 0.01); + } else { + consecutiveFails += 1; + consecutivePasses = 0; + // Penalty for fails: -0.1 per fail + (streak penalty), min 0 + qualityScore = Math.max(0, qualityScore - 0.1 - consecutiveFails * 0.02); + } + + await prisma.scenario.update({ + where: { id: scenarioId }, + data: { + consecutivePasses, + consecutiveFails, + qualityScore, + totalRuns: totalRuns + 1, + lastRunAt: new Date(), + lastRunStatus: status, + }, + }); +} + +/** + * Execute a scenario + * + * NOTE: This currently uses simulated execution. + * Full agent integration will be added in Phase 3. + * + * @param scenario The scenario to execute + * @param userId The user triggering the execution + * @param options Execution options + */ +export async function executeScenario( + scenario: Scenario, + userId: string, + options: ExecutionOptions = {} +): Promise { + const { evaluatorModel = DEFAULT_EVALUATOR } = options; + + // Create run record + const run = await prisma.scenarioRun.create({ + data: { + scenarioId: scenario.id, + userId, + status: 'pending', + }, + }); + + let lastError: Error | null = null; + let retryCount = 0; + + // Retry loop + for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { + retryCount = attempt; + + try { + // Update status to running + await prisma.scenarioRun.update({ + where: { id: run.id }, + data: { + status: 'running', + startedAt: new Date(), + retryCount: attempt, + }, + }); + + // TODO: Phase 3 - Replace with actual agent execution + // For now, we simulate execution + const executionResult = await simulateExecution(scenario); + + // Evaluate with LLM + const evaluation = await evaluateScenarioRun( + scenario.prompt, + executionResult.output, + executionResult.conversation, + evaluatorModel + ); + + // Run assertions if defined + const assertions = scenario.assertions as { + regex?: string[]; + schema?: Record; + } | null; + const assertionResults = assertions + ? runAssertions(executionResult.output, assertions) + : null; + + // Determine final verdict + const finalStatus = determineFinalVerdict(evaluation, assertionResults); + + // Update run record + const updatedRun = await prisma.scenarioRun.update({ + where: { id: run.id }, + data: { + status: finalStatus, + conversation: executionResult.conversation as object, + output: executionResult.output, + evaluatorModel, + evaluatorVerdict: evaluation.verdict, + evaluatorReason: evaluation.reason, + assertionResults: assertionResults as object, + inputTokens: executionResult.usage.inputTokens, + outputTokens: executionResult.usage.outputTokens, + totalTokens: executionResult.usage.totalTokens, + executionTimeMs: executionResult.durationMs, + completedAt: new Date(), + }, + }); + + // Update scenario metrics + await updateScenarioMetrics(scenario.id, finalStatus); + + return { run: updatedRun, success: finalStatus === 'pass' }; + } catch (error) { + lastError = error as Error; + + // If this is the last attempt, mark as error + if (attempt === MAX_RETRIES) { + const errorRun = await prisma.scenarioRun.update({ + where: { id: run.id }, + data: { + status: 'error', + retryCount, + errorLog: (error as Error).stack || (error as Error).message, + completedAt: new Date(), + }, + }); + + await updateScenarioMetrics(scenario.id, 'error'); + + return { run: errorRun, success: false }; + } + } + } + + // Should never reach here, but TypeScript needs this + throw lastError || new Error('Unknown execution error'); +} + +/** + * Simulated execution for development/testing + * + * This will be replaced with actual agent execution in Phase 3. + */ +async function simulateExecution(scenario: Scenario): Promise<{ + output: string; + conversation: unknown[]; + usage: { + inputTokens: number; + outputTokens: number; + totalTokens: number; + }; + durationMs: number; +}> { + // Simulate some processing time + const durationMs = Math.floor(Math.random() * 3000) + 1000; + await new Promise((resolve) => setTimeout(resolve, 100)); // Small delay + + // Generate simulated output based on the prompt + const passRate = 0.7; // 70% pass rate for simulated runs + const willPass = Math.random() < passRate; + + const output = willPass + ? `[SIMULATED] Successfully completed task: ${scenario.prompt.slice(0, 100)}...\n\nThe scenario was executed successfully. All requested operations were performed.` + : `[SIMULATED] Failed to complete task: ${scenario.prompt.slice(0, 100)}...\n\nEncountered an error during execution. The requested operation could not be completed.`; + + const conversation = [ + { role: 'user', content: scenario.prompt }, + { role: 'assistant', content: output }, + ]; + + const inputTokens = Math.floor(scenario.prompt.length / 4); + const outputTokens = Math.floor(output.length / 4); + + return { + output, + conversation, + usage: { + inputTokens, + outputTokens, + totalTokens: inputTokens + outputTokens, + }, + durationMs, + }; +} + +/** + * Get user's current quota status + */ +export async function getQuotaStatus( + userId: string +): Promise<{ used: number; limit: number; remaining: number; resetsAt: Date }> { + const now = new Date(); + const tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1); + + const quota = await prisma.scenarioQuota.findUnique({ + where: { userId }, + }); + + if (!quota) { + return { used: 0, limit: 50, remaining: 50, resetsAt: tomorrow }; + } + + const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const used = quota.lastResetAt < startOfDay ? 0 : quota.dailyUsed; + + return { + used, + limit: quota.dailyLimit, + remaining: quota.dailyLimit - used, + resetsAt: tomorrow, + }; +} diff --git a/apps/web/src/lib/scenarios/generate-prompt.ts b/apps/web/src/lib/scenarios/generate-prompt.ts new file mode 100644 index 0000000..a3a3f0f --- /dev/null +++ b/apps/web/src/lib/scenarios/generate-prompt.ts @@ -0,0 +1,143 @@ +/** + * AI Prompt Generation Service + * + * Uses GPT-4o-mini to generate realistic test scenario prompts + * for collections based on their tools. + */ + +import { openai } from '@ai-sdk/openai'; +import { generateObject, generateText } from 'ai'; +import { z } from 'zod'; + +const GENERATOR_MODEL = 'gpt-4o-mini'; + +interface Tool { + name: string; + description: string | null; +} + +interface Collection { + name: string; + description: string | null; + tools: Tool[]; +} + +/** + * Generate a single scenario prompt for a collection + */ +export async function generateScenarioPrompt(collection: Collection): Promise { + const toolDescriptions = collection.tools + .map((t) => `- ${t.name}: ${t.description || 'No description'}`) + .join('\n'); + + const { text } = await generateText({ + model: openai(GENERATOR_MODEL), + prompt: `Generate a realistic test scenario for this tool collection. + +Collection: ${collection.name} +Description: ${collection.description || 'No description provided'} + +Available tools: +${toolDescriptions} + +Write a single, specific task that a user might want to accomplish using these tools. +Be concrete and include example data where helpful (like URLs, file paths, or specific values). +The task should be achievable using the available tools. +Output only the scenario prompt, nothing else.`, + }); + + return text.trim(); +} + +/** + * Generate multiple scenario prompts with metadata + */ +export interface GeneratedScenario { + prompt: string; + name: string; + tags: string[]; +} + +const GeneratedScenarioSchema = z.object({ + prompt: z.string().describe('The specific task prompt'), + name: z.string().describe('A short descriptive name for the scenario (max 50 chars)'), + tags: z.array(z.string()).max(5).describe('Relevant tags for categorization'), +}); + +const GeneratedScenariosSchema = z.object({ + scenarios: z.array(GeneratedScenarioSchema), +}); + +/** + * Generate multiple scenarios with names and tags + */ +export async function generateScenarios( + collection: Collection, + count: number = 1 +): Promise { + const toolDescriptions = collection.tools + .map((t) => `- ${t.name}: ${t.description || 'No description'}`) + .join('\n'); + + const { object } = await generateObject({ + model: openai(GENERATOR_MODEL), + schema: GeneratedScenariosSchema, + prompt: `Generate ${count} realistic test scenario${count > 1 ? 's' : ''} for this tool collection. + +Collection: ${collection.name} +Description: ${collection.description || 'No description provided'} + +Available tools: +${toolDescriptions} + +For each scenario: +1. Write a specific, achievable task using the available tools +2. Include concrete example data (URLs, file paths, values) where helpful +3. Provide a short descriptive name (max 50 characters) +4. Add 1-5 relevant tags for categorization + +${count > 1 ? 'Make the scenarios diverse - cover different use cases and tool combinations.' : ''}`, + }); + + return object.scenarios; +} + +/** + * Generate tags for an existing prompt + */ +export async function generateTags(prompt: string): Promise { + const TagsSchema = z.object({ + tags: z.array(z.string()).min(1).max(5).describe('Relevant tags for the scenario'), + }); + + const { object } = await generateObject({ + model: openai(GENERATOR_MODEL), + schema: TagsSchema, + prompt: `Generate 1-5 relevant tags for this scenario prompt: + +"${prompt}" + +Tags should be: +- Lowercase +- Single words or short phrases (max 2 words) +- Descriptive of the task type, domain, or tools involved`, + }); + + return object.tags; +} + +/** + * Generate a name for an existing prompt + */ +export async function generateName(prompt: string): Promise { + const { text } = await generateText({ + model: openai(GENERATOR_MODEL), + prompt: `Generate a short, descriptive name (max 50 characters) for this scenario: + +"${prompt}" + +Output only the name, nothing else.`, + }); + + return text.trim().slice(0, 50); +} diff --git a/apps/web/src/lib/scenarios/similarity.ts b/apps/web/src/lib/scenarios/similarity.ts new file mode 100644 index 0000000..12fae5f --- /dev/null +++ b/apps/web/src/lib/scenarios/similarity.ts @@ -0,0 +1,170 @@ +/** + * Scenario Similarity Service + * + * Uses OpenAI embeddings to detect similar scenarios. + * Embeddings are stored in PostgreSQL JSONB for persistence. + */ + +import { openai } from '@ai-sdk/openai'; +import type { Scenario, ScenarioEmbedding } from '@prisma/client'; +import { prisma } from '@tpmjs/db'; +import { embed } from 'ai'; + +const EMBEDDING_MODEL = 'text-embedding-3-small'; +const SIMILARITY_THRESHOLD = 0.7; // 70% similarity triggers warning + +/** + * Compute embedding for a text string + */ +export async function computeEmbedding(text: string): Promise { + const { embedding } = await embed({ + model: openai.embedding(EMBEDDING_MODEL), + value: text, + }); + return embedding; +} + +/** + * Compute cosine similarity between two vectors + */ +export function cosineSimilarity(a: number[], b: number[]): number { + if (a.length !== b.length) { + throw new Error('Vectors must have same length'); + } + + let dotProduct = 0; + let normA = 0; + let normB = 0; + + for (let i = 0; i < a.length; i++) { + const aVal = a[i] ?? 0; + const bVal = b[i] ?? 0; + dotProduct += aVal * bVal; + normA += aVal * aVal; + normB += bVal * bVal; + } + + const denominator = Math.sqrt(normA) * Math.sqrt(normB); + if (denominator === 0) return 0; + + return dotProduct / denominator; +} + +export interface SimilarScenario { + scenario: Scenario; + similarity: number; +} + +/** + * Find scenarios similar to a given prompt + * + * @param prompt The prompt to check for similarity + * @param collectionId The collection to search within + * @param threshold Minimum similarity score (0-1), default 0.7 + * @param excludeScenarioId Optional scenario ID to exclude (for updates) + */ +export async function findSimilarScenarios( + prompt: string, + collectionId: string, + threshold: number = SIMILARITY_THRESHOLD, + excludeScenarioId?: string +): Promise { + // Compute embedding for the new prompt + const newEmbedding = await computeEmbedding(prompt); + + // Get all scenarios with embeddings for this collection + const scenarios = await prisma.scenario.findMany({ + where: { + collectionId, + ...(excludeScenarioId && { id: { not: excludeScenarioId } }), + embedding: { isNot: null }, + }, + include: { + embedding: true, + }, + }); + + // Calculate similarity scores + const similar: SimilarScenario[] = []; + + for (const scenario of scenarios) { + if (!scenario.embedding) continue; + + const existingEmbedding = scenario.embedding.embedding as number[]; + const similarity = cosineSimilarity(newEmbedding, existingEmbedding); + + if (similarity >= threshold) { + // Remove embedding from returned scenario to keep response light + const { embedding: _, ...scenarioWithoutEmbedding } = scenario; + similar.push({ + scenario: scenarioWithoutEmbedding as Scenario, + similarity, + }); + } + } + + // Sort by similarity descending + return similar.sort((a, b) => b.similarity - a.similarity); +} + +/** + * Store embedding for a scenario + */ +export async function storeScenarioEmbedding( + scenarioId: string, + embedding: number[] +): Promise { + // Upsert to handle both create and update cases + return prisma.scenarioEmbedding.upsert({ + where: { scenarioId }, + update: { + embedding: embedding as unknown as object, + model: EMBEDDING_MODEL, + }, + create: { + scenarioId, + embedding: embedding as unknown as object, + model: EMBEDDING_MODEL, + }, + }); +} + +/** + * Generate and store embedding for a scenario + */ +export async function generateAndStoreEmbedding( + scenarioId: string, + prompt: string +): Promise { + const embedding = await computeEmbedding(prompt); + return storeScenarioEmbedding(scenarioId, embedding); +} + +/** + * Check similarity and return warning if similar scenarios exist + */ +export interface SimilarityCheckResult { + hasSimilar: boolean; + maxSimilarity: number; + similarScenarios: SimilarScenario[]; +} + +export async function checkSimilarity( + prompt: string, + collectionId: string, + excludeScenarioId?: string +): Promise { + const similar = await findSimilarScenarios( + prompt, + collectionId, + SIMILARITY_THRESHOLD, + excludeScenarioId + ); + + const firstSimilar = similar[0]; + return { + hasSimilar: similar.length > 0, + maxSimilarity: firstSimilar?.similarity ?? 0, + similarScenarios: similar.slice(0, 5), // Return top 5 similar + }; +} diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index bdd2659..36ea4d4 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -4,9 +4,7 @@ "aliases": [], "args": {}, "description": "Run diagnostic checks for TPMJS CLI", - "examples": [ - "<%= config.bin %> <%= command.id %>" - ], + "examples": ["<%= config.bin %> <%= command.id %>"], "flags": { "json": { "description": "Output in JSON format", @@ -31,11 +29,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "doctor.js" - ] + "relativePath": ["dist", "commands", "doctor.js"] }, "playground": { "aliases": [], @@ -79,19 +73,13 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "playground.js" - ] + "relativePath": ["dist", "commands", "playground.js"] }, "update": { "aliases": [], "args": {}, "description": "Update the TPMJS CLI to the latest version", - "examples": [ - "<%= config.bin %> <%= command.id %>" - ], + "examples": ["<%= config.bin %> <%= command.id %>"], "flags": { "json": { "description": "Output in JSON format", @@ -122,11 +110,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "update.js" - ] + "relativePath": ["dist", "commands", "update.js"] }, "agent:chat": { "aliases": [], @@ -186,12 +170,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "agent", - "chat.js" - ] + "relativePath": ["dist", "commands", "agent", "chat.js"] }, "agent:create": { "aliases": [], @@ -233,13 +212,7 @@ "required": true, "hasDynamicHelp": false, "multiple": false, - "options": [ - "ANTHROPIC", - "OPENAI", - "GOOGLE", - "GROQ", - "MISTRAL" - ], + "options": ["ANTHROPIC", "OPENAI", "GOOGLE", "GROQ", "MISTRAL"], "type": "option" }, "model": { @@ -297,12 +270,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "agent", - "create.js" - ] + "relativePath": ["dist", "commands", "agent", "create.js"] }, "agent:delete": { "aliases": [], @@ -349,12 +317,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "agent", - "delete.js" - ] + "relativePath": ["dist", "commands", "agent", "delete.js"] }, "agent:list": { "aliases": [], @@ -406,12 +369,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "agent", - "list.js" - ] + "relativePath": ["dist", "commands", "agent", "list.js"] }, "agent:update": { "aliases": [], @@ -457,13 +415,7 @@ "name": "provider", "hasDynamicHelp": false, "multiple": false, - "options": [ - "ANTHROPIC", - "OPENAI", - "GOOGLE", - "GROQ", - "MISTRAL" - ], + "options": ["ANTHROPIC", "OPENAI", "GOOGLE", "GROQ", "MISTRAL"], "type": "option" }, "model": { @@ -519,12 +471,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "agent", - "update.js" - ] + "relativePath": ["dist", "commands", "agent", "update.js"] }, "auth:login": { "aliases": [], @@ -579,20 +526,13 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "auth", - "login.js" - ] + "relativePath": ["dist", "commands", "auth", "login.js"] }, "auth:logout": { "aliases": [], "args": {}, "description": "Log out from TPMJS", - "examples": [ - "<%= config.bin %> <%= command.id %>" - ], + "examples": ["<%= config.bin %> <%= command.id %>"], "flags": { "json": { "description": "Output in JSON format", @@ -610,20 +550,13 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "auth", - "logout.js" - ] + "relativePath": ["dist", "commands", "auth", "logout.js"] }, "auth:status": { "aliases": [], "args": {}, "description": "Show authentication status", - "examples": [ - "<%= config.bin %> <%= command.id %>" - ], + "examples": ["<%= config.bin %> <%= command.id %>"], "flags": { "json": { "description": "Output in JSON format", @@ -648,20 +581,13 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "auth", - "status.js" - ] + "relativePath": ["dist", "commands", "auth", "status.js"] }, "auth:whoami": { "aliases": [], "args": {}, "description": "Show current user information", - "examples": [ - "<%= config.bin %> <%= command.id %>" - ], + "examples": ["<%= config.bin %> <%= command.id %>"], "flags": { "json": { "description": "Output in JSON format", @@ -686,12 +612,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "auth", - "whoami.js" - ] + "relativePath": ["dist", "commands", "auth", "whoami.js"] }, "collection:add": { "aliases": [], @@ -731,12 +652,7 @@ "strict": false, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "collection", - "add.js" - ] + "relativePath": ["dist", "commands", "collection", "add.js"] }, "collection:create": { "aliases": [], @@ -793,12 +709,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "collection", - "create.js" - ] + "relativePath": ["dist", "commands", "collection", "create.js"] }, "collection:delete": { "aliases": [], @@ -845,12 +756,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "collection", - "delete.js" - ] + "relativePath": ["dist", "commands", "collection", "delete.js"] }, "collection:import": { "aliases": [], @@ -899,12 +805,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "collection", - "import.js" - ] + "relativePath": ["dist", "commands", "collection", "import.js"] }, "collection:list": { "aliases": [], @@ -956,12 +857,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "collection", - "list.js" - ] + "relativePath": ["dist", "commands", "collection", "list.js"] }, "collection:remove": { "aliases": [], @@ -978,9 +874,7 @@ } }, "description": "Remove a tool from a collection", - "examples": [ - "<%= config.bin %> <%= command.id %> my-collection tool-id-1" - ], + "examples": ["<%= config.bin %> <%= command.id %> my-collection tool-id-1"], "flags": { "json": { "description": "Output in JSON format", @@ -1005,12 +899,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "collection", - "remove.js" - ] + "relativePath": ["dist", "commands", "collection", "remove.js"] }, "collection:update": { "aliases": [], @@ -1072,12 +961,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "collection", - "update.js" - ] + "relativePath": ["dist", "commands", "collection", "update.js"] }, "mcp:config": { "aliases": [], @@ -1102,12 +986,7 @@ "default": "claude", "hasDynamicHelp": false, "multiple": false, - "options": [ - "claude", - "cursor", - "windsurf", - "generic" - ], + "options": ["claude", "cursor", "windsurf", "generic"], "type": "option" }, "output": { @@ -1142,12 +1021,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "mcp", - "config.js" - ] + "relativePath": ["dist", "commands", "mcp", "config.js"] }, "mcp:serve": { "aliases": [], @@ -1208,12 +1082,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "mcp", - "serve.js" - ] + "relativePath": ["dist", "commands", "mcp", "serve.js"] }, "publish:check": { "aliases": [], @@ -1253,12 +1122,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "publish", - "check.js" - ] + "relativePath": ["dist", "commands", "publish", "check.js"] }, "publish:preview": { "aliases": [], @@ -1301,12 +1165,271 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "publish", - "preview.js" - ] + "relativePath": ["dist", "commands", "publish", "preview.js"] + }, + "scenario:generate": { + "aliases": [], + "args": { + "collection": { + "description": "Collection ID or slug", + "name": "collection", + "required": true + } + }, + "description": "Generate AI-powered scenarios for a collection", + "examples": [ + "<%= config.bin %> <%= command.id %> my-collection", + "<%= config.bin %> <%= command.id %> my-collection --count 3", + "<%= config.bin %> <%= command.id %> my-collection --skip-similarity-check" + ], + "flags": { + "count": { + "char": "n", + "description": "Number of scenarios to generate (1-10)", + "name": "count", + "default": 1, + "hasDynamicHelp": false, + "multiple": false, + "type": "option" + }, + "skip-similarity-check": { + "description": "Skip checking for similar existing scenarios", + "name": "skip-similarity-check", + "allowNo": false, + "type": "boolean" + }, + "json": { + "description": "Output in JSON format", + "name": "json", + "allowNo": false, + "type": "boolean" + }, + "verbose": { + "char": "v", + "description": "Show verbose output", + "name": "verbose", + "allowNo": false, + "type": "boolean" + } + }, + "hasDynamicHelp": false, + "hiddenAliases": [], + "id": "scenario:generate", + "pluginAlias": "@tpmjs/cli", + "pluginName": "@tpmjs/cli", + "pluginType": "core", + "strict": true, + "enableJsonFlag": false, + "isESM": true, + "relativePath": ["dist", "commands", "scenario", "generate.js"] + }, + "scenario:info": { + "aliases": [], + "args": { + "scenarioId": { + "description": "Scenario ID", + "name": "scenarioId", + "required": true + } + }, + "description": "Show detailed information about a scenario", + "examples": [ + "<%= config.bin %> <%= command.id %> clu123abc456", + "<%= config.bin %> <%= command.id %> clu123abc456 --runs 20", + "<%= config.bin %> <%= command.id %> clu123abc456 --json" + ], + "flags": { + "runs": { + "char": "r", + "description": "Number of recent runs to show", + "name": "runs", + "default": 10, + "hasDynamicHelp": false, + "multiple": false, + "type": "option" + }, + "json": { + "description": "Output in JSON format", + "name": "json", + "allowNo": false, + "type": "boolean" + }, + "verbose": { + "char": "v", + "description": "Show verbose output", + "name": "verbose", + "allowNo": false, + "type": "boolean" + } + }, + "hasDynamicHelp": false, + "hiddenAliases": [], + "id": "scenario:info", + "pluginAlias": "@tpmjs/cli", + "pluginName": "@tpmjs/cli", + "pluginType": "core", + "strict": true, + "enableJsonFlag": false, + "isESM": true, + "relativePath": ["dist", "commands", "scenario", "info.js"] + }, + "scenario:list": { + "aliases": [], + "args": { + "collection": { + "description": "Collection ID or slug (optional - shows all public scenarios if omitted)", + "name": "collection", + "required": false + } + }, + "description": "List scenarios for a collection or all public scenarios", + "examples": [ + "<%= config.bin %> <%= command.id %>", + "<%= config.bin %> <%= command.id %> my-collection", + "<%= config.bin %> <%= command.id %> --limit 20 --json" + ], + "flags": { + "limit": { + "char": "l", + "description": "Maximum number of results", + "name": "limit", + "default": 20, + "hasDynamicHelp": false, + "multiple": false, + "type": "option" + }, + "offset": { + "char": "o", + "description": "Offset for pagination", + "name": "offset", + "default": 0, + "hasDynamicHelp": false, + "multiple": false, + "type": "option" + }, + "tags": { + "char": "t", + "description": "Filter by tags (comma-separated)", + "name": "tags", + "hasDynamicHelp": false, + "multiple": false, + "type": "option" + }, + "json": { + "description": "Output in JSON format", + "name": "json", + "allowNo": false, + "type": "boolean" + }, + "verbose": { + "char": "v", + "description": "Show verbose output", + "name": "verbose", + "allowNo": false, + "type": "boolean" + } + }, + "hasDynamicHelp": false, + "hiddenAliases": [], + "id": "scenario:list", + "pluginAlias": "@tpmjs/cli", + "pluginName": "@tpmjs/cli", + "pluginType": "core", + "strict": true, + "enableJsonFlag": false, + "isESM": true, + "relativePath": ["dist", "commands", "scenario", "list.js"] + }, + "scenario:run": { + "aliases": [], + "args": { + "collection": { + "description": "Collection ID or slug", + "name": "collection", + "required": true + } + }, + "description": "Run all scenarios for a collection", + "examples": [ + "<%= config.bin %> <%= command.id %> my-collection", + "<%= config.bin %> <%= command.id %> my-collection --json", + "<%= config.bin %> <%= command.id %> my-collection --verbose" + ], + "flags": { + "json": { + "description": "Output in JSON format", + "name": "json", + "allowNo": false, + "type": "boolean" + }, + "verbose": { + "char": "v", + "description": "Show verbose output", + "name": "verbose", + "allowNo": false, + "type": "boolean" + }, + "limit": { + "char": "l", + "description": "Maximum number of scenarios to run", + "name": "limit", + "default": 50, + "hasDynamicHelp": false, + "multiple": false, + "type": "option" + } + }, + "hasDynamicHelp": false, + "hiddenAliases": [], + "id": "scenario:run", + "pluginAlias": "@tpmjs/cli", + "pluginName": "@tpmjs/cli", + "pluginType": "core", + "strict": true, + "enableJsonFlag": false, + "isESM": true, + "relativePath": ["dist", "commands", "scenario", "run.js"] + }, + "scenario:test": { + "aliases": [], + "args": { + "scenarioId": { + "description": "Scenario ID to run", + "name": "scenarioId", + "required": true + } + }, + "description": "Run a single scenario by ID", + "examples": [ + "<%= config.bin %> <%= command.id %> clu123abc456", + "<%= config.bin %> <%= command.id %> clu123abc456 --json", + "<%= config.bin %> <%= command.id %> clu123abc456 --verbose" + ], + "flags": { + "json": { + "description": "Output in JSON format", + "name": "json", + "allowNo": false, + "type": "boolean" + }, + "verbose": { + "char": "v", + "description": "Show verbose output including full reason", + "name": "verbose", + "allowNo": false, + "type": "boolean" + } + }, + "hasDynamicHelp": false, + "hiddenAliases": [], + "id": "scenario:test", + "pluginAlias": "@tpmjs/cli", + "pluginName": "@tpmjs/cli", + "pluginType": "core", + "strict": true, + "enableJsonFlag": false, + "isESM": true, + "relativePath": ["dist", "commands", "scenario", "test.js"] }, "tool:execute": { "aliases": [], @@ -1379,12 +1502,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "tool", - "execute.js" - ] + "relativePath": ["dist", "commands", "tool", "execute.js"] }, "tool:info": { "aliases": [], @@ -1429,12 +1547,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "tool", - "info.js" - ] + "relativePath": ["dist", "commands", "tool", "info.js"] }, "tool:init": { "aliases": [], @@ -1459,10 +1572,7 @@ "default": "minimal", "hasDynamicHelp": false, "multiple": false, - "options": [ - "minimal", - "rich" - ], + "options": ["minimal", "rich"], "type": "option" }, "category": { @@ -1519,12 +1629,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "tool", - "init.js" - ] + "relativePath": ["dist", "commands", "tool", "init.js"] }, "tool:search": { "aliases": [], @@ -1591,12 +1696,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "tool", - "search.js" - ] + "relativePath": ["dist", "commands", "tool", "search.js"] }, "tool:trending": { "aliases": [], @@ -1639,12 +1739,7 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "tool", - "trending.js" - ] + "relativePath": ["dist", "commands", "tool", "trending.js"] }, "tool:validate": { "aliases": [], @@ -1687,13 +1782,8 @@ "strict": true, "enableJsonFlag": false, "isESM": true, - "relativePath": [ - "dist", - "commands", - "tool", - "validate.js" - ] + "relativePath": ["dist", "commands", "tool", "validate.js"] } }, - "version": "0.1.2" -} \ No newline at end of file + "version": "0.1.3" +} diff --git a/packages/cli/package.json b/packages/cli/package.json index 17338a6..4ebee34 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/cli", - "version": "0.1.2", + "version": "0.1.3", "description": "TPMJS command-line interface for AI tool discovery and execution", "author": "TPMJS", "license": "MIT", @@ -55,6 +55,9 @@ "collection": { "description": "Collection management" }, + "scenario": { + "description": "Test scenario management and execution" + }, "auth": { "description": "Authentication" }, diff --git a/packages/cli/src/commands/auth/login.ts b/packages/cli/src/commands/auth/login.ts index 3cc490f..e1e8f0a 100644 --- a/packages/cli/src/commands/auth/login.ts +++ b/packages/cli/src/commands/auth/login.ts @@ -1,9 +1,9 @@ -import { Args, Command, Flags } from '@oclif/core'; -import open from 'open'; import { createServer } from 'node:http'; import { URL } from 'node:url'; -import { saveCredentials, getApiUrl } from '../../lib/config.js'; +import { Args, Command, Flags } from '@oclif/core'; +import open from 'open'; import { TpmClient } from '../../lib/api-client.js'; +import { getApiUrl, saveCredentials } from '../../lib/config.js'; import { createOutput } from '../../lib/output.js'; export default class Login extends Command { @@ -62,7 +62,9 @@ export default class Login extends Command { output.listItem('tpm auth login --api-key '); output.listItem('tpm auth login --browser (opens browser for OAuth)'); output.newLine(); - output.text(`Get your API key at: ${output.link('tpmjs.com/dashboard/settings/tpmjs-api-keys', 'https://tpmjs.com/dashboard/settings/tpmjs-api-keys')}`); + output.text( + `Get your API key at: ${output.link('tpmjs.com/dashboard/settings/tpmjs-api-keys', 'https://tpmjs.com/dashboard/settings/tpmjs-api-keys')}` + ); } } @@ -122,6 +124,13 @@ export default class Login extends Command { output.info('Opening browser for authentication...'); return new Promise((resolve) => { + let timeoutId: NodeJS.Timeout; + + const cleanup = () => { + clearTimeout(timeoutId); + server.close(); + }; + const server = createServer(async (req, res) => { const url = new URL(req.url ?? '/', `http://localhost:${port}`); @@ -132,8 +141,10 @@ export default class Login extends Command { if (error) { res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end('

Authentication Failed

You can close this window.

'); - server.close(); + res.end( + '

Authentication Failed

You can close this window.

' + ); + cleanup(); output.error(`Authentication failed: ${error}`); resolve(); return; @@ -141,8 +152,10 @@ export default class Login extends Command { if (receivedState !== state) { res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end('

Invalid State

Authentication failed due to invalid state.

'); - server.close(); + res.end( + '

Invalid State

Authentication failed due to invalid state.

' + ); + cleanup(); output.error('Authentication failed: Invalid state parameter'); resolve(); return; @@ -152,8 +165,10 @@ export default class Login extends Command { saveCredentials({ apiKey }); res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end('

Success!

You are now logged in. You can close this window.

'); - server.close(); + res.end( + '

Success!

You are now logged in. You can close this window.

' + ); + cleanup(); output.success('Logged in successfully via browser'); @@ -165,7 +180,7 @@ export default class Login extends Command { } else { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end('

Error

No API key received.

'); - server.close(); + cleanup(); output.error('No API key received from authentication'); resolve(); } @@ -190,11 +205,14 @@ export default class Login extends Command { }); // Timeout after 5 minutes - setTimeout(() => { - server.close(); - output.error('Authentication timed out'); - resolve(); - }, 5 * 60 * 1000); + timeoutId = setTimeout( + () => { + server.close(); + output.error('Authentication timed out'); + resolve(); + }, + 5 * 60 * 1000 + ); }); } } diff --git a/packages/cli/src/commands/scenario/generate.ts b/packages/cli/src/commands/scenario/generate.ts new file mode 100644 index 0000000..d996ba0 --- /dev/null +++ b/packages/cli/src/commands/scenario/generate.ts @@ -0,0 +1,164 @@ +import { Args, Command, Flags } from '@oclif/core'; +import { getClient } from '../../lib/api-client.js'; +import { createOutput } from '../../lib/output.js'; + +export default class ScenarioGenerate extends Command { + static description = 'Generate AI-powered scenarios for a collection'; + + static examples = [ + '<%= config.bin %> <%= command.id %> my-collection', + '<%= config.bin %> <%= command.id %> my-collection --count 3', + '<%= config.bin %> <%= command.id %> my-collection --skip-similarity-check', + ]; + + static args = { + collection: Args.string({ + description: 'Collection ID or slug', + required: true, + }), + }; + + static flags = { + count: Flags.integer({ + char: 'n', + description: 'Number of scenarios to generate (1-10)', + default: 1, + min: 1, + max: 10, + }), + 'skip-similarity-check': Flags.boolean({ + description: 'Skip checking for similar existing scenarios', + default: false, + }), + json: Flags.boolean({ + description: 'Output in JSON format', + default: false, + }), + verbose: Flags.boolean({ + char: 'v', + description: 'Show verbose output', + default: false, + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(ScenarioGenerate); + const output = createOutput(flags); + const client = getClient(); + + if (!client.isAuthenticated()) { + output.error('Not authenticated. Run `tpm auth login` first.'); + return; + } + + // Find collection + const collectionsSpinner = output.spinner('Finding collection...'); + let collectionId: string; + let collectionName: string; + + try { + const collections = await client.listCollections({ limit: 100 }); + const collection = collections.data.find( + (c) => c.id === args.collection || c.slug === args.collection + ); + + if (!collection) { + collectionsSpinner.fail('Collection not found'); + output.error(`No collection found with ID or slug: ${args.collection}`); + return; + } + + collectionId = collection.id; + collectionName = collection.name; + collectionsSpinner.stop(); + } catch (error) { + collectionsSpinner.fail('Failed to find collection'); + output.error(error instanceof Error ? error.message : 'Unknown error'); + return; + } + + // Generate scenarios + const generateSpinner = output.spinner( + `Generating ${flags.count} scenario${flags.count > 1 ? 's' : ''} for "${collectionName}"...` + ); + + try { + const result = await client.generateScenarios(collectionId, { + count: flags.count, + skipSimilarityCheck: flags['skip-similarity-check'], + }); + + const scenarios = + ( + result as unknown as { + data: { + scenarios: Array<{ + scenario: { + id: string; + name: string; + prompt: string; + tags: string[]; + }; + similarity?: { + hasSimilar: boolean; + maxSimilarity: number; + similar: Array<{ name: string; similarity: number }>; + }; + }>; + }; + } + ).data?.scenarios || []; + + generateSpinner.succeed( + `Generated ${scenarios.length} scenario${scenarios.length > 1 ? 's' : ''}` + ); + + if (flags.json) { + output.json({ collection: collectionName, scenarios }); + return; + } + + output.newLine(); + + for (let i = 0; i < scenarios.length; i++) { + const item = scenarios[i]; + if (!item) continue; + const { scenario, similarity } = item; + output.text(output.bold(`${i + 1}. ${scenario.name}`)); + output.text(` ID: ${scenario.id}`); + + if (flags.verbose) { + output.text(` Prompt: ${scenario.prompt}`); + } else { + output.text(` Prompt: ${scenario.prompt.slice(0, 80)}...`); + } + + if (scenario.tags.length > 0) { + output.text(` Tags: ${scenario.tags.join(', ')}`); + } + + if (similarity?.hasSimilar) { + output.text( + output.yellow(` ⚠ Similar to existing: ${similarity.maxSimilarity}% match`) + ); + if (flags.verbose && similarity.similar.length > 0) { + for (const s of similarity.similar) { + output.text(output.dim(` - "${s.name}" (${s.similarity}% similar)`)); + } + } + } + + output.newLine(); + } + + output.text(output.dim('Run these scenarios with:')); + output.text(output.dim(` tpm scenario run ${args.collection}`)); + } catch (error) { + generateSpinner.fail('Failed to generate scenarios'); + output.error( + error instanceof Error ? error.message : 'Unknown error', + flags.verbose ? String(error) : undefined + ); + } + } +} diff --git a/packages/cli/src/commands/scenario/info.ts b/packages/cli/src/commands/scenario/info.ts new file mode 100644 index 0000000..00a8c4f --- /dev/null +++ b/packages/cli/src/commands/scenario/info.ts @@ -0,0 +1,190 @@ +import { Args, Command, Flags } from '@oclif/core'; +import { getClient } from '../../lib/api-client.js'; +import { createOutput } from '../../lib/output.js'; + +export default class ScenarioInfo extends Command { + static description = 'Show detailed information about a scenario'; + + static examples = [ + '<%= config.bin %> <%= command.id %> clu123abc456', + '<%= config.bin %> <%= command.id %> clu123abc456 --runs 20', + '<%= config.bin %> <%= command.id %> clu123abc456 --json', + ]; + + static args = { + scenarioId: Args.string({ + description: 'Scenario ID', + required: true, + }), + }; + + static flags = { + runs: Flags.integer({ + char: 'r', + description: 'Number of recent runs to show', + default: 10, + }), + json: Flags.boolean({ + description: 'Output in JSON format', + default: false, + }), + verbose: Flags.boolean({ + char: 'v', + description: 'Show verbose output', + default: false, + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(ScenarioInfo); + const output = createOutput(flags); + const client = getClient(); + + const spinner = output.spinner('Fetching scenario...'); + + try { + const response = await client.getScenario(args.scenarioId); + const scenario = ( + response as unknown as { + data: { + id: string; + name: string | null; + prompt: string; + description: string | null; + tags: string[]; + qualityScore: number; + consecutivePasses: number; + consecutiveFails: number; + totalRuns: number; + lastRunAt: string | null; + lastRunStatus: string | null; + createdAt: string; + updatedAt: string; + collection?: { + id: string; + name: string; + slug: string | null; + username: string | null; + }; + recentRuns?: Array<{ + id: string; + status: string; + evaluatorVerdict: string | null; + executionTimeMs: number | null; + createdAt: string; + }>; + runCount?: number; + }; + } + ).data; + + spinner.stop(); + + if (flags.json) { + output.json(scenario); + return; + } + + // Header + output.text(output.bold(scenario.name || 'Unnamed Scenario')); + output.text(output.dim(`ID: ${scenario.id}`)); + output.newLine(); + + // Collection info + if (scenario.collection) { + output.text(output.bold('Collection')); + output.text(` Name: ${scenario.collection.name}`); + if (scenario.collection.slug) { + output.text(` Slug: ${scenario.collection.slug}`); + } + if (scenario.collection.username) { + output.text(` Owner: @${scenario.collection.username}`); + } + output.newLine(); + } + + // Prompt + output.text(output.bold('Prompt')); + if (flags.verbose || scenario.prompt.length <= 200) { + output.text(` ${scenario.prompt}`); + } else { + output.text(` ${scenario.prompt.slice(0, 200)}...`); + output.text(output.dim(' (use --verbose to see full prompt)')); + } + output.newLine(); + + // Tags + if (scenario.tags && scenario.tags.length > 0) { + output.text(output.bold('Tags')); + output.text(` ${scenario.tags.join(', ')}`); + output.newLine(); + } + + // Metrics + output.text(output.bold('Metrics')); + output.text(` Quality Score: ${(scenario.qualityScore * 100).toFixed(1)}%`); + output.text(` Total Runs: ${scenario.totalRuns}`); + output.text(` Consecutive Passes: ${scenario.consecutivePasses}`); + output.text(` Consecutive Fails: ${scenario.consecutiveFails}`); + if (scenario.lastRunStatus) { + const statusColor = + scenario.lastRunStatus === 'pass' + ? output.green + : scenario.lastRunStatus === 'fail' + ? output.red + : output.yellow; + output.text(` Last Run Status: ${statusColor(scenario.lastRunStatus)}`); + } + if (scenario.lastRunAt) { + output.text(` Last Run: ${new Date(scenario.lastRunAt).toLocaleString()}`); + } + output.newLine(); + + // Timestamps + output.text(output.bold('Timestamps')); + output.text(` Created: ${new Date(scenario.createdAt).toLocaleString()}`); + output.text(` Updated: ${new Date(scenario.updatedAt).toLocaleString()}`); + output.newLine(); + + // Recent runs + if (scenario.recentRuns && scenario.recentRuns.length > 0) { + output.text( + output.bold( + `Recent Runs (${scenario.recentRuns.length} of ${scenario.runCount ?? scenario.totalRuns})` + ) + ); + output.table( + scenario.recentRuns.map((run) => ({ + status: + run.status === 'pass' + ? output.green('pass') + : run.status === 'fail' + ? output.red('fail') + : output.yellow(run.status), + verdict: run.evaluatorVerdict || '-', + time: run.executionTimeMs ? `${run.executionTimeMs}ms` : '-', + date: new Date(run.createdAt).toLocaleString(), + })), + [ + { key: 'status', header: 'Status', width: 10 }, + { key: 'verdict', header: 'Verdict', width: 10 }, + { key: 'time', header: 'Time', width: 12 }, + { key: 'date', header: 'Date', width: 25 }, + ] + ); + } else { + output.text(output.dim('No runs yet')); + } + + output.newLine(); + output.text(output.dim('Run this scenario with:')); + output.text(output.dim(` tpm scenario test ${scenario.id}`)); + } catch (error) { + spinner.fail('Failed to fetch scenario'); + output.error( + error instanceof Error ? error.message : 'Unknown error', + flags.verbose ? String(error) : undefined + ); + } + } +} diff --git a/packages/cli/src/commands/scenario/list.ts b/packages/cli/src/commands/scenario/list.ts new file mode 100644 index 0000000..68508eb --- /dev/null +++ b/packages/cli/src/commands/scenario/list.ts @@ -0,0 +1,181 @@ +import { Args, Command, Flags } from '@oclif/core'; +import { + type ApiResponse, + getClient, + type PaginatedResponse, + type Scenario, +} from '../../lib/api-client.js'; +import { createOutput } from '../../lib/output.js'; + +export default class ScenarioList extends Command { + static description = 'List scenarios for a collection or all public scenarios'; + + static examples = [ + '<%= config.bin %> <%= command.id %>', + '<%= config.bin %> <%= command.id %> my-collection', + '<%= config.bin %> <%= command.id %> --limit 20 --json', + ]; + + static args = { + collection: Args.string({ + description: 'Collection ID or slug (optional - shows all public scenarios if omitted)', + required: false, + }), + }; + + static flags = { + limit: Flags.integer({ + char: 'l', + description: 'Maximum number of results', + default: 20, + }), + offset: Flags.integer({ + char: 'o', + description: 'Offset for pagination', + default: 0, + }), + tags: Flags.string({ + char: 't', + description: 'Filter by tags (comma-separated)', + }), + json: Flags.boolean({ + description: 'Output in JSON format', + default: false, + }), + verbose: Flags.boolean({ + char: 'v', + description: 'Show verbose output', + default: false, + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(ScenarioList); + const output = createOutput(flags); + const client = getClient(); + + const spinner = output.spinner('Fetching scenarios...'); + + try { + let response: PaginatedResponse | ApiResponse<{ scenarios: Scenario[] }>; + + if (args.collection) { + // Try to find collection by ID or slug + const collections = await client.listCollections({ limit: 100 }); + const collection = collections.data.find( + (c) => c.id === args.collection || c.slug === args.collection + ); + + if (!collection) { + spinner.fail('Collection not found'); + output.error(`No collection found with ID or slug: ${args.collection}`); + return; + } + + response = await client.listCollectionScenarios(collection.id, { + limit: flags.limit, + offset: flags.offset, + }); + + spinner.stop(); + + if (flags.json) { + output.json(response); + return; + } + + const scenarios = + ( + response as unknown as { + data: { + scenarios: Array<{ + id: string; + name: string | null; + prompt: string; + qualityScore: number; + totalRuns: number; + lastRunStatus: string | null; + tags: string[]; + }>; + }; + } + ).data?.scenarios || []; + + if (scenarios.length === 0) { + output.info(`No scenarios found for collection "${collection.name}"`); + output.text( + 'Generate some with: tpm scenario generate ' + (collection.slug || collection.id) + ); + return; + } + + output.text(output.bold(`Scenarios for ${collection.name}\n`)); + output.table( + scenarios.map((s) => ({ + name: s.name || s.prompt.slice(0, 30) + '...', + quality: (s.qualityScore * 100).toFixed(0) + '%', + runs: s.totalRuns, + status: s.lastRunStatus || '-', + tags: s.tags.slice(0, 3).join(', ') || '-', + })), + [ + { key: 'name', header: 'Name', width: 35 }, + { key: 'quality', header: 'Quality', width: 10 }, + { key: 'runs', header: 'Runs', width: 8 }, + { key: 'status', header: 'Status', width: 8 }, + { key: 'tags', header: 'Tags', width: 20 }, + ] + ); + } else { + response = await client.listScenarios({ + limit: flags.limit, + offset: flags.offset, + tags: flags.tags, + }); + + spinner.stop(); + + if (flags.json) { + output.json(response); + return; + } + + if (response.data.length === 0) { + output.info('No public scenarios found'); + return; + } + + output.table( + response.data.map((s) => ({ + name: s.name || s.prompt.slice(0, 30) + '...', + collection: s.collection?.name || '-', + quality: (s.qualityScore * 100).toFixed(0) + '%', + runs: s.totalRuns, + status: s.lastRunStatus || '-', + })), + [ + { key: 'name', header: 'Name', width: 35 }, + { key: 'collection', header: 'Collection', width: 25 }, + { key: 'quality', header: 'Quality', width: 10 }, + { key: 'runs', header: 'Runs', width: 8 }, + { key: 'status', header: 'Status', width: 8 }, + ] + ); + + output.newLine(); + output.text( + output.dim( + `Showing ${response.data.length} scenario(s)` + + (response.pagination.hasMore ? ` (more available)` : '') + ) + ); + } + } catch (error) { + spinner.fail('Failed to fetch scenarios'); + output.error( + error instanceof Error ? error.message : 'Unknown error', + flags.verbose ? String(error) : undefined + ); + } + } +} diff --git a/packages/cli/src/commands/scenario/run.ts b/packages/cli/src/commands/scenario/run.ts new file mode 100644 index 0000000..de526ec --- /dev/null +++ b/packages/cli/src/commands/scenario/run.ts @@ -0,0 +1,208 @@ +import { Args, Command, Flags } from '@oclif/core'; +import { getClient } from '../../lib/api-client.js'; +import { createOutput } from '../../lib/output.js'; + +export default class ScenarioRun extends Command { + static description = 'Run all scenarios for a collection'; + + static examples = [ + '<%= config.bin %> <%= command.id %> my-collection', + '<%= config.bin %> <%= command.id %> my-collection --json', + '<%= config.bin %> <%= command.id %> my-collection --verbose', + ]; + + static args = { + collection: Args.string({ + description: 'Collection ID or slug', + required: true, + }), + }; + + static flags = { + json: Flags.boolean({ + description: 'Output in JSON format', + default: false, + }), + verbose: Flags.boolean({ + char: 'v', + description: 'Show verbose output', + default: false, + }), + limit: Flags.integer({ + char: 'l', + description: 'Maximum number of scenarios to run', + default: 50, + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(ScenarioRun); + const output = createOutput(flags); + const client = getClient(); + + if (!client.isAuthenticated()) { + output.error('Not authenticated. Run `tpm auth login` first.'); + return; + } + + // Find collection + const collectionsSpinner = output.spinner('Finding collection...'); + let collectionId: string; + + try { + const collections = await client.listCollections({ limit: 100 }); + const collection = collections.data.find( + (c) => c.id === args.collection || c.slug === args.collection + ); + + if (!collection) { + collectionsSpinner.fail('Collection not found'); + output.error(`No collection found with ID or slug: ${args.collection}`); + return; + } + + collectionId = collection.id; + collectionsSpinner.stop(); + output.text(output.bold(`Running scenarios for: ${collection.name}\n`)); + } catch (error) { + collectionsSpinner.fail('Failed to find collection'); + output.error(error instanceof Error ? error.message : 'Unknown error'); + return; + } + + // Fetch scenarios + const scenariosSpinner = output.spinner('Fetching scenarios...'); + let scenarios: Array<{ + id: string; + name: string | null; + prompt: string; + }>; + + try { + const response = await client.listCollectionScenarios(collectionId, { limit: flags.limit }); + scenarios = + ( + response as unknown as { + data: { + scenarios: Array<{ + id: string; + name: string | null; + prompt: string; + }>; + }; + } + ).data?.scenarios || []; + + if (scenarios.length === 0) { + scenariosSpinner.fail('No scenarios found'); + output.info('This collection has no scenarios. Generate some with:'); + output.text(` tpm scenario generate ${args.collection}`); + return; + } + + scenariosSpinner.stop(); + output.info(`Found ${scenarios.length} scenario(s) to run\n`); + } catch (error) { + scenariosSpinner.fail('Failed to fetch scenarios'); + output.error(error instanceof Error ? error.message : 'Unknown error'); + return; + } + + // Run each scenario + const results: Array<{ + name: string; + status: string; + verdict: string | null; + reason: string | null; + timeMs: number | null; + }> = []; + let passed = 0; + let failed = 0; + let errors = 0; + + for (const scenario of scenarios) { + const name = scenario.name || scenario.prompt.slice(0, 40) + '...'; + const runSpinner = output.spinner(`Running: ${name}`); + + try { + const result = await client.runScenario(scenario.id); + const runData = ( + result as unknown as { + data: { + status: string; + success: boolean; + evaluator: { verdict: string | null; reason: string | null }; + usage: { executionTimeMs: number | null }; + }; + } + ).data; + + if (runData.success) { + passed++; + runSpinner.succeed(`${output.green('✓')} ${name}`); + } else if (runData.status === 'error') { + errors++; + runSpinner.fail(`${output.red('✗')} ${name} (error)`); + } else { + failed++; + runSpinner.fail(`${output.red('✗')} ${name}`); + } + + if (flags.verbose && runData.evaluator?.reason) { + output.text(output.dim(` → ${runData.evaluator.reason}`)); + } + + results.push({ + name, + status: runData.status, + verdict: runData.evaluator?.verdict ?? null, + reason: runData.evaluator?.reason ?? null, + timeMs: runData.usage?.executionTimeMs ?? null, + }); + } catch (error) { + errors++; + runSpinner.fail(`${output.red('✗')} ${name} (error)`); + results.push({ + name, + status: 'error', + verdict: null, + reason: error instanceof Error ? error.message : 'Unknown error', + timeMs: null, + }); + } + } + + // Output summary + output.newLine(); + output.text(output.bold('─'.repeat(50))); + output.text(output.bold('Summary')); + output.text(` ${output.green('Passed:')} ${passed}`); + output.text(` ${output.red('Failed:')} ${failed}`); + if (errors > 0) { + output.text(` ${output.yellow('Errors:')} ${errors}`); + } + output.text(` ${output.dim('Total:')} ${scenarios.length}`); + + const passRate = scenarios.length > 0 ? (passed / scenarios.length) * 100 : 0; + output.newLine(); + output.text(`Pass rate: ${passRate.toFixed(1)}%`); + + if (flags.json) { + output.newLine(); + output.json({ + collection: args.collection, + total: scenarios.length, + passed, + failed, + errors, + passRate: passRate.toFixed(1), + results, + }); + } + + // Exit with error code if any failures + if (failed > 0 || errors > 0) { + this.exit(1); + } + } +} diff --git a/packages/cli/src/commands/scenario/test.ts b/packages/cli/src/commands/scenario/test.ts new file mode 100644 index 0000000..8389fe7 --- /dev/null +++ b/packages/cli/src/commands/scenario/test.ts @@ -0,0 +1,168 @@ +import { Args, Command, Flags } from '@oclif/core'; +import { getClient } from '../../lib/api-client.js'; +import { createOutput } from '../../lib/output.js'; + +export default class ScenarioTest extends Command { + static description = 'Run a single scenario by ID'; + + static examples = [ + '<%= config.bin %> <%= command.id %> clu123abc456', + '<%= config.bin %> <%= command.id %> clu123abc456 --json', + '<%= config.bin %> <%= command.id %> clu123abc456 --verbose', + ]; + + static args = { + scenarioId: Args.string({ + description: 'Scenario ID to run', + required: true, + }), + }; + + static flags = { + json: Flags.boolean({ + description: 'Output in JSON format', + default: false, + }), + verbose: Flags.boolean({ + char: 'v', + description: 'Show verbose output including full reason', + default: false, + }), + }; + + async run(): Promise { + const { args, flags } = await this.parse(ScenarioTest); + const output = createOutput(flags); + const client = getClient(); + + if (!client.isAuthenticated()) { + output.error('Not authenticated. Run `tpm auth login` first.'); + return; + } + + // Fetch scenario info first + const infoSpinner = output.spinner('Fetching scenario...'); + let scenarioName: string; + + try { + const scenarioResponse = await client.getScenario(args.scenarioId); + const scenario = ( + scenarioResponse as unknown as { + data: { + name: string | null; + prompt: string; + collection?: { name: string }; + }; + } + ).data; + + scenarioName = scenario.name || scenario.prompt.slice(0, 50) + '...'; + infoSpinner.stop(); + + output.text(output.bold(`Scenario: ${scenarioName}`)); + if (scenario.collection) { + output.text(output.dim(`Collection: ${scenario.collection.name}`)); + } + output.newLine(); + } catch (error) { + infoSpinner.fail('Scenario not found'); + output.error( + error instanceof Error ? error.message : 'Unknown error', + flags.verbose ? String(error) : undefined + ); + return; + } + + // Run the scenario + const runSpinner = output.spinner('Executing scenario...'); + + try { + const result = await client.runScenario(args.scenarioId); + const runData = ( + result as unknown as { + data: { + runId: string; + status: string; + success: boolean; + evaluator: { + model: string | null; + verdict: string | null; + reason: string | null; + }; + usage: { + inputTokens: number | null; + outputTokens: number | null; + totalTokens: number | null; + executionTimeMs: number | null; + }; + timestamps: { + startedAt: string | null; + completedAt: string | null; + createdAt: string; + }; + quotaRemaining: number; + }; + } + ).data; + + if (runData.success) { + runSpinner.succeed(output.green('Scenario PASSED')); + } else if (runData.status === 'error') { + runSpinner.fail(output.red('Scenario ERROR')); + } else { + runSpinner.fail(output.red('Scenario FAILED')); + } + + output.newLine(); + + if (flags.json) { + output.json(runData); + return; + } + + // Display results + output.text(output.bold('Results')); + output.text(` Status: ${runData.status}`); + output.text(` Verdict: ${runData.evaluator?.verdict || 'N/A'}`); + + if (runData.evaluator?.reason) { + if (flags.verbose) { + output.text(` Reason: ${runData.evaluator.reason}`); + } else { + const truncatedReason = + runData.evaluator.reason.length > 80 + ? runData.evaluator.reason.slice(0, 80) + '...' + : runData.evaluator.reason; + output.text(` Reason: ${truncatedReason}`); + } + } + + output.newLine(); + output.text(output.bold('Usage')); + if (runData.usage.executionTimeMs) { + output.text(` Duration: ${runData.usage.executionTimeMs}ms`); + } + if (runData.usage.totalTokens) { + output.text( + ` Tokens: ${runData.usage.totalTokens} (in: ${runData.usage.inputTokens}, out: ${runData.usage.outputTokens})` + ); + } + + output.newLine(); + output.text(output.dim(`Run ID: ${runData.runId}`)); + output.text(output.dim(`Quota remaining: ${runData.quotaRemaining} runs/day`)); + + // Exit with error code if failed + if (!runData.success) { + this.exit(1); + } + } catch (error) { + runSpinner.fail('Failed to run scenario'); + output.error( + error instanceof Error ? error.message : 'Unknown error', + flags.verbose ? String(error) : undefined + ); + this.exit(1); + } + } +} diff --git a/packages/cli/src/lib/api-client.ts b/packages/cli/src/lib/api-client.ts index 97f9421..cf80e49 100644 --- a/packages/cli/src/lib/api-client.ts +++ b/packages/cli/src/lib/api-client.ts @@ -149,6 +149,73 @@ export interface ApiKey { createdAt: string; } +// Scenario types +export interface Scenario { + id: string; + collectionId: string | null; + prompt: string; + name: string | null; + description: string | null; + tags: string[]; + qualityScore: number; + totalRuns: number; + lastRunAt: string | null; + lastRunStatus: string | null; + consecutivePasses: number; + consecutiveFails: number; + createdAt: string; + updatedAt: string; + collection?: { + id: string; + name: string; + slug: string | null; + username: string | null; + } | null; +} + +export interface ScenarioRun { + id: string; + status: string; + success: boolean; + evaluator: { + model: string | null; + verdict: string | null; + reason: string | null; + }; + assertions: unknown; + usage: { + inputTokens: number | null; + outputTokens: number | null; + totalTokens: number | null; + executionTimeMs: number | null; + }; + timestamps: { + startedAt: string | null; + completedAt: string | null; + createdAt: string; + }; + quotaRemaining?: number; +} + +export interface ScenarioListOptions extends PaginationOptions { + collectionId?: string; + tags?: string; + sortBy?: 'qualityScore' | 'totalRuns' | 'createdAt' | 'lastRunAt'; +} + +export interface CreateScenarioInput { + collectionId: string; + prompt: string; + name?: string; + description?: string; + tags?: string[]; +} + +export interface GenerateScenariosInput { + count?: number; + skipSimilarityCheck?: boolean; +} + // Stats types export interface Stats { tools: { @@ -175,10 +242,7 @@ export class TpmClient { this.timeout = options.timeout ?? 30000; } - private async request( - endpoint: string, - options: RequestInit = {} - ): Promise { + private async request(endpoint: string, options: RequestInit = {}): Promise { const url = `${this.baseUrl}${endpoint}`; const headers: Record = { 'Content-Type': 'application/json', @@ -199,7 +263,7 @@ export class TpmClient { signal: controller.signal, }); - const data = await response.json() as T & { message?: string; error?: string }; + const data = (await response.json()) as T & { message?: string; error?: string }; if (!response.ok) { throw new ApiError( @@ -240,14 +304,16 @@ export class TpmClient { } async getTool(packageName: string, toolName: string): Promise> { - return this.request(`/tools/${encodeURIComponent(packageName)}/${encodeURIComponent(toolName)}`); + return this.request( + `/tools/${encodeURIComponent(packageName)}/${encodeURIComponent(toolName)}` + ); } async getToolBySlug(slug: string): Promise> { // Search for the tool by slug const searchResult = await this.searchTools({ query: slug, limit: 1 }); if (searchResult.data && searchResult.data.length > 0) { - const tool = searchResult.data.find(t => t.slug === slug) || searchResult.data[0]; + const tool = searchResult.data.find((t) => t.slug === slug) || searchResult.data[0]; return { success: true, data: tool }; } return { success: false, error: 'Tool not found' }; @@ -264,7 +330,9 @@ export class TpmClient { return this.request>(endpoint); } - async validateTpmjsField(field: unknown): Promise> { + async validateTpmjsField( + field: unknown + ): Promise> { return this.request('/tools/validate', { method: 'POST', body: JSON.stringify(field), @@ -285,7 +353,7 @@ export class TpmClient { const url = `${this.baseUrl}/tools/${encodeURIComponent(slug)}/execute`; const headers: Record = { 'Content-Type': 'application/json', - 'Accept': 'text/event-stream', + Accept: 'text/event-stream', }; if (this.apiKey) { @@ -403,7 +471,10 @@ export class TpmClient { }); } - async updateCollection(id: string, input: UpdateCollectionInput): Promise> { + async updateCollection( + id: string, + input: UpdateCollectionInput + ): Promise> { return this.request(`/collections/${id}`, { method: 'PATCH', body: JSON.stringify(input), @@ -441,6 +512,80 @@ export class TpmClient { return this.request('/user/tpmjs-api-keys'); } + // Scenarios + async listScenarios(options: ScenarioListOptions = {}): Promise> { + const params = new URLSearchParams(); + if (options.limit) params.set('limit', String(options.limit)); + if (options.offset) params.set('offset', String(options.offset)); + if (options.collectionId) params.set('collectionId', options.collectionId); + if (options.tags) params.set('tags', options.tags); + if (options.sortBy) params.set('sortBy', options.sortBy); + + const queryString = params.toString(); + const endpoint = queryString ? `/scenarios?${queryString}` : '/scenarios'; + + return this.request>(endpoint); + } + + async listCollectionScenarios( + collectionId: string, + options: PaginationOptions = {} + ): Promise> { + const params = new URLSearchParams(); + if (options.limit) params.set('limit', String(options.limit)); + if (options.offset) params.set('offset', String(options.offset)); + + const queryString = params.toString(); + const endpoint = queryString + ? `/collections/${collectionId}/scenarios?${queryString}` + : `/collections/${collectionId}/scenarios`; + + return this.request>(endpoint); + } + + async getScenario(id: string): Promise> { + return this.request(`/scenarios/${id}`); + } + + async createScenario(input: CreateScenarioInput): Promise> { + return this.request('/scenarios', { + method: 'POST', + body: JSON.stringify(input), + }); + } + + async generateScenarios( + collectionId: string, + input: GenerateScenariosInput = {} + ): Promise> { + return this.request(`/collections/${collectionId}/scenarios/generate`, { + method: 'POST', + body: JSON.stringify(input), + }); + } + + async runScenario(scenarioId: string): Promise> { + return this.request(`/scenarios/${scenarioId}/run`, { + method: 'POST', + }); + } + + async getScenarioRuns( + scenarioId: string, + options: PaginationOptions = {} + ): Promise> { + const params = new URLSearchParams(); + if (options.limit) params.set('limit', String(options.limit)); + if (options.offset) params.set('offset', String(options.offset)); + + const queryString = params.toString(); + const endpoint = queryString + ? `/scenarios/${scenarioId}/runs?${queryString}` + : `/scenarios/${scenarioId}/runs`; + + return this.request>(endpoint); + } + // Check if authenticated isAuthenticated(): boolean { return !!this.apiKey; diff --git a/packages/cli/src/lib/output.ts b/packages/cli/src/lib/output.ts index 2fb9be9..3004ac6 100644 --- a/packages/cli/src/lib/output.ts +++ b/packages/cli/src/lib/output.ts @@ -173,6 +173,27 @@ export class OutputFormatter { // OSC 8 hyperlink support for modern terminals return `\x1b]8;;${url}\x07${pc.underline(pc.blue(text))}\x1b]8;;\x07`; } + + // Color helpers + green(text: string): string { + return pc.green(text); + } + + red(text: string): string { + return pc.red(text); + } + + yellow(text: string): string { + return pc.yellow(text); + } + + blue(text: string): string { + return pc.blue(text); + } + + cyan(text: string): string { + return pc.cyan(text); + } } // Convenience function to create formatter from command flags diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index f122a9c..eaf21b6 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -467,6 +467,7 @@ model Collection { agents AgentCollection[] likes CollectionLike[] bridgeTools CollectionBridgeTool[] + scenarios Scenario[] // Unique constraint: user can't have duplicate collection slugs @@unique([userId, slug]) @@ -1118,3 +1119,136 @@ model ApiUsageSummary { @@index([periodType, periodStart]) @@map("api_usage_summaries") } + +// ============================================================================ +// Scenario Models (Integration Testing for Collections) +// ============================================================================ + +/// Scenario - AI-generated test scenarios for collections +model Scenario { + id String @id @default(cuid()) + + // Collection relationship (nullable for orphaned scenarios) + collectionId String? @map("collection_id") + collection Collection? @relation(fields: [collectionId], references: [id], onDelete: SetNull) + + // Content + prompt String @db.Text // AI-generated free-form prompt + name String? @db.VarChar(200) // Optional human-readable name + description String? @db.Text + + // Validation (optional assertions) + assertions Json? @db.JsonB // { regex?: string[], schema?: object } + + // AI-generated metadata + tags String[] @default([]) @db.Text + + // Quality metrics (streak-based scoring) + qualityScore Float @default(0) @map("quality_score") + consecutivePasses Int @default(0) @map("consecutive_passes") + consecutiveFails Int @default(0) @map("consecutive_fails") + totalRuns Int @default(0) @map("total_runs") + lastRunAt DateTime? @map("last_run_at") + lastRunStatus String? @map("last_run_status") @db.VarChar(20) // 'pass' | 'fail' | 'error' + + // Timestamps + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + // Relations + runs ScenarioRun[] + embedding ScenarioEmbedding? + + @@index([collectionId]) + @@index([qualityScore]) + @@index([createdAt]) + @@index([lastRunStatus]) + @@map("scenarios") +} + +/// ScenarioEmbedding - vector embeddings for scenario similarity detection +model ScenarioEmbedding { + id String @id @default(cuid()) + + // Scenario relationship + scenarioId String @unique @map("scenario_id") + scenario Scenario @relation(fields: [scenarioId], references: [id], onDelete: Cascade) + + // Embedding data + embedding Json @db.JsonB // Array of floats (1536 dims for text-embedding-3-small) + model String @default("text-embedding-3-small") @db.VarChar(50) + + // Timestamps + createdAt DateTime @default(now()) @map("created_at") + + @@index([scenarioId]) + @@map("scenario_embeddings") +} + +/// ScenarioRun - individual execution records for scenarios +model ScenarioRun { + id String @id @default(cuid()) + + // Scenario relationship + scenarioId String @map("scenario_id") + scenario Scenario @relation(fields: [scenarioId], references: [id], onDelete: Cascade) + + // Execution context + userId String @map("user_id") // Who triggered the run + agentId String? @map("agent_id") // Ephemeral agent ID (for debugging) + + // Status + status String @db.VarChar(20) // 'pending' | 'running' | 'pass' | 'fail' | 'error' + retryCount Int @default(0) @map("retry_count") + + // Results + conversation Json? @db.JsonB // Full message history + output String? @db.Text // Final output from agent + errorLog String? @map("error_log") @db.Text // Full error logs (private to owner) + + // LLM Evaluation + evaluatorModel String? @map("evaluator_model") @db.VarChar(50) // e.g., "claude-3.5-sonnet" + evaluatorVerdict String? @map("evaluator_verdict") @db.VarChar(10) // 'pass' | 'fail' + evaluatorReason String? @map("evaluator_reason") @db.Text // Explanation + + // Assertions + assertionResults Json? @map("assertion_results") @db.JsonB // { passed: string[], failed: string[] } + + // Cost tracking + inputTokens Int? @map("input_tokens") + outputTokens Int? @map("output_tokens") + totalTokens Int? @map("total_tokens") + executionTimeMs Int? @map("execution_time_ms") + estimatedCost Decimal? @map("estimated_cost") @db.Decimal(10, 6) + + // Timestamps + startedAt DateTime? @map("started_at") + completedAt DateTime? @map("completed_at") + createdAt DateTime @default(now()) @map("created_at") + + @@index([scenarioId]) + @@index([userId]) + @@index([status]) + @@index([createdAt]) + @@map("scenario_runs") +} + +/// ScenarioQuota - daily usage quotas for scenario runs +model ScenarioQuota { + id String @id @default(cuid()) + + // User relationship + userId String @unique @map("user_id") + + // Quota configuration + dailyLimit Int @default(50) @map("daily_limit") // Runs per day + dailyUsed Int @default(0) @map("daily_used") + lastResetAt DateTime @default(now()) @map("last_reset_at") + + // Timestamps + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@index([userId]) + @@map("scenario_quotas") +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e553dcc..cc62e7b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -440,6 +440,58 @@ importers: specifier: ^5.9.3 version: 5.9.3 + packages/cli: + dependencies: + '@oclif/core': + specifier: ^4.2.10 + version: 4.8.0 + '@oclif/plugin-autocomplete': + specifier: ^3.2.25 + version: 3.2.39 + '@oclif/plugin-help': + specifier: ^6.2.27 + version: 6.2.36 + '@oclif/plugin-plugins': + specifier: ^5.4.36 + version: 5.4.54 + '@tpmjs/types': + specifier: workspace:* + version: link:../types + cli-table3: + specifier: ^0.6.5 + version: 0.6.5 + conf: + specifier: ^13.1.0 + version: 13.1.0 + eventsource-parser: + specifier: ^3.0.1 + version: 3.0.6 + open: + specifier: ^10.1.0 + version: 10.2.0 + ora: + specifier: ^8.2.0 + version: 8.2.0 + picocolors: + specifier: ^1.1.1 + version: 1.1.1 + devDependencies: + '@tpmjs/tsconfig': + specifier: workspace:* + version: link:../config/tsconfig + '@types/node': + specifier: ^22.15.29 + version: 22.19.5 + oclif: + specifier: ^4.17.35 + version: 4.22.65(@types/node@22.19.5) + tsup: + specifier: ^8.5.1 + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + packages/config: dependencies: zod: @@ -4091,6 +4143,173 @@ packages: '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-cloudfront@3.971.0': + resolution: {integrity: sha512-kLtm5jaWVXaej8a6WbFd1iDMFXy19WakT8b/hk3gHtcm6KfnTGX1K/YwpNGfuTzUze16ZjQrbIen/loM+2U2KA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-s3@3.971.0': + resolution: {integrity: sha512-BBUne390fKa4C4QvZlUZ5gKcu+Uyid4IyQ20N4jl0vS7SK2xpfXlJcgKqPW5ts6kx6hWTQBk6sH5Lf12RvuJxg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-sso@3.971.0': + resolution: {integrity: sha512-Xx+w6DQqJxDdymYyIxyKJnRzPvVJ4e/Aw0czO7aC9L/iraaV7AG8QtRe93OGW6aoHSh72CIiinnpJJfLsQqP4g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.970.0': + resolution: {integrity: sha512-klpzObldOq8HXzDjDlY6K8rMhYZU6mXRz6P9F9N+tWnjoYFfeBMra8wYApydElTUYQKP1O7RLHwH1OKFfKcqIA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/crc64-nvme@3.969.0': + resolution: {integrity: sha512-IGNkP54HD3uuLnrPCYsv3ZD478UYq+9WwKrIVJ9Pdi3hxPg8562CH3ZHf8hEgfePN31P9Kj+Zu9kq2Qcjjt61A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.970.0': + resolution: {integrity: sha512-rtVzXzEtAfZBfh+lq3DAvRar4c3jyptweOAJR2DweyXx71QSMY+O879hjpMwES7jl07a3O1zlnFIDo4KP/96kQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.970.0': + resolution: {integrity: sha512-CjDbWL7JxjLc9ZxQilMusWSw05yRvUJKRpz59IxDpWUnSMHC9JMMUUkOy5Izk8UAtzi6gupRWArp4NG4labt9Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.971.0': + resolution: {integrity: sha512-c0TGJG4xyfTZz3SInXfGU8i5iOFRrLmy4Bo7lMyH+IpngohYMYGYl61omXqf2zdwMbDv+YJ9AviQTcCaEUKi8w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.971.0': + resolution: {integrity: sha512-yhbzmDOsk0RXD3rTPhZra4AWVnVAC4nFWbTp+sUty1hrOPurUmhuz8bjpLqYTHGnlMbJp+UqkQONhS2+2LzW2g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.971.0': + resolution: {integrity: sha512-epUJBAKivtJqalnEBRsYIULKYV063o/5mXNJshZfyvkAgNIzc27CmmKRXTN4zaNOZg8g/UprFp25BGsi19x3nQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.970.0': + resolution: {integrity: sha512-0XeT8OaT9iMA62DFV9+m6mZfJhrD0WNKf4IvsIpj2Z7XbaYfz3CoDDvNoALf3rPY9NzyMHgDxOspmqdvXP00mw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.971.0': + resolution: {integrity: sha512-dY0hMQ7dLVPQNJ8GyqXADxa9w5wNfmukgQniLxGVn+dMRx3YLViMp5ZpTSQpFhCWNF0oKQrYAI5cHhUJU1hETw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.971.0': + resolution: {integrity: sha512-F1AwfNLr7H52T640LNON/h34YDiMuIqW/ZreGzhRR6vnFGaSPtNSKAKB2ssAMkLM8EVg8MjEAYD3NCUiEo+t/w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-bucket-endpoint@3.969.0': + resolution: {integrity: sha512-MlbrlixtkTVhYhoasblKOkr7n2yydvUZjjxTnBhIuHmkyBS1619oGnTfq/uLeGYb4NYXdeQ5OYcqsRGvmWSuTw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-expect-continue@3.969.0': + resolution: {integrity: sha512-qXygzSi8osok7tH9oeuS3HoKw6jRfbvg5Me/X5RlHOvSSqQz8c5O9f3MjUApaCUSwbAU92KrbZWasw2PKiaVHg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.971.0': + resolution: {integrity: sha512-+hGUDUxeIw8s2kkjfeXym0XZxdh0cqkHkDpEanWYdS1gnWkIR+gf9u/DKbKqGHXILPaqHXhWpLTQTVlaB4sI7Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.969.0': + resolution: {integrity: sha512-AWa4rVsAfBR4xqm7pybQ8sUNJYnjyP/bJjfAw34qPuh3M9XrfGbAHG0aiAfQGrBnmS28jlO6Kz69o+c6PRw1dw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-location-constraint@3.969.0': + resolution: {integrity: sha512-zH7pDfMLG/C4GWMOpvJEoYcSpj7XsNP9+irlgqwi667sUQ6doHQJ3yyDut3yiTk0maq1VgmriPFELyI9lrvH/g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.969.0': + resolution: {integrity: sha512-xwrxfip7Y2iTtCMJ+iifN1E1XMOuhxIHY9DreMCvgdl4r7+48x2S1bCYPWH3eNY85/7CapBWdJ8cerpEl12sQQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.969.0': + resolution: {integrity: sha512-2r3PuNquU3CcS1Am4vn/KHFwLi8QFjMdA/R+CRDXT4AFO/0qxevF/YStW3gAKntQIgWgQV8ZdEtKAoJvLI4UWg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.970.0': + resolution: {integrity: sha512-v/Y5F1lbFFY7vMeG5yYxuhnn0CAshz6KMxkz1pDyPxejNE9HtA0w8R6OTBh/bVdIm44QpjhbI7qeLdOE/PLzXQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-ssec@3.971.0': + resolution: {integrity: sha512-QGVhvRveYG64ZhnS/b971PxXM6N2NU79Fxck4EfQ7am8v1Br0ctoeDDAn9nXNblLGw87we9Z65F7hMxxiFHd3w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.970.0': + resolution: {integrity: sha512-dnSJGGUGSFGEX2NzvjwSefH+hmZQ347AwbLhAsi0cdnISSge+pcGfOFrJt2XfBIypwFe27chQhlfuf/gWdzpZg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.971.0': + resolution: {integrity: sha512-TWaILL8GyYlhGrxxnmbkazM4QsXatwQgoWUvo251FXmUOsiXDFDVX3hoGIfB3CaJhV2pJPfebHUNJtY6TjZ11g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.969.0': + resolution: {integrity: sha512-scj9OXqKpcjJ4jsFLtqYWz3IaNvNOQTFFvEY8XMJXTv+3qF5I7/x9SJtKzTRJEBF3spjzBUYPtGFbs9sj4fisQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.970.0': + resolution: {integrity: sha512-z3syXfuK/x/IsKf/AeYmgc2NT7fcJ+3fHaGO+fkghkV9WEba3fPyOwtTBX4KpFMNb2t50zDGZwbzW1/5ighcUQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.971.0': + resolution: {integrity: sha512-4hKGWZbmuDdONMJV0HJ+9jwTDb0zLfKxcCLx2GEnBY31Gt9GeyIQ+DZ97Bb++0voawj6pnZToFikXTyrEq2x+w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.969.0': + resolution: {integrity: sha512-7IIzM5TdiXn+VtgPdVLjmE6uUBUtnga0f4RiSEI1WW10RPuNvZ9U+pL3SwDiRDAdoGrOF9tSLJOFZmfuwYuVYQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-arn-parser@3.968.0': + resolution: {integrity: sha512-gqqvYcitIIM2K4lrDX9de9YvOfXBcVdxfT/iLnvHJd4YHvSXlt+gs+AsL4FfPCxG4IG9A+FyulP9Sb1MEA75vw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.970.0': + resolution: {integrity: sha512-TZNZqFcMUtjvhZoZRtpEGQAdULYiy6rcGiXAbLU7e9LSpIYlRqpLa207oMNfgbzlL2PnHko+eVg8rajDiSOYCg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.2': + resolution: {integrity: sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.969.0': + resolution: {integrity: sha512-bpJGjuKmFr0rA6UKUCmN8D19HQFMLXMx5hKBXqBlPFdalMhxJSjcxzX9DbQh0Fn6bJtxCguFmRGOBdQqNOt49g==} + + '@aws-sdk/util-user-agent-node@3.971.0': + resolution: {integrity: sha512-Eygjo9mFzQYjbGY3MYO6CsIhnTwAMd3WmuFalCykqEmj2r5zf0leWrhPaqvA5P68V5JdGfPYgj7vhNOd6CtRBQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.969.0': + resolution: {integrity: sha512-BSe4Lx/qdRQQdX8cSSI7Et20vqBspzAjBy8ZmXVoyLkol3y4sXBXzn+BiLtR+oh60ExQn6o2DU4QjdOZbXaKIQ==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.3': + resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -4343,6 +4562,10 @@ packages: '@clack/prompts@0.11.0': resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==} + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} @@ -5082,6 +5305,19 @@ packages: resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} + '@inquirer/checkbox@4.3.2': + resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@3.2.0': + resolution: {integrity: sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==} + engines: {node: '>=18'} + '@inquirer/confirm@5.1.21': resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} engines: {node: '>=18'} @@ -5100,6 +5336,28 @@ packages: '@types/node': optional: true + '@inquirer/core@9.2.1': + resolution: {integrity: sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==} + engines: {node: '>=18'} + + '@inquirer/editor@4.2.23': + resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.23': + resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} @@ -5113,6 +5371,85 @@ packages: resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} engines: {node: '>=18'} + '@inquirer/input@2.3.0': + resolution: {integrity: sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==} + engines: {node: '>=18'} + + '@inquirer/input@4.3.1': + resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.23': + resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.23': + resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.10.1': + resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.1.11': + resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.2.2': + resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@2.5.0': + resolution: {integrity: sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==} + engines: {node: '>=18'} + + '@inquirer/select@4.4.2': + resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@1.5.5': + resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==} + engines: {node: '>=18'} + + '@inquirer/type@2.0.0': + resolution: {integrity: sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==} + engines: {node: '>=18'} + '@inquirer/type@3.0.10': resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} engines: {node: '>=18'} @@ -5310,6 +5647,30 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@oclif/core@4.8.0': + resolution: {integrity: sha512-jteNUQKgJHLHFbbz806aGZqf+RJJ7t4gwF4MYa8fCwCxQ8/klJNWc0MvaJiBebk7Mc+J39mdlsB4XraaCKznFw==} + engines: {node: '>=18.0.0'} + + '@oclif/plugin-autocomplete@3.2.39': + resolution: {integrity: sha512-OwAZNnSpuDjKyhAwoOJkFWxGswPFKBB4hpNIMsj6PUtbKwGBPmD+2wGGPgTsDioVwLmUELSb2bZ+1dxHfvXmvg==} + engines: {node: '>=18.0.0'} + + '@oclif/plugin-help@6.2.36': + resolution: {integrity: sha512-NBQIg5hEMhvdbi4mSrdqRGl5XJ0bqTAHq6vDCCCDXUcfVtdk3ZJbSxtRVWyVvo9E28vwqu6MZyHOJylevqcHbA==} + engines: {node: '>=18.0.0'} + + '@oclif/plugin-not-found@3.2.73': + resolution: {integrity: sha512-2bQieTGI9XNFe9hKmXQjJmHV5rZw+yn7Rud1+C5uLEo8GaT89KZbiLTJgL35tGILahy/cB6+WAs812wjw7TK6w==} + engines: {node: '>=18.0.0'} + + '@oclif/plugin-plugins@5.4.54': + resolution: {integrity: sha512-yzdukEfvvyXx31AhN+YhxLhuQdx2SrZDcRtPl5CNkuqh/uNSB2BuA3xpurdv2qotpaw/Z9InRl+Sa9bLp/4aLA==} + engines: {node: '>=18.0.0'} + + '@oclif/plugin-warn-if-update-available@3.1.53': + resolution: {integrity: sha512-ALxKMNFFJQJV1Z2OMVTV+q7EbKHhnTAPcTgkgHeXCNdW5nFExoXuwusZLS4Zv2o83j9UoDx1R/CSX7QZVgEHTA==} + engines: {node: '>=18.0.0'} + '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} @@ -5430,6 +5791,18 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@3.0.2': + resolution: {integrity: sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==} + engines: {node: '>=12'} + '@prisma/client-runtime-utils@7.2.0': resolution: {integrity: sha512-dn7oB53v0tqkB0wBdMuTNFNPdEbfICEUe82Tn9FoKAhJCUkDH+fmyEp0ClciGh+9Hp2Tuu2K52kth2MTLstvmA==} @@ -5735,6 +6108,226 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@sindresorhus/is@5.6.0': + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + + '@smithy/abort-controller@4.2.8': + resolution: {integrity: sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader-native@4.2.1': + resolution: {integrity: sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader@5.2.0': + resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.4.6': + resolution: {integrity: sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.20.7': + resolution: {integrity: sha512-aO7jmh3CtrmPsIJxUwYIzI5WVlMK8BMCPQ4D4nTzqTqBhbzvxHNzBMGcEg13yg/z9R2Qsz49NUFl0F0lVbTVFw==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.8': + resolution: {integrity: sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-codec@4.2.8': + resolution: {integrity: sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-browser@4.2.8': + resolution: {integrity: sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-config-resolver@4.3.8': + resolution: {integrity: sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-node@4.2.8': + resolution: {integrity: sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-universal@4.2.8': + resolution: {integrity: sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.9': + resolution: {integrity: sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-blob-browser@4.2.9': + resolution: {integrity: sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.8': + resolution: {integrity: sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-stream-node@4.2.8': + resolution: {integrity: sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.8': + resolution: {integrity: sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.0': + resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} + engines: {node: '>=18.0.0'} + + '@smithy/md5-js@4.2.8': + resolution: {integrity: sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.8': + resolution: {integrity: sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.8': + resolution: {integrity: sha512-TV44qwB/T0OMMzjIuI+JeS0ort3bvlPJ8XIH0MSlGADraXpZqmyND27ueuAL3E14optleADWqtd7dUgc2w+qhQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.24': + resolution: {integrity: sha512-yiUY1UvnbUFfP5izoKLtfxDSTRv724YRRwyiC/5HYY6vdsVDcDOXKSXmkJl/Hovcxt5r+8tZEUAdrOaCJwrl9Q==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.9': + resolution: {integrity: sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.8': + resolution: {integrity: sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.8': + resolution: {integrity: sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.4.8': + resolution: {integrity: sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.8': + resolution: {integrity: sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.8': + resolution: {integrity: sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.8': + resolution: {integrity: sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.8': + resolution: {integrity: sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.8': + resolution: {integrity: sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.3': + resolution: {integrity: sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.8': + resolution: {integrity: sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.10.9': + resolution: {integrity: sha512-Je0EvGXVJ0Vrrr2lsubq43JGRIluJ/hX17aN/W/A0WfE+JpoMdI8kwk2t9F0zTX9232sJDGcoH4zZre6m6f/sg==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.12.0': + resolution: {integrity: sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.8': + resolution: {integrity: sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.0': + resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.0': + resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.1': + resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.0': + resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.0': + resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.23': + resolution: {integrity: sha512-mMg+r/qDfjfF/0psMbV4zd7F/i+rpyp7Hjh0Wry7eY15UnzTEId+xmQTGDU8IdZtDfbGQxuWNfgBZKBj+WuYbA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.26': + resolution: {integrity: sha512-EQqe/WkbCinah0h1lMWh9ICl0Ob4lyl20/10WTB35SC9vDQfD8zWsOT+x2FIOXKAoZQ8z/y0EFMoodbcqWJY/w==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.2.8': + resolution: {integrity: sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.0': + resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.8': + resolution: {integrity: sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.8': + resolution: {integrity: sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.10': + resolution: {integrity: sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.0': + resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.0': + resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-waiter@4.2.8': + resolution: {integrity: sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.0': + resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==} + engines: {node: '>=18.0.0'} + '@stablelib/base64@1.0.1': resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} @@ -5936,6 +6529,10 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@szmarczak/http-timer@5.0.1': + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + '@tailwindcss/typography@0.5.19': resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} peerDependencies: @@ -6138,6 +6735,9 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} @@ -6173,6 +6773,9 @@ packages: '@types/mustache@4.2.6': resolution: {integrity: sha512-t+8/QWTAhOFlrF1IVZqKnMRJi84EgkIK5Kh0p2JV4OLywUvCwJPFxbJAl7XAow7DVIHsF+xW9f1MVzg0L6Szjw==} + '@types/mute-stream@0.0.4': + resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -6261,6 +6864,9 @@ packages: '@types/whatwg-url@11.0.5': resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + '@types/wrap-ansi@3.0.0': + resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} + '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -6595,6 +7201,10 @@ packages: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -6615,6 +7225,10 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} + ansis@3.17.0: + resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==} + engines: {node: '>=14'} + any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -6709,6 +7323,9 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + atomically@2.1.0: + resolution: {integrity: sha512-+gDffFXRW6sl/HCwbta7zK4uNqbPjv4YJEAdz7Vu+FLQHe77eZ4bvbJGi4hE0QPeJlMYMA3piXEr1UL3dAwx7Q==} + autoprefixer@10.4.23: resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} engines: {node: ^10 || ^12 || >=14} @@ -6866,6 +7483,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bowser@2.13.1: + resolution: {integrity: sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -6897,6 +7517,10 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + bundle-require@5.1.0: resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -6919,6 +7543,14 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + + cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -6935,6 +7567,9 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} @@ -6948,6 +7583,9 @@ packages: caniuse-lite@1.0.30001763: resolution: {integrity: sha512-mh/dGtq56uN98LlNX9qdbKnzINhX0QzhiWBFEkFfsFO4QyCvL8YegrJAazCwXIeqkIob8BlZPGM3xdnY+sgmvQ==} + capital-case@1.0.4: + resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} + caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -6974,6 +7612,9 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + change-case@4.1.2: + resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==} + character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} @@ -7033,6 +7674,10 @@ packages: citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + clean-stack@3.0.1: + resolution: {integrity: sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==} + engines: {node: '>=10'} + cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -7045,6 +7690,10 @@ packages: resolution: {integrity: sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ==} engines: {node: '>=18.20'} + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -7101,16 +7750,26 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + conf@13.1.0: + resolution: {integrity: sha512-Bi6v586cy1CoTFViVO4lGTtx780lfF96fUmS1lSX6wpZf6330NvHUu6fReVuDP1de8Mg0nkZb01c8tAQdz1o3w==} + engines: {node: '>=18'} + confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + constant-case@3.0.4: + resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} + content-disposition@1.0.1: resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} engines: {node: '>=18'} @@ -7368,6 +8027,10 @@ packages: dayjs@1.11.19: resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + debounce-fn@6.0.0: + resolution: {integrity: sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ==} + engines: {node: '>=18'} + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -7414,6 +8077,18 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.4.0: + resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==} + engines: {node: '>=18'} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -7422,6 +8097,10 @@ packages: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -7463,10 +8142,18 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} + detect-indent@7.0.2: + resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==} + engines: {node: '>=12.20'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + detect-newline@4.0.1: + resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -7514,6 +8201,13 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dot-prop@9.0.0: + resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} + engines: {node: '>=18'} + dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -7637,6 +8331,11 @@ packages: effect@3.18.4: resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==} + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} @@ -7683,6 +8382,9 @@ packages: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + es-abstract@1.24.1: resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} @@ -7950,16 +8652,27 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-levenshtein@3.0.0: + resolution: {integrity: sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==} + fast-sha256@1.3.0: resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-xml-parser@5.2.5: + resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} + hasBin: true + fast-xml-parser@5.3.3: resolution: {integrity: sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==} hasBin: true + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -7991,6 +8704,9 @@ packages: file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -8007,6 +8723,9 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + find-yarn-workspace-root@2.0.0: + resolution: {integrity: sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==} + firecrawl-aisdk@0.7.2: resolution: {integrity: sha512-JyqKs12ScYcKmHnvkc/h/ItvcJxLjxn1WkFTe6w6RVb/LXVw7gqjOuRg2bvfsZeeqlTOyiLAzy5tbj4eMOgjRA==} engines: {node: '>=18.0.0'} @@ -8041,6 +8760,10 @@ packages: forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} + form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + form-data@2.3.3: resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} engines: {node: '>= 0.12'} @@ -8141,6 +8864,10 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + get-port-please@3.1.2: resolution: {integrity: sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==} @@ -8148,6 +8875,14 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-stdin@9.0.0: + resolution: {integrity: sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==} + engines: {node: '>=12'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} @@ -8162,9 +8897,15 @@ packages: resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} hasBin: true + git-hooks-list@3.2.0: + resolution: {integrity: sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==} + github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -8208,6 +8949,13 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + got@13.0.0: + resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==} + engines: {node: '>=16'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -8311,6 +9059,9 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + header-case@2.0.4: + resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} + headers-polyfill@4.0.3: resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} @@ -8333,6 +9084,10 @@ packages: resolution: {integrity: sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==} engines: {node: '>=16.9.0'} + hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + html-encoding-sniffer@3.0.0: resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} engines: {node: '>=12'} @@ -8350,6 +9105,13 @@ packages: htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-call@5.3.0: + resolution: {integrity: sha512-ahwimsC23ICE4kPl9xTBjKB4inbRaeLyZeRunC/1Jy/Z6X8tv22MEAjK+KBOMSVLaqXPTTmd8638waVIKLGx2w==} + engines: {node: '>=8.0.0'} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -8374,6 +9136,10 @@ packages: http-status-codes@2.3.0: resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} + http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -8462,6 +9228,9 @@ packages: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -8509,6 +9278,11 @@ packages: engines: {node: '>=8'} hasBin: true + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -8532,6 +9306,11 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + is-installed-globally@1.0.0: resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} engines: {node: '>=18'} @@ -8587,6 +9366,10 @@ packages: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} + is-retry-allowed@1.2.0: + resolution: {integrity: sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==} + engines: {node: '>=0.10.0'} + is-set@2.0.3: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} @@ -8595,6 +9378,10 @@ packages: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} @@ -8642,6 +9429,10 @@ packages: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -8671,6 +9462,11 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true @@ -8725,6 +9521,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + json-promise@1.1.8: resolution: {integrity: sha512-rz31P/7VfYnjQFrF60zpPTT0egMPlc8ZvIQHWs4ZtNZNnAXRmXo6oS+6eyWr5sEMG03OVhklNrTXxiIRYzoUgQ==} @@ -9023,6 +9822,13 @@ packages: loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lowlight@1.20.0: resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} @@ -9315,6 +10121,10 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -9326,6 +10136,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -9419,6 +10233,10 @@ packages: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mute-stream@2.0.0: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} @@ -9494,6 +10312,9 @@ packages: sass: optional: true + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + node-abi@3.85.0: resolution: {integrity: sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==} engines: {node: '>=10'} @@ -9504,10 +10325,100 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + normalize-package-data@6.0.2: + resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} + engines: {node: ^16.14.0 || >=18.0.0} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-url@8.1.1: + resolution: {integrity: sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==} + engines: {node: '>=14.16'} + + npm-package-arg@11.0.3: + resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==} + engines: {node: ^16.14.0 || >=18.0.0} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + npm@10.9.4: + resolution: {integrity: sha512-OnUG836FwboQIbqtefDNlyR0gTHzIfwRfE3DuiNewBvnMnWEpB0VEXwBlFVgqpNzIgYo/MHh3d2Hel/pszapAA==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + bundledDependencies: + - '@isaacs/string-locale-compare' + - '@npmcli/arborist' + - '@npmcli/config' + - '@npmcli/fs' + - '@npmcli/map-workspaces' + - '@npmcli/package-json' + - '@npmcli/promise-spawn' + - '@npmcli/redact' + - '@npmcli/run-script' + - '@sigstore/tuf' + - abbrev + - archy + - cacache + - chalk + - ci-info + - cli-columns + - fastest-levenshtein + - fs-minipass + - glob + - graceful-fs + - hosted-git-info + - ini + - init-package-json + - is-cidr + - json-parse-even-better-errors + - libnpmaccess + - libnpmdiff + - libnpmexec + - libnpmfund + - libnpmhook + - libnpmorg + - libnpmpack + - libnpmpublish + - libnpmsearch + - libnpmteam + - libnpmversion + - make-fetch-happen + - minimatch + - minipass + - minipass-pipeline + - ms + - node-gyp + - nopt + - normalize-package-data + - npm-audit-report + - npm-install-checks + - npm-package-arg + - npm-pick-manifest + - npm-profile + - npm-registry-fetch + - npm-user-validate + - p-map + - pacote + - parse-conflict-json + - proc-log + - qrcode-terminal + - read + - semver + - spdx-expression-parse + - ssri + - supports-color + - tar + - text-table + - tiny-relative-date + - treeverse + - validate-npm-package-name + - which + - write-file-atomic + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -9535,6 +10446,10 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + object-treeify@4.0.1: + resolution: {integrity: sha512-Y6tg5rHfsefSkfKujv2SwHulInROy/rCL5F4w0QOWxut8AnxYxf0YmNhTh95Zfyxpsudo66uqkux0ACFnyMSgQ==} + engines: {node: '>= 16'} + object.assign@4.1.7: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} @@ -9558,6 +10473,11 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + oclif@4.22.65: + resolution: {integrity: sha512-pJW0P+gUzIAS6gSQH11jmbu9xQgjfxgBV+FjWvvwu68NUtljtpZm1w3uftXUVk51Ra40r9XB1Jh/Mcbb+I6yJw==} + engines: {node: '>=18.0.0'} + hasBin: true + ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} @@ -9578,6 +10498,10 @@ packages: oniguruma-to-es@4.3.4: resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -9626,6 +10550,10 @@ packages: oxc-resolver@11.16.2: resolution: {integrity: sha512-Uy76u47vwhhF7VAmVY61Srn+ouiOobf45MU9vGct9GD2ARy6hKoqEElyHDB0L+4JOM6VLuZ431KiLwyjI/A21g==} + p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -9674,6 +10602,9 @@ packages: papaparse@5.5.3: resolution: {integrity: sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==} + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -9681,6 +10612,10 @@ packages: parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + parse-srcset@1.0.2: resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} @@ -9697,6 +10632,12 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-case@3.0.4: + resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} + path-data-parser@0.1.0: resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} @@ -9708,6 +10649,10 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -9965,6 +10910,10 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} + proc-log@4.2.0: + resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -9985,6 +10934,9 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -10019,6 +10971,10 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -10146,6 +11102,10 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + registry-auth-token@5.1.1: + resolution: {integrity: sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==} + engines: {node: '>=14'} + rehype-harden@1.1.7: resolution: {integrity: sha512-j5DY0YSK2YavvNGV+qBHma15J9m0WZmRe8posT5AtKDS6TNWtMVTo6RiqF8SidfcASYz8f3k2J/1RWmq5zTXUw==} @@ -10224,6 +11184,9 @@ packages: '@react-email/render': optional: true + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -10244,6 +11207,10 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -10284,6 +11251,10 @@ packages: rss-parser@3.13.0: resolution: {integrity: sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==} + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -10351,6 +11322,9 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} + sentence-case@3.0.4: + resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} + seq-queue@0.0.5: resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} @@ -10441,12 +11415,22 @@ packages: resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} engines: {node: '>= 18'} + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + sonner@2.0.7: resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} peerDependencies: react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + sort-object-keys@1.1.3: + resolution: {integrity: sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==} + + sort-package-json@2.15.1: + resolution: {integrity: sha512-9x9+o8krTT2saA9liI4BljNjwAbvUnWf11Wq+i/iZt8nl2UGYnf3TH5uBydE7VALmP7AGwlfszuEeL8BDyb0YA==} + hasBin: true + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -10471,6 +11455,18 @@ packages: spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -10628,6 +11624,12 @@ packages: strnum@2.1.2: resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} + stubborn-fs@2.0.0: + resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} + + stubborn-utils@1.0.2: + resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==} + style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -10659,6 +11661,10 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -10737,6 +11743,9 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tiny-jsonc@1.0.2: + resolution: {integrity: sha512-f5QDAfLq6zIVSyCZQZhhyl0QS6MvAyTxgz4X4x3+EoCktNWEYJ6PeoEA97fyb98njpBNNi88ybpD7m+BDFXaCw==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -10936,6 +11945,10 @@ packages: resolution: {integrity: sha512-E67Chw7SxFe++uotisxt/xzB1UxxvLztzzQqVyUZ/jKujsejVqvoO5vn25oMvqJydqYrASBVBCQCy082E2qQYQ==} hasBin: true + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + type-fest@0.7.1: resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} engines: {node: '>=8'} @@ -10986,6 +11999,10 @@ packages: ufo@1.6.2: resolution: {integrity: sha512-heMioaxBcG9+Znsda5Q8sQbWnLJSl98AFDXTO80wELWEzX3hordXsTdxrIfMQoO9IY1MEnoGoPjpoKpMj+Yx0Q==} + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -11058,6 +12075,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + upper-case-first@2.0.2: + resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} + + upper-case@2.0.2: + resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -11104,6 +12127,13 @@ packages: typescript: optional: true + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + validate-npm-package-name@7.0.2: resolution: {integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==} engines: {node: ^20.17.0 || >=22.9.0} @@ -11273,6 +12303,9 @@ packages: resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} engines: {node: '>=20'} + when-exit@2.1.5: + resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -11304,6 +12337,10 @@ packages: engines: {node: '>=8'} hasBin: true + widest-line@3.1.0: + resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} + engines: {node: '>=8'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -11316,6 +12353,9 @@ packages: resolution: {integrity: sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==} engines: {node: '>=0.4.0'} + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -11343,6 +12383,10 @@ packages: utf-8-validate: optional: true + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} @@ -11385,6 +12429,11 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yarn@1.22.22: + resolution: {integrity: sha512-prL3kGtyG7o9Z9Sv8IPfBNrWTDmXB4Qbes8A9rEzt6wkJV8mUvoirjU0Mp3GGAU06Y0XQyA3/2/RQFVuK7MTfg==} + engines: {node: '>=4.0.0'} + hasBin: true + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -11620,6 +12669,537 @@ snapshots: '@asamuzakjp/nwsapi@2.3.9': {} + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.969.0 + tslib: 2.8.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.969.0 + tslib: 2.8.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-locate-window': 3.965.2 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-locate-window': 3.965.2 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.969.0 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-cloudfront@3.971.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/credential-provider-node': 3.971.0 + '@aws-sdk/middleware-host-header': 3.969.0 + '@aws-sdk/middleware-logger': 3.969.0 + '@aws-sdk/middleware-recursion-detection': 3.969.0 + '@aws-sdk/middleware-user-agent': 3.970.0 + '@aws-sdk/region-config-resolver': 3.969.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-endpoints': 3.970.0 + '@aws-sdk/util-user-agent-browser': 3.969.0 + '@aws-sdk/util-user-agent-node': 3.971.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/core': 3.20.7 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/hash-node': 4.2.8 + '@smithy/invalid-dependency': 4.2.8 + '@smithy/middleware-content-length': 4.2.8 + '@smithy/middleware-endpoint': 4.4.8 + '@smithy/middleware-retry': 4.4.24 + '@smithy/middleware-serde': 4.2.9 + '@smithy/middleware-stack': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/node-http-handler': 4.4.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.23 + '@smithy/util-defaults-mode-node': 4.2.26 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + '@smithy/util-waiter': 4.2.8 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-s3@3.971.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/credential-provider-node': 3.971.0 + '@aws-sdk/middleware-bucket-endpoint': 3.969.0 + '@aws-sdk/middleware-expect-continue': 3.969.0 + '@aws-sdk/middleware-flexible-checksums': 3.971.0 + '@aws-sdk/middleware-host-header': 3.969.0 + '@aws-sdk/middleware-location-constraint': 3.969.0 + '@aws-sdk/middleware-logger': 3.969.0 + '@aws-sdk/middleware-recursion-detection': 3.969.0 + '@aws-sdk/middleware-sdk-s3': 3.970.0 + '@aws-sdk/middleware-ssec': 3.971.0 + '@aws-sdk/middleware-user-agent': 3.970.0 + '@aws-sdk/region-config-resolver': 3.969.0 + '@aws-sdk/signature-v4-multi-region': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-endpoints': 3.970.0 + '@aws-sdk/util-user-agent-browser': 3.969.0 + '@aws-sdk/util-user-agent-node': 3.971.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/core': 3.20.7 + '@smithy/eventstream-serde-browser': 4.2.8 + '@smithy/eventstream-serde-config-resolver': 4.3.8 + '@smithy/eventstream-serde-node': 4.2.8 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/hash-blob-browser': 4.2.9 + '@smithy/hash-node': 4.2.8 + '@smithy/hash-stream-node': 4.2.8 + '@smithy/invalid-dependency': 4.2.8 + '@smithy/md5-js': 4.2.8 + '@smithy/middleware-content-length': 4.2.8 + '@smithy/middleware-endpoint': 4.4.8 + '@smithy/middleware-retry': 4.4.24 + '@smithy/middleware-serde': 4.2.9 + '@smithy/middleware-stack': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/node-http-handler': 4.4.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.23 + '@smithy/util-defaults-mode-node': 4.2.26 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + '@smithy/util-waiter': 4.2.8 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso@3.971.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/middleware-host-header': 3.969.0 + '@aws-sdk/middleware-logger': 3.969.0 + '@aws-sdk/middleware-recursion-detection': 3.969.0 + '@aws-sdk/middleware-user-agent': 3.970.0 + '@aws-sdk/region-config-resolver': 3.969.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-endpoints': 3.970.0 + '@aws-sdk/util-user-agent-browser': 3.969.0 + '@aws-sdk/util-user-agent-node': 3.971.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/core': 3.20.7 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/hash-node': 4.2.8 + '@smithy/invalid-dependency': 4.2.8 + '@smithy/middleware-content-length': 4.2.8 + '@smithy/middleware-endpoint': 4.4.8 + '@smithy/middleware-retry': 4.4.24 + '@smithy/middleware-serde': 4.2.9 + '@smithy/middleware-stack': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/node-http-handler': 4.4.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.23 + '@smithy/util-defaults-mode-node': 4.2.26 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.970.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@aws-sdk/xml-builder': 3.969.0 + '@smithy/core': 3.20.7 + '@smithy/node-config-provider': 4.3.8 + '@smithy/property-provider': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/signature-v4': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/crc64-nvme@3.969.0': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.970.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.970.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/node-http-handler': 4.4.8 + '@smithy/property-provider': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/util-stream': 4.5.10 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.971.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/credential-provider-env': 3.970.0 + '@aws-sdk/credential-provider-http': 3.970.0 + '@aws-sdk/credential-provider-login': 3.971.0 + '@aws-sdk/credential-provider-process': 3.970.0 + '@aws-sdk/credential-provider-sso': 3.971.0 + '@aws-sdk/credential-provider-web-identity': 3.971.0 + '@aws-sdk/nested-clients': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/credential-provider-imds': 4.2.8 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.971.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/nested-clients': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.971.0': + dependencies: + '@aws-sdk/credential-provider-env': 3.970.0 + '@aws-sdk/credential-provider-http': 3.970.0 + '@aws-sdk/credential-provider-ini': 3.971.0 + '@aws-sdk/credential-provider-process': 3.970.0 + '@aws-sdk/credential-provider-sso': 3.971.0 + '@aws-sdk/credential-provider-web-identity': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/credential-provider-imds': 4.2.8 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.970.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.971.0': + dependencies: + '@aws-sdk/client-sso': 3.971.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/token-providers': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.971.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/nested-clients': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/middleware-bucket-endpoint@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-arn-parser': 3.968.0 + '@smithy/node-config-provider': 4.3.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-config-provider': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.971.0': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/crc64-nvme': 3.969.0 + '@aws-sdk/types': 3.969.0 + '@smithy/is-array-buffer': 4.2.0 + '@smithy/node-config-provider': 4.3.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-location-constraint@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@aws/lambda-invoke-store': 0.2.3 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.970.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-arn-parser': 3.968.0 + '@smithy/core': 3.20.7 + '@smithy/node-config-provider': 4.3.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/signature-v4': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-ssec@3.971.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.970.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-endpoints': 3.970.0 + '@smithy/core': 3.20.7 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.971.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.970.0 + '@aws-sdk/middleware-host-header': 3.969.0 + '@aws-sdk/middleware-logger': 3.969.0 + '@aws-sdk/middleware-recursion-detection': 3.969.0 + '@aws-sdk/middleware-user-agent': 3.970.0 + '@aws-sdk/region-config-resolver': 3.969.0 + '@aws-sdk/types': 3.969.0 + '@aws-sdk/util-endpoints': 3.970.0 + '@aws-sdk/util-user-agent-browser': 3.969.0 + '@aws-sdk/util-user-agent-node': 3.971.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/core': 3.20.7 + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/hash-node': 4.2.8 + '@smithy/invalid-dependency': 4.2.8 + '@smithy/middleware-content-length': 4.2.8 + '@smithy/middleware-endpoint': 4.4.8 + '@smithy/middleware-retry': 4.4.24 + '@smithy/middleware-serde': 4.2.9 + '@smithy/middleware-stack': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/node-http-handler': 4.4.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.23 + '@smithy/util-defaults-mode-node': 4.2.26 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/config-resolver': 4.4.6 + '@smithy/node-config-provider': 4.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.970.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@smithy/protocol-http': 5.3.8 + '@smithy/signature-v4': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.971.0': + dependencies: + '@aws-sdk/core': 3.970.0 + '@aws-sdk/nested-clients': 3.971.0 + '@aws-sdk/types': 3.969.0 + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.969.0': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/util-arn-parser@3.968.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.970.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-endpoints': 3.2.8 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.2': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.969.0': + dependencies: + '@aws-sdk/types': 3.969.0 + '@smithy/types': 4.12.0 + bowser: 2.13.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.971.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.970.0 + '@aws-sdk/types': 3.969.0 + '@smithy/node-config-provider': 4.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.969.0': + dependencies: + '@smithy/types': 4.12.0 + fast-xml-parser: 5.2.5 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.3': {} + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -11641,7 +13221,7 @@ snapshots: '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -11713,7 +13293,7 @@ snapshots: '@babel/parser': 7.28.5 '@babel/template': 7.27.2 '@babel/types': 7.28.5 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -12010,6 +13590,9 @@ snapshots: picocolors: 1.1.1 sisteransi: 1.0.5 + '@colors/colors@1.5.0': + optional: true + '@csstools/color-helpers@5.1.0': {} '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': @@ -12307,7 +13890,7 @@ snapshots: '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -12323,7 +13906,7 @@ snapshots: '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -12474,6 +14057,28 @@ snapshots: '@inquirer/ansi@1.0.2': {} + '@inquirer/checkbox@4.3.2(@types/node@22.19.5)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@22.19.5) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@22.19.5) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.19.5 + + '@inquirer/confirm@3.2.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 + + '@inquirer/confirm@5.1.21(@types/node@22.19.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.19.5) + '@inquirer/type': 3.0.10(@types/node@22.19.5) + optionalDependencies: + '@types/node': 22.19.5 + '@inquirer/confirm@5.1.21(@types/node@25.0.3)': dependencies: '@inquirer/core': 10.3.2(@types/node@25.0.3) @@ -12481,6 +14086,19 @@ snapshots: optionalDependencies: '@types/node': 25.0.3 + '@inquirer/core@10.3.2(@types/node@22.19.5)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@22.19.5) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.19.5 + '@inquirer/core@10.3.2(@types/node@25.0.3)': dependencies: '@inquirer/ansi': 1.0.2 @@ -12494,6 +14112,44 @@ snapshots: optionalDependencies: '@types/node': 25.0.3 + '@inquirer/core@9.2.1': + dependencies: + '@inquirer/figures': 1.0.15 + '@inquirer/type': 2.0.0 + '@types/mute-stream': 0.0.4 + '@types/node': 22.19.5 + '@types/wrap-ansi': 3.0.0 + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 1.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + + '@inquirer/editor@4.2.23(@types/node@22.19.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.19.5) + '@inquirer/external-editor': 1.0.3(@types/node@22.19.5) + '@inquirer/type': 3.0.10(@types/node@22.19.5) + optionalDependencies: + '@types/node': 22.19.5 + + '@inquirer/expand@4.0.23(@types/node@22.19.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.19.5) + '@inquirer/type': 3.0.10(@types/node@22.19.5) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.19.5 + + '@inquirer/external-editor@1.0.3(@types/node@22.19.5)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.2 + optionalDependencies: + '@types/node': 22.19.5 + '@inquirer/external-editor@1.0.3(@types/node@25.0.3)': dependencies: chardet: 2.1.1 @@ -12503,6 +14159,95 @@ snapshots: '@inquirer/figures@1.0.15': {} + '@inquirer/input@2.3.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/type': 1.5.5 + + '@inquirer/input@4.3.1(@types/node@22.19.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.19.5) + '@inquirer/type': 3.0.10(@types/node@22.19.5) + optionalDependencies: + '@types/node': 22.19.5 + + '@inquirer/number@3.0.23(@types/node@22.19.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.19.5) + '@inquirer/type': 3.0.10(@types/node@22.19.5) + optionalDependencies: + '@types/node': 22.19.5 + + '@inquirer/password@4.0.23(@types/node@22.19.5)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@22.19.5) + '@inquirer/type': 3.0.10(@types/node@22.19.5) + optionalDependencies: + '@types/node': 22.19.5 + + '@inquirer/prompts@7.10.1(@types/node@22.19.5)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@22.19.5) + '@inquirer/confirm': 5.1.21(@types/node@22.19.5) + '@inquirer/editor': 4.2.23(@types/node@22.19.5) + '@inquirer/expand': 4.0.23(@types/node@22.19.5) + '@inquirer/input': 4.3.1(@types/node@22.19.5) + '@inquirer/number': 3.0.23(@types/node@22.19.5) + '@inquirer/password': 4.0.23(@types/node@22.19.5) + '@inquirer/rawlist': 4.1.11(@types/node@22.19.5) + '@inquirer/search': 3.2.2(@types/node@22.19.5) + '@inquirer/select': 4.4.2(@types/node@22.19.5) + optionalDependencies: + '@types/node': 22.19.5 + + '@inquirer/rawlist@4.1.11(@types/node@22.19.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.19.5) + '@inquirer/type': 3.0.10(@types/node@22.19.5) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.19.5 + + '@inquirer/search@3.2.2(@types/node@22.19.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@22.19.5) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@22.19.5) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.19.5 + + '@inquirer/select@2.5.0': + dependencies: + '@inquirer/core': 9.2.1 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 1.5.5 + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.3 + + '@inquirer/select@4.4.2(@types/node@22.19.5)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@22.19.5) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@22.19.5) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.19.5 + + '@inquirer/type@1.5.5': + dependencies: + mute-stream: 1.0.0 + + '@inquirer/type@2.0.0': + dependencies: + mute-stream: 1.0.0 + + '@inquirer/type@3.0.10(@types/node@22.19.5)': + optionalDependencies: + '@types/node': 22.19.5 + '@inquirer/type@3.0.10(@types/node@25.0.3)': optionalDependencies: '@types/node': 25.0.3 @@ -12707,6 +14452,76 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@oclif/core@4.8.0': + dependencies: + ansi-escapes: 4.3.2 + ansis: 3.17.0 + clean-stack: 3.0.1 + cli-spinners: 2.9.2 + debug: 4.4.3(supports-color@8.1.1) + ejs: 3.1.10 + get-package-type: 0.1.0 + indent-string: 4.0.0 + is-wsl: 2.2.0 + lilconfig: 3.1.3 + minimatch: 9.0.5 + semver: 7.7.3 + string-width: 4.2.3 + supports-color: 8.1.1 + tinyglobby: 0.2.15 + widest-line: 3.1.0 + wordwrap: 1.0.0 + wrap-ansi: 7.0.0 + + '@oclif/plugin-autocomplete@3.2.39': + dependencies: + '@oclif/core': 4.8.0 + ansis: 3.17.0 + debug: 4.4.3(supports-color@8.1.1) + ejs: 3.1.10 + transitivePeerDependencies: + - supports-color + + '@oclif/plugin-help@6.2.36': + dependencies: + '@oclif/core': 4.8.0 + + '@oclif/plugin-not-found@3.2.73(@types/node@22.19.5)': + dependencies: + '@inquirer/prompts': 7.10.1(@types/node@22.19.5) + '@oclif/core': 4.8.0 + ansis: 3.17.0 + fast-levenshtein: 3.0.0 + transitivePeerDependencies: + - '@types/node' + + '@oclif/plugin-plugins@5.4.54': + dependencies: + '@oclif/core': 4.8.0 + ansis: 3.17.0 + debug: 4.4.3(supports-color@8.1.1) + npm: 10.9.4 + npm-package-arg: 11.0.3 + npm-run-path: 5.3.0 + object-treeify: 4.0.1 + semver: 7.7.3 + validate-npm-package-name: 5.0.1 + which: 4.0.0 + yarn: 1.22.22 + transitivePeerDependencies: + - supports-color + + '@oclif/plugin-warn-if-update-available@3.1.53': + dependencies: + '@oclif/core': 4.8.0 + ansis: 3.17.0 + debug: 4.4.3(supports-color@8.1.1) + http-call: 5.3.0 + lodash: 4.17.21 + registry-auth-token: 5.1.1 + transitivePeerDependencies: + - supports-color + '@open-draft/deferred-promise@2.2.0': {} '@open-draft/logger@0.3.0': @@ -12786,6 +14601,18 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@3.0.2': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + '@prisma/client-runtime-utils@7.2.0': optional: true @@ -13106,6 +14933,346 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@sindresorhus/is@5.6.0': {} + + '@smithy/abort-controller@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader-native@4.2.1': + dependencies: + '@smithy/util-base64': 4.3.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader@5.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/config-resolver@4.4.6': + dependencies: + '@smithy/node-config-provider': 4.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-config-provider': 4.2.0 + '@smithy/util-endpoints': 3.2.8 + '@smithy/util-middleware': 4.2.8 + tslib: 2.8.1 + + '@smithy/core@3.20.7': + dependencies: + '@smithy/middleware-serde': 4.2.9 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.2.8': + dependencies: + '@smithy/node-config-provider': 4.3.8 + '@smithy/property-provider': 4.2.8 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.2.8': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.12.0 + '@smithy/util-hex-encoding': 4.2.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.2.8': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.3.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.2.8': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.2.8': + dependencies: + '@smithy/eventstream-codec': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.3.9': + dependencies: + '@smithy/protocol-http': 5.3.8 + '@smithy/querystring-builder': 4.2.8 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + tslib: 2.8.1 + + '@smithy/hash-blob-browser@4.2.9': + dependencies: + '@smithy/chunked-blob-reader': 5.2.0 + '@smithy/chunked-blob-reader-native': 4.2.1 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/hash-stream-node@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/md5-js@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.2.8': + dependencies: + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.4.8': + dependencies: + '@smithy/core': 3.20.7 + '@smithy/middleware-serde': 4.2.9 + '@smithy/node-config-provider': 4.3.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-middleware': 4.2.8 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.4.24': + dependencies: + '@smithy/node-config-provider': 4.3.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/service-error-classification': 4.2.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-retry': 4.2.8 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + + '@smithy/middleware-serde@4.2.9': + dependencies: + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.3.8': + dependencies: + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.4.8': + dependencies: + '@smithy/abort-controller': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/querystring-builder': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/protocol-http@5.3.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + '@smithy/util-uri-escape': 4.2.0 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + + '@smithy/shared-ini-file-loader@4.4.3': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.8': + dependencies: + '@smithy/is-array-buffer': 4.2.0 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-hex-encoding': 4.2.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-uri-escape': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/smithy-client@4.10.9': + dependencies: + '@smithy/core': 3.20.7 + '@smithy/middleware-endpoint': 4.4.8 + '@smithy/middleware-stack': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-stream': 4.5.10 + tslib: 2.8.1 + + '@smithy/types@4.12.0': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.2.8': + dependencies: + '@smithy/querystring-parser': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-base64@4.3.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.2.1': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.2.0': + dependencies: + '@smithy/is-array-buffer': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.3.23': + dependencies: + '@smithy/property-provider': 4.2.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.2.26': + dependencies: + '@smithy/config-resolver': 4.4.6 + '@smithy/credential-provider-imds': 4.2.8 + '@smithy/node-config-provider': 4.3.8 + '@smithy/property-provider': 4.2.8 + '@smithy/smithy-client': 4.10.9 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.2.8': + dependencies: + '@smithy/node-config-provider': 4.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-retry@4.2.8': + dependencies: + '@smithy/service-error-classification': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.10': + dependencies: + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/node-http-handler': 4.4.8 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-hex-encoding': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.2.0': + dependencies: + '@smithy/util-buffer-from': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-waiter@4.2.8': + dependencies: + '@smithy/abort-controller': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + + '@smithy/uuid@1.1.0': + dependencies: + tslib: 2.8.1 + '@stablelib/base64@1.0.1': {} '@standard-schema/spec@1.1.0': {} @@ -13363,6 +15530,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@szmarczak/http-timer@5.0.1': + dependencies: + defer-to-connect: 2.0.1 + '@tailwindcss/typography@0.5.19(tailwindcss@3.4.17)': dependencies: postcss-selector-parser: 6.0.10 @@ -13613,6 +15784,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/http-cache-semantics@4.0.4': {} + '@types/js-yaml@4.0.9': {} '@types/jsdom@27.0.0': @@ -13645,6 +15818,10 @@ snapshots: '@types/mustache@4.2.6': {} + '@types/mute-stream@0.0.4': + dependencies: + '@types/node': 25.0.3 + '@types/node@12.20.55': {} '@types/node@20.19.27': @@ -13728,6 +15905,8 @@ snapshots: dependencies: '@types/webidl-conversions': 7.0.3 + '@types/wrap-ansi@3.0.0': {} + '@types/ws@8.18.1': dependencies: '@types/node': 25.0.3 @@ -13754,7 +15933,7 @@ snapshots: '@typescript-eslint/types': 8.52.0 '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.52.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -13764,7 +15943,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) '@typescript-eslint/types': 8.52.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -13783,7 +15962,7 @@ snapshots: '@typescript-eslint/types': 8.52.0 '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) '@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 @@ -13798,7 +15977,7 @@ snapshots: '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) '@typescript-eslint/types': 8.52.0 '@typescript-eslint/visitor-keys': 8.52.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) minimatch: 9.0.5 semver: 7.7.3 tinyglobby: 0.2.15 @@ -14073,6 +16252,10 @@ snapshots: ansi-colors@4.1.3: {} + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -14085,6 +16268,8 @@ snapshots: ansi-styles@6.2.3: {} + ansis@3.17.0: {} + any-promise@1.3.0: {} anymatch@3.1.3: @@ -14203,6 +16388,11 @@ snapshots: asynckit@0.4.0: {} + atomically@2.1.0: + dependencies: + stubborn-fs: 2.0.0 + when-exit: 2.1.5 + autoprefixer@10.4.23(postcss@8.5.6): dependencies: browserslist: 4.28.1 @@ -14327,7 +16517,7 @@ snapshots: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) http-errors: 2.0.1 iconv-lite: 0.7.2 on-finished: 2.4.1 @@ -14339,6 +16529,8 @@ snapshots: boolbase@1.0.0: {} + bowser@2.13.1: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -14376,6 +16568,10 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + bundle-require@5.1.0(esbuild@0.27.2): dependencies: esbuild: 0.27.2 @@ -14400,6 +16596,18 @@ snapshots: cac@6.7.14: {} + cacheable-lookup@7.0.0: {} + + cacheable-request@10.2.14: + dependencies: + '@types/http-cache-semantics': 4.0.4 + get-stream: 6.0.1 + http-cache-semantics: 4.2.0 + keyv: 4.5.4 + mimic-response: 4.0.0 + normalize-url: 8.1.1 + responselike: 3.0.0 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -14419,6 +16627,11 @@ snapshots: callsites@3.1.0: {} + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + camelcase-css@2.0.1: {} camera-controls@3.1.2(three@0.182.0): @@ -14427,6 +16640,12 @@ snapshots: caniuse-lite@1.0.30001763: {} + capital-case@1.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + upper-case-first: 2.0.2 + caseless@0.12.0: {} ccount@2.0.1: {} @@ -14453,6 +16672,21 @@ snapshots: chalk@5.6.2: {} + change-case@4.1.2: + dependencies: + camel-case: 4.1.2 + capital-case: 1.0.4 + constant-case: 3.0.4 + dot-case: 3.0.4 + header-case: 2.0.4 + no-case: 3.0.4 + param-case: 3.0.4 + pascal-case: 3.1.2 + path-case: 3.0.4 + sentence-case: 3.0.4 + snake-case: 3.0.4 + tslib: 2.8.1 + character-entities-html4@2.1.0: {} character-entities-legacy@3.0.0: {} @@ -14534,6 +16768,10 @@ snapshots: dependencies: consola: 3.4.2 + clean-stack@3.0.1: + dependencies: + escape-string-regexp: 4.0.0 + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -14542,6 +16780,12 @@ snapshots: cli-spinners@3.3.0: {} + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + cli-width@4.1.0: {} client-only@0.0.1: {} @@ -14580,12 +16824,35 @@ snapshots: concat-map@0.0.1: {} + conf@13.1.0: + dependencies: + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + atomically: 2.1.0 + debounce-fn: 6.0.0 + dot-prop: 9.0.0 + env-paths: 3.0.0 + json-schema-typed: 8.0.2 + semver: 7.7.3 + uint8array-extras: 1.5.0 + confbox@0.1.8: {} confbox@0.2.2: {} + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + consola@3.4.2: {} + constant-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + upper-case: 2.0.2 + content-disposition@1.0.1: {} content-type@1.0.5: {} @@ -14868,13 +17135,19 @@ snapshots: dayjs@1.11.19: {} + debounce-fn@6.0.0: + dependencies: + mimic-function: 5.0.1 + debug@3.2.7: dependencies: ms: 2.1.3 - debug@4.4.3: + debug@4.4.3(supports-color@8.1.1): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 decimal.js@10.6.0: {} @@ -14896,6 +17169,15 @@ snapshots: deepmerge@4.3.1: {} + default-browser-id@5.0.1: {} + + default-browser@5.4.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + defer-to-connect@2.0.1: {} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -14904,6 +17186,8 @@ snapshots: define-lazy-prop@2.0.0: {} + define-lazy-prop@3.0.0: {} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 @@ -14954,8 +17238,12 @@ snapshots: detect-indent@6.1.0: {} + detect-indent@7.0.2: {} + detect-libc@2.1.2: {} + detect-newline@4.0.1: {} + devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -15004,6 +17292,15 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dot-prop@9.0.0: + dependencies: + type-fest: 4.41.0 + dotenv@16.6.1: {} dotenv@17.2.3: {} @@ -15070,6 +17367,10 @@ snapshots: '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 + ejs@3.1.10: + dependencies: + jake: 10.9.4 + electron-to-chromium@1.5.267: {} emoji-regex@10.6.0: {} @@ -15102,8 +17403,11 @@ snapshots: entities@6.0.1: {} - env-paths@3.0.0: - optional: true + env-paths@3.0.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 es-abstract@1.24.1: dependencies: @@ -15210,7 +17514,7 @@ snapshots: esbuild-register@3.6.0(esbuild@0.25.12): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) esbuild: 0.25.12 transitivePeerDependencies: - supports-color @@ -15311,8 +17615,8 @@ snapshots: '@next/eslint-plugin-next': 16.1.1 eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(eslint@9.39.2(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1)) @@ -15334,10 +17638,10 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.2(jiti@2.6.1) get-tsconfig: 4.13.0 is-bun-module: 2.0.0 @@ -15345,7 +17649,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -15359,13 +17663,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -15398,7 +17702,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -15409,7 +17713,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -15503,7 +17807,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -15581,7 +17885,7 @@ snapshots: content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 @@ -15640,14 +17944,24 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-levenshtein@3.0.0: + dependencies: + fastest-levenshtein: 1.0.16 + fast-sha256@1.3.0: {} fast-uri@3.1.0: {} + fast-xml-parser@5.2.5: + dependencies: + strnum: 2.1.2 + fast-xml-parser@5.3.3: dependencies: strnum: 2.1.2 + fastest-levenshtein@1.0.16: {} + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -15674,13 +17988,17 @@ snapshots: file-uri-to-path@1.0.0: {} + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 finalhandler@2.1.1: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -15699,6 +18017,10 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + find-yarn-workspace-root@2.0.0: + dependencies: + micromatch: 4.0.8 + firecrawl-aisdk@0.7.2: dependencies: '@mendable/firecrawl-js': 4.10.0 @@ -15733,6 +18055,8 @@ snapshots: forever-agent@0.6.1: {} + form-data-encoder@2.1.4: {} + form-data@2.3.3: dependencies: asynckit: 0.4.0 @@ -15801,7 +18125,7 @@ snapshots: gel@2.2.0: dependencies: '@petamoriken/float16': 3.9.3 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) env-paths: 3.0.0 semver: 7.7.3 shell-quote: 1.8.3 @@ -15838,6 +18162,8 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 + get-package-type@0.1.0: {} + get-port-please@3.1.2: optional: true @@ -15846,6 +18172,10 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-stdin@9.0.0: {} + + get-stream@6.0.1: {} + get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 @@ -15869,8 +18199,12 @@ snapshots: nypm: 0.6.2 pathe: 2.0.3 + git-hooks-list@3.2.0: {} + github-from-package@0.0.0: {} + github-slugger@2.0.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -15920,6 +18254,22 @@ snapshots: gopd@1.2.0: {} + got@13.0.0: + dependencies: + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 + decompress-response: 6.0.0 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 + + graceful-fs@4.2.10: {} + graceful-fs@4.2.11: {} grammex@3.1.12: @@ -16099,6 +18449,11 @@ snapshots: he@1.2.0: {} + header-case@2.0.4: + dependencies: + capital-case: 1.0.4 + tslib: 2.8.1 + headers-polyfill@4.0.3: {} hermes-estree@0.25.1: {} @@ -16115,6 +18470,10 @@ snapshots: hono@4.10.6: {} + hosted-git-info@7.0.2: + dependencies: + lru-cache: 10.4.3 + html-encoding-sniffer@3.0.0: dependencies: whatwg-encoding: 2.0.0 @@ -16136,6 +18495,19 @@ snapshots: domutils: 3.2.2 entities: 4.5.0 + http-cache-semantics@4.2.0: {} + + http-call@5.3.0: + dependencies: + content-type: 1.0.5 + debug: 4.4.3(supports-color@8.1.1) + is-retry-allowed: 1.2.0 + is-stream: 2.0.1 + parse-json: 4.0.0 + tunnel-agent: 0.6.0 + transitivePeerDependencies: + - supports-color + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -16147,7 +18519,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -16187,10 +18559,15 @@ snapshots: http-status-codes@2.3.0: optional: true + http2-wrapper@2.2.1: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -16261,6 +18638,8 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-arrayish@0.2.1: {} + is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -16309,6 +18688,8 @@ snapshots: is-docker@2.2.1: {} + is-docker@3.0.0: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -16331,6 +18712,10 @@ snapshots: is-hexadecimal@2.0.1: {} + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + is-installed-globally@1.0.0: dependencies: global-directory: 4.0.1 @@ -16373,12 +18758,16 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + is-retry-allowed@1.2.0: {} + is-set@2.0.3: {} is-shared-array-buffer@1.0.4: dependencies: call-bound: 1.0.4 + is-stream@2.0.1: {} + is-string@1.1.1: dependencies: call-bound: 1.0.4 @@ -16421,12 +18810,15 @@ snapshots: dependencies: is-docker: 2.2.1 + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + isarray@2.0.5: {} isexe@2.0.0: {} - isexe@3.1.1: - optional: true + isexe@3.1.1: {} isomorphic-dompurify@2.35.0: dependencies: @@ -16463,6 +18855,12 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.4 + picocolors: 1.1.1 + jiti@1.21.7: {} jiti@2.6.1: {} @@ -16520,6 +18918,8 @@ snapshots: json-buffer@3.0.1: {} + json-parse-better-errors@1.0.2: {} + json-promise@1.1.8: dependencies: bluebird: 3.7.2 @@ -16787,6 +19187,12 @@ snapshots: loupe@3.2.1: {} + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lowercase-keys@3.0.0: {} + lowlight@1.20.0: dependencies: fault: 1.0.4 @@ -17256,7 +19662,7 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -17298,6 +19704,8 @@ snapshots: mimic-response@3.1.0: {} + mimic-response@4.0.0: {} + min-indent@1.0.1: {} minimatch@10.1.1: @@ -17308,6 +19716,10 @@ snapshots: dependencies: brace-expansion: 1.1.12 + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.2 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 @@ -17371,7 +19783,7 @@ snapshots: mquery@5.0.0: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -17406,6 +19818,8 @@ snapshots: mustache@4.2.0: {} + mute-stream@1.0.0: {} + mute-stream@2.0.0: {} mysql2@3.15.3: @@ -17505,6 +19919,11 @@ snapshots: - '@babel/core' - babel-plugin-macros + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + node-abi@3.85.0: dependencies: semver: 7.7.3 @@ -17513,8 +19932,29 @@ snapshots: node-releases@2.0.27: {} + normalize-package-data@6.0.2: + dependencies: + hosted-git-info: 7.0.2 + semver: 7.7.3 + validate-npm-package-license: 3.0.4 + normalize-path@3.0.0: {} + normalize-url@8.1.1: {} + + npm-package-arg@11.0.3: + dependencies: + hosted-git-info: 7.0.2 + proc-log: 4.2.0 + semver: 7.7.3 + validate-npm-package-name: 5.0.1 + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + npm@10.9.4: {} + nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -17537,6 +19977,8 @@ snapshots: object-keys@1.1.1: {} + object-treeify@4.0.1: {} + object.assign@4.1.7: dependencies: call-bind: 1.0.8 @@ -17575,6 +20017,37 @@ snapshots: obug@2.1.1: {} + oclif@4.22.65(@types/node@22.19.5): + dependencies: + '@aws-sdk/client-cloudfront': 3.971.0 + '@aws-sdk/client-s3': 3.971.0 + '@inquirer/confirm': 3.2.0 + '@inquirer/input': 2.3.0 + '@inquirer/select': 2.5.0 + '@oclif/core': 4.8.0 + '@oclif/plugin-help': 6.2.36 + '@oclif/plugin-not-found': 3.2.73(@types/node@22.19.5) + '@oclif/plugin-warn-if-update-available': 3.1.53 + ansis: 3.17.0 + async-retry: 1.3.3 + change-case: 4.1.2 + debug: 4.4.3(supports-color@8.1.1) + ejs: 3.1.10 + find-yarn-workspace-root: 2.0.0 + fs-extra: 8.1.0 + github-slugger: 2.0.0 + got: 13.0.0 + lodash: 4.17.21 + normalize-package-data: 6.0.2 + semver: 7.7.3 + sort-package-json: 2.15.1 + tiny-jsonc: 1.0.2 + validate-npm-package-name: 5.0.1 + transitivePeerDependencies: + - '@types/node' + - aws-crt + - supports-color + ohash@2.0.11: {} on-finished@2.4.1: @@ -17597,6 +20070,13 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 + open@10.2.0: + dependencies: + default-browser: 5.4.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + open@8.4.2: dependencies: define-lazy-prop: 2.0.0 @@ -17686,6 +20166,8 @@ snapshots: '@oxc-resolver/binding-win32-ia32-msvc': 11.16.2 '@oxc-resolver/binding-win32-x64-msvc': 11.16.2 + p-cancelable@3.0.0: {} + p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -17728,6 +20210,11 @@ snapshots: papaparse@5.5.3: {} + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -17742,6 +20229,11 @@ snapshots: is-decimal: 2.0.1 is-hexadecimal: 2.0.1 + parse-json@4.0.0: + dependencies: + error-ex: 1.3.4 + json-parse-better-errors: 1.0.2 + parse-srcset@1.0.2: {} parse5-htmlparser2-tree-adapter@7.1.0: @@ -17759,12 +20251,24 @@ snapshots: parseurl@1.3.3: {} + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + path-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + path-data-parser@0.1.0: {} path-exists@4.0.0: {} path-key@3.1.1: {} + path-key@4.0.0: {} + path-parse@1.0.7: {} path-scurry@1.11.1: @@ -17866,7 +20370,7 @@ snapshots: portfinder@1.0.38: dependencies: async: 3.2.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -18000,6 +20504,8 @@ snapshots: prismjs@1.30.0: {} + proc-log@4.2.0: {} + process@0.11.10: {} promise-worker-transferable@1.0.4: @@ -18027,6 +20533,8 @@ snapshots: property-information@7.1.0: {} + proto-list@1.2.4: {} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -18057,6 +20565,8 @@ snapshots: queue-microtask@1.2.3: {} + quick-lru@5.1.1: {} + range-parser@1.2.1: {} raw-body@3.0.2: @@ -18238,6 +20748,10 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + registry-auth-token@5.1.1: + dependencies: + '@pnpm/npm-conf': 3.0.2 + rehype-harden@1.1.7: dependencies: unist-util-visit: 5.0.0 @@ -18366,6 +20880,8 @@ snapshots: dependencies: svix: 1.84.1 + resolve-alpn@1.2.1: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -18384,6 +20900,10 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + responselike@3.0.0: + dependencies: + lowercase-keys: 3.0.0 + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -18442,7 +20962,7 @@ snapshots: router@2.2.0: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -18455,6 +20975,8 @@ snapshots: entities: 2.2.0 xml2js: 0.5.0 + run-applescript@7.1.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -18521,7 +21043,7 @@ snapshots: send@1.2.1: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -18535,6 +21057,12 @@ snapshots: transitivePeerDependencies: - supports-color + sentence-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + upper-case-first: 2.0.2 + seq-queue@0.0.5: optional: true @@ -18676,11 +21204,29 @@ snapshots: smol-toml@1.6.0: {} + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + sonner@2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) + sort-object-keys@1.1.3: {} + + sort-package-json@2.15.1: + dependencies: + detect-indent: 7.0.2 + detect-newline: 4.0.1 + get-stdin: 9.0.0 + git-hooks-list: 3.2.0 + is-plain-obj: 4.1.0 + semver: 7.7.3 + sort-object-keys: 1.1.3 + tinyglobby: 0.2.15 + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -18703,6 +21249,20 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.22 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.22 + + spdx-license-ids@3.0.22: {} + split2@4.2.0: {} sprintf-js@1.0.3: {} @@ -18908,6 +21468,12 @@ snapshots: strnum@2.1.2: {} + stubborn-fs@2.0.0: + dependencies: + stubborn-utils: 1.0.2 + + stubborn-utils@1.0.2: {} + style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -18937,6 +21503,10 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} suspend-react@0.1.3(react@19.2.3): @@ -19036,6 +21606,8 @@ snapshots: tiny-invariant@1.3.3: {} + tiny-jsonc@1.0.2: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -19140,7 +21712,7 @@ snapshots: cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) esbuild: 0.27.2 fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 @@ -19240,10 +21812,11 @@ snapshots: transitivePeerDependencies: - typescript + type-fest@0.21.3: {} + type-fest@0.7.1: {} - type-fest@4.41.0: - optional: true + type-fest@4.41.0: {} type-fest@5.3.1: dependencies: @@ -19305,6 +21878,8 @@ snapshots: ufo@1.6.2: {} + uint8array-extras@1.5.0: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -19412,6 +21987,14 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + upper-case-first@2.0.2: + dependencies: + tslib: 2.8.1 + + upper-case@2.0.2: + dependencies: + tslib: 2.8.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -19447,6 +22030,13 @@ snapshots: typescript: 5.9.3 optional: true + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + validate-npm-package-name@5.0.1: {} + validate-npm-package-name@7.0.2: {} vary@1.1.2: {} @@ -19583,6 +22173,8 @@ snapshots: tr46: 6.0.0 webidl-conversions: 8.0.1 + when-exit@2.1.5: {} + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -19631,19 +22223,24 @@ snapshots: which@4.0.0: dependencies: isexe: 3.1.1 - optional: true why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 + widest-line@3.1.0: + dependencies: + string-width: 4.2.3 + word-wrap@1.2.5: {} wordnet-db@3.1.14: {} wordwrap@0.0.3: {} + wordwrap@1.0.0: {} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -19666,6 +22263,10 @@ snapshots: ws@8.19.0: {} + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.0 + xml-name-validator@5.0.0: {} xml2js@0.5.0: @@ -19699,6 +22300,8 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yarn@1.22.22: {} + yocto-queue@0.1.0: {} yocto-queue@1.2.2: {} diff --git a/scripts/migrate-use-cases-to-scenarios.ts b/scripts/migrate-use-cases-to-scenarios.ts new file mode 100644 index 0000000..58a2996 --- /dev/null +++ b/scripts/migrate-use-cases-to-scenarios.ts @@ -0,0 +1,184 @@ +/** + * Migration script: Convert existing Collection.useCases (JSON) to Scenario records + * + * This script: + * 1. Finds all collections with useCases + * 2. Creates a Scenario for each use case + * 3. Generates embeddings for similarity detection + * 4. Generates AI tags for categorization + * + * Run with: npx tsx scripts/migrate-use-cases-to-scenarios.ts + */ + +// Direct import since this script runs standalone +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +// Use case structure from the existing generator +interface ToolStep { + toolName: string; + packageName: string; + purpose: string; + order: number; +} + +interface UseCase { + id: string; + userPrompt: string; + description: string; + toolSequence: ToolStep[]; +} + +async function computeEmbedding(text: string): Promise { + // Skip embedding generation in migration - we'll generate them lazily later + // This keeps the migration fast and doesn't require API keys + console.log(` [skip] Embedding generation deferred for: "${text.slice(0, 50)}..."`); + return null; +} + +async function generateTags(prompt: string, description: string): Promise { + // Generate basic tags from the prompt/description + // Real AI-based tag generation will happen when scenarios are viewed + const words = `${prompt} ${description}`.toLowerCase(); + const tags: string[] = []; + + // Simple keyword extraction + if (words.includes('scrape') || words.includes('crawl') || words.includes('fetch')) { + tags.push('web-scraping'); + } + if (words.includes('api') || words.includes('endpoint')) { + tags.push('api'); + } + if (words.includes('data') || words.includes('extract')) { + tags.push('data-extraction'); + } + if (words.includes('search') || words.includes('find')) { + tags.push('search'); + } + if (words.includes('code') || words.includes('debug') || words.includes('fix')) { + tags.push('development'); + } + if (words.includes('file') || words.includes('document')) { + tags.push('files'); + } + if (words.includes('image') || words.includes('screenshot')) { + tags.push('media'); + } + if (words.includes('email') || words.includes('message')) { + tags.push('communication'); + } + if (words.includes('monitor') || words.includes('track')) { + tags.push('monitoring'); + } + if (words.includes('automate') || words.includes('workflow')) { + tags.push('automation'); + } + + return tags.length > 0 ? tags : ['general']; +} + +async function migrateUseCases() { + console.log('Starting use cases to scenarios migration...\n'); + + // Find all collections with useCases + const collections = await prisma.collection.findMany({ + where: { + useCases: { not: null }, + }, + select: { + id: true, + name: true, + useCases: true, + }, + }); + + console.log(`Found ${collections.length} collections with use cases to migrate.\n`); + + let totalMigrated = 0; + let totalSkipped = 0; + let totalErrors = 0; + + for (const collection of collections) { + console.log(`Processing collection: "${collection.name}" (${collection.id})`); + + const useCases = collection.useCases as { useCases: UseCase[] } | null; + if (!useCases || !useCases.useCases || !Array.isArray(useCases.useCases)) { + console.log(` [skip] No valid useCases array found\n`); + totalSkipped++; + continue; + } + + for (const useCase of useCases.useCases) { + try { + // Check if scenario already exists for this prompt + const existing = await prisma.scenario.findFirst({ + where: { + collectionId: collection.id, + prompt: useCase.userPrompt, + }, + }); + + if (existing) { + console.log( + ` [skip] Scenario already exists for: "${useCase.userPrompt.slice(0, 40)}..."` + ); + totalSkipped++; + continue; + } + + // Generate tags + const tags = await generateTags(useCase.userPrompt, useCase.description); + + // Create the scenario + const scenario = await prisma.scenario.create({ + data: { + collectionId: collection.id, + prompt: useCase.userPrompt, + name: useCase.description, + description: `Migrated from legacy use case: ${useCase.id}. Tool sequence: ${useCase.toolSequence.map((t) => t.toolName).join(' → ')}`, + tags, + }, + }); + + console.log(` [created] Scenario: "${useCase.description.slice(0, 50)}..."`); + totalMigrated++; + + // Optionally generate embedding (deferred for now) + const embedding = await computeEmbedding(useCase.userPrompt); + if (embedding) { + await prisma.scenarioEmbedding.create({ + data: { + scenarioId: scenario.id, + embedding: embedding as unknown as object, + }, + }); + } + } catch (error) { + console.error(` [error] Failed to migrate use case "${useCase.id}":`, error); + totalErrors++; + } + } + + console.log(''); + } + + console.log('Migration complete!'); + console.log(` Total migrated: ${totalMigrated}`); + console.log(` Total skipped: ${totalSkipped}`); + console.log(` Total errors: ${totalErrors}`); +} + +// Run the migration +migrateUseCases() + .then(() => { + console.log('\nDone.'); + process.exit(0); + }) + .catch((error) => { + console.error('Migration failed:', error); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + });