diff --git a/.cargo/config.toml b/.cargo/config.toml index 3022c2de..09ce5cab 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,8 +1,7 @@ [build] rustflags = ["-C", "target-cpu=native"] -# On macOS, the default linker (ld64) is already quite fast -# For Linux, you could use mold: rustflags = ["-C", "link-arg=-fuse-ld=mold"] +# Note: mold linker doesn't support macOS. Use default ld64 (already fast on Mac) [alias] b = "build" diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..ddc7e530 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,41 @@ +# ============================================================================= +# Docker Build Context Exclusions +# ============================================================================= + +# Build artifacts +target/ +*.rs.bk + +# IDE and editor files +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# Git +.git/ +.gitignore + +# Documentation and planning +.planning/ +*.md +!README.md + +# CI/CD +.github/ + +# Test fixtures +tests/test-*/ + +# Local configuration +.env +.env.* +*.local + +# Logs +*.log + +# OS files +.DS_Store +Thumbs.db diff --git a/Cargo.lock b/Cargo.lock index 947f7f01..c85c7a73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,33 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ag-ui-core" +version = "0.1.0" +dependencies = [ + "json-patch 3.0.1", + "jsonptr 0.6.3", + "serde", + "serde_json", + "thiserror 2.0.17", + "uuid", +] + +[[package]] +name = "ag-ui-server" +version = "0.1.0" +dependencies = [ + "ag-ui-core", + "async-trait", + "axum", + "futures", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tokio-stream", +] + [[package]] name = "ahash" version = "0.8.12" @@ -194,6 +221,17 @@ dependencies = [ "syn", ] +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -601,6 +639,61 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core", + "base64 0.22.1", + "bytes", + "form_urlencoded", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backon" version = "1.6.0" @@ -1204,6 +1297,12 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + [[package]] name = "deluxe" version = "0.5.0" @@ -2897,6 +2996,7 @@ dependencies = [ "http 1.3.1", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "pin-utils", @@ -3351,13 +3451,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr 0.6.3", + "serde", + "serde_json", + "thiserror 1.0.69", +] + [[package]] name = "json-patch" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f300e415e2134745ef75f04562dd0145405c2f7fd92065db029ac4b16b57fe90" dependencies = [ - "jsonptr", + "jsonptr 0.7.1", "serde", "serde_json", "thiserror 1.0.69", @@ -3376,6 +3488,16 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "jsonptr" version = "0.7.1" @@ -3467,7 +3589,7 @@ dependencies = [ "derive_more", "form_urlencoded", "http 1.3.1", - "json-patch", + "json-patch 4.1.0", "k8s-openapi", "schemars", "serde", @@ -3504,7 +3626,7 @@ dependencies = [ "futures", "hashbrown 0.15.3", "hostname", - "json-patch", + "json-patch 4.1.0", "k8s-openapi", "kube-client", "parking_lot", @@ -3623,6 +3745,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "maybe-async" version = "0.2.10" @@ -5036,9 +5164,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.224" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aaeb1e94f53b16384af593c71e20b095e958dab1d26939c1b70645c5cfbcc0b" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -5056,18 +5184,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.224" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f39390fa6346e24defbcdd3d9544ba8a19985d0af74df8501fbfe9a64341ab" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.224" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ff78ab5e8561c9a675bfc1785cb07ae721f0ee53329a595cefd8c04c2ac4e0" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -5098,6 +5226,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "serde_spanned" version = "1.0.1" @@ -5354,6 +5493,8 @@ dependencies = [ name = "syncable-cli" version = "0.26.1" dependencies = [ + "ag-ui-core", + "ag-ui-server", "ahash", "aho-corasick", "anyhow", @@ -5362,6 +5503,7 @@ dependencies = [ "aws-config", "aws-sdk-bedrockruntime", "aws-smithy-types", + "axum", "base64 0.22.1", "blake3", "bstr", @@ -5416,7 +5558,9 @@ dependencies = [ "textwrap", "thiserror 2.0.17", "tokio", + "tokio-stream", "toml", + "tower-http", "tracing", "urlencoding", "uuid", @@ -5764,6 +5908,30 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.15" @@ -5948,6 +6116,23 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http 1.3.1", + "httparse", + "log", + "rand 0.9.1", + "sha1", + "thiserror 2.0.17", + "utf-8", +] + [[package]] name = "twox-hash" version = "2.1.1" @@ -6118,6 +6303,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -6138,6 +6329,7 @@ checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.3", "js-sys", + "serde", "wasm-bindgen", ] diff --git a/Cargo.toml b/Cargo.toml index e496ebbf..48f3a5a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,13 @@ futures-util = "0.3" # Agent dependencies (using Rig - LLM application framework) rig-core = { version = "0.28", features = ["derive", "image"] } +# AG-UI Protocol - enables frontend connectivity for agent mode +ag-ui-core = { path = "../ag-ui-sdk/crates/ag-ui-core" } +ag-ui-server = { path = "../ag-ui-sdk/crates/ag-ui-server" } +axum = { version = "0.8", features = ["ws"] } +tower-http = { version = "0.6", features = ["cors"] } +tokio-stream = { version = "0.1", features = ["sync"] } + # AWS Bedrock dependencies (inlined bedrock module with extended thinking fixes) async-stream = "0.3" aws-config = { version = "1", features = ["behavior-version-latest"] } diff --git a/Dockerfile.agent b/Dockerfile.agent new file mode 100644 index 00000000..8b7c77c1 --- /dev/null +++ b/Dockerfile.agent @@ -0,0 +1,75 @@ +# ============================================================================= +# Syncable CLI Agent - Multi-stage Dockerfile +# ============================================================================= +# Builds the sync-ctl binary and creates a minimal runtime image for the +# AG-UI agent server. Used for containerized deployments where the agent +# serves frontend connections via SSE/WebSocket. +# +# Build: +# docker build -f Dockerfile.agent -t syncable-agent:latest . +# +# Run: +# docker run -p 9090:9090 -e OPENAI_API_KEY=$OPENAI_API_KEY syncable-agent:latest +# +# ============================================================================= + +# ----------------------------------------------------------------------------- +# Build stage - compile the Rust binary +# ----------------------------------------------------------------------------- +FROM rust:1.83-slim-bookworm AS builder + +# Install build dependencies +RUN apt-get update && apt-get install -y \ + pkg-config \ + libssl-dev \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Copy manifests first for better layer caching +COPY Cargo.toml Cargo.lock ./ + +# Create dummy src to build dependencies +RUN mkdir -p src && echo "fn main() {}" > src/main.rs + +# Build dependencies (this layer will be cached) +RUN cargo build --release && rm -rf src target/release/sync-ctl* + +# Copy actual source code +COPY src/ src/ + +# Build the real binary +RUN cargo build --release --bin sync-ctl + +# ----------------------------------------------------------------------------- +# Runtime stage - minimal image with just the binary +# ----------------------------------------------------------------------------- +FROM debian:bookworm-slim AS runtime + +# Install runtime dependencies +RUN apt-get update && apt-get install -y \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Create non-root user for security +RUN useradd -m -u 1000 syncable +USER syncable + +# Copy the compiled binary +COPY --from=builder /app/target/release/sync-ctl /usr/local/bin/sync-ctl + +# Working directory for project analysis +WORKDIR /project + +# AG-UI server port +EXPOSE 9090 + +# Health check endpoint +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:9090/health || exit 1 + +# Default entrypoint runs the agent server +ENTRYPOINT ["sync-ctl", "agent"] + +# Default arguments (can be overridden) +CMD ["--host", "0.0.0.0", "--port", "9090", "."] diff --git a/deploy/k8s/agent-deployment.yaml b/deploy/k8s/agent-deployment.yaml new file mode 100644 index 00000000..7648dd32 --- /dev/null +++ b/deploy/k8s/agent-deployment.yaml @@ -0,0 +1,144 @@ +# ============================================================================= +# Syncable Agent - Kubernetes Deployment +# ============================================================================= +# Deploys the AG-UI agent server as a Kubernetes deployment with service. +# +# Prerequisites: +# - Create secret with API keys: +# kubectl create secret generic syncable-api-keys \ +# --from-literal=OPENAI_API_KEY=sk-... \ +# --from-literal=ANTHROPIC_API_KEY=sk-ant-... +# +# Apply: +# kubectl apply -f agent-deployment.yaml +# +# ============================================================================= +apiVersion: v1 +kind: Namespace +metadata: + name: syncable + labels: + app.kubernetes.io/name: syncable +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: syncable-agent + namespace: syncable + labels: + app.kubernetes.io/name: syncable-agent + app.kubernetes.io/component: agent +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: syncable-agent + template: + metadata: + labels: + app.kubernetes.io/name: syncable-agent + spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + fsGroup: 1000 + containers: + - name: agent + image: syncable-agent:latest + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 9090 + protocol: TCP + args: + - "--host" + - "0.0.0.0" + - "--port" + - "9090" + - "--provider" + - "auto" + - "/project" + env: + - name: RUST_LOG + value: "info" + - name: OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: syncable-api-keys + key: OPENAI_API_KEY + optional: true + - name: ANTHROPIC_API_KEY + valueFrom: + secretKeyRef: + name: syncable-api-keys + key: ANTHROPIC_API_KEY + optional: true + resources: + requests: + cpu: "100m" + memory: "256Mi" + limits: + cpu: "1000m" + memory: "1Gi" + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 30 + timeoutSeconds: 3 + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + volumeMounts: + - name: project + mountPath: /project + readOnly: true + volumes: + - name: project + emptyDir: {} +--- +apiVersion: v1 +kind: Service +metadata: + name: syncable-agent + namespace: syncable + labels: + app.kubernetes.io/name: syncable-agent +spec: + type: ClusterIP + ports: + - name: http + port: 9090 + targetPort: http + protocol: TCP + selector: + app.kubernetes.io/name: syncable-agent +--- +# Optional: Ingress for external access +# Uncomment and configure for your ingress controller +# apiVersion: networking.k8s.io/v1 +# kind: Ingress +# metadata: +# name: syncable-agent +# namespace: syncable +# annotations: +# nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" +# nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" +# spec: +# ingressClassName: nginx +# rules: +# - host: agent.syncable.example.com +# http: +# paths: +# - path: / +# pathType: Prefix +# backend: +# service: +# name: syncable-agent +# port: +# name: http diff --git a/docker-compose.agent.yml b/docker-compose.agent.yml new file mode 100644 index 00000000..565b098e --- /dev/null +++ b/docker-compose.agent.yml @@ -0,0 +1,67 @@ +# ============================================================================= +# Syncable CLI Agent - Docker Compose Configuration +# ============================================================================= +# Runs the AG-UI agent server for frontend connectivity. Mount your project +# directory and provide API keys via environment variables. +# +# Usage: +# docker compose -f docker-compose.agent.yml up --build +# +# Environment: +# OPENAI_API_KEY - OpenAI API key (for GPT models) +# ANTHROPIC_API_KEY - Anthropic API key (for Claude models) +# AWS_ACCESS_KEY_ID - AWS credentials (for Bedrock) +# AWS_SECRET_ACCESS_KEY +# AWS_REGION +# +# ============================================================================= + +services: + syncable-agent: + build: + context: . + dockerfile: Dockerfile.agent + container_name: syncable-agent + ports: + - "9090:9090" + environment: + # LLM Provider API Keys (set in .env or environment) + - OPENAI_API_KEY=${OPENAI_API_KEY:-} + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} + # AWS Bedrock credentials (optional) + - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-} + - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-} + - AWS_REGION=${AWS_REGION:-us-east-1} + # Rust logging + - RUST_LOG=${RUST_LOG:-info} + volumes: + # Mount project directory for analysis (read-only) + - ${PROJECT_PATH:-.}:/project:ro + command: + - "--host" + - "0.0.0.0" + - "--port" + - "9090" + - "--provider" + - "${PROVIDER:-auto}" + - "/project" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9090/health"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 10s + restart: unless-stopped + + # Optional: Frontend development server + # Uncomment to run a frontend alongside the agent + # frontend: + # image: node:20-alpine + # working_dir: /app + # volumes: + # - ./frontend:/app + # ports: + # - "3000:3000" + # command: npm run dev + # depends_on: + # - syncable-agent diff --git a/src/agent/mod.rs b/src/agent/mod.rs index 1404ec69..7adf3c1c 100644 --- a/src/agent/mod.rs +++ b/src/agent/mod.rs @@ -104,6 +104,71 @@ pub enum AgentError { pub type AgentResult = Result; +// ============================================================================= +// AG-UI State Types +// ============================================================================= + +/// Agent state for AG-UI state synchronization +#[derive(Debug, Clone, serde::Serialize)] +pub struct AgentState { + /// Project being analyzed + pub project_path: String, + /// LLM provider name + pub provider: String, + /// Model being used + pub model: String, + /// Whether plan mode is active + pub plan_mode: bool, + /// Token usage statistics + pub token_usage: TokenUsageState, + /// Conversation state + pub conversation: ConversationState, +} + +/// Token usage state for AG-UI +#[derive(Debug, Clone, serde::Serialize)] +pub struct TokenUsageState { + /// Estimated input tokens + pub input_tokens: usize, + /// Estimated output tokens + pub output_tokens: usize, + /// Total tokens + pub total_tokens: usize, +} + +/// Conversation state for AG-UI +#[derive(Debug, Clone, serde::Serialize)] +pub struct ConversationState { + /// Number of conversation turns + pub turn_count: usize, + /// Whether history has been compacted + pub has_compacted: bool, +} + +/// Build AgentState from session and conversation history +fn build_agent_state(session: &ChatSession, history: &ConversationHistory) -> AgentState { + // Check if history has been compacted (status contains "compacted") + let has_compacted = history.status().contains("compacted"); + let input = session.token_usage.prompt_tokens as usize; + let output = session.token_usage.completion_tokens as usize; + + AgentState { + project_path: session.project_path.display().to_string(), + provider: session.provider.to_string(), + model: session.model.clone(), + plan_mode: session.plan_mode.is_planning(), + token_usage: TokenUsageState { + input_tokens: input, + output_tokens: output, + total_tokens: input + output, + }, + conversation: ConversationState { + turn_count: history.turn_count(), + has_compacted, + }, + } +} + /// Get the system prompt for the agent based on query type and plan mode fn get_system_prompt(project_path: &Path, query: Option<&str>, plan_mode: PlanMode) -> String { // In planning mode, use the read-only exploration prompt @@ -125,16 +190,73 @@ fn get_system_prompt(project_path: &Path, query: Option<&str>, plan_mode: PlanMo prompts::get_analysis_prompt(project_path) } +/// Run the agent as a dedicated AG-UI server (headless mode for containers/deployments). +/// +/// This starts the AG-UI server without interactive stdin, accepting connections +/// from frontends via SSE or WebSocket. The agent processes messages received +/// through the AG-UI protocol. +/// +/// # Arguments +/// +/// * `project_path` - Path to the project directory +/// * `provider` - LLM provider to use +/// * `model` - Optional model override +/// * `host` - Host address to bind to +/// * `port` - Port number to listen on +pub async fn run_agent_server( + project_path: &Path, + provider: ProviderType, + model: Option, + host: &str, + port: u16, +) -> AgentResult<()> { + use crate::server::{AgUiConfig, AgUiServer, ProcessorConfig}; + + // Configure the agent processor with provider, model, and project path + // Use regional model IDs (no global. prefix) for wider availability + let default_model = match provider { + // Claude 3.5 Sonnet v2 is widely available across regions + ProviderType::Bedrock => "anthropic.claude-3-5-sonnet-20241022-v2:0".to_string(), + ProviderType::Anthropic => "claude-3-5-sonnet-20241022".to_string(), + ProviderType::OpenAI => "gpt-4o".to_string(), + }; + let processor_config = ProcessorConfig::new() + .with_provider(&provider.to_string()) + .with_model(&model.unwrap_or(default_model)) + .with_project_path(project_path); + + let config = AgUiConfig::new() + .port(port) + .host(host) + .with_processor_config(processor_config); + let server = AgUiServer::new(config); + + println!("AG-UI agent server listening on http://{}:{}", host, port); + println!("Project path: {}", project_path.display()); + println!("Connect frontends via SSE (/sse) or WebSocket (/ws)"); + println!("Press Ctrl+C to stop the server"); + + // Run server (blocks until shutdown signal) + server + .run() + .await + .map_err(|e| AgentError::ProviderError(e.to_string())) +} + /// Run the agent in interactive mode with custom REPL supporting /model and /provider commands pub async fn run_interactive( project_path: &Path, provider: ProviderType, model: Option, + event_bridge: Option, ) -> AgentResult<()> { use tools::*; let mut session = ChatSession::new(project_path, provider, model); + // Store event bridge for use in tool hooks + let event_bridge = event_bridge; + // Shared background process manager for Prometheus port-forwards let bg_manager = Arc::new(BackgroundProcessManager::new()); @@ -217,6 +339,19 @@ pub async fn run_interactive( // Initialize session recorder for conversation persistence let mut session_recorder = persistence::SessionRecorder::new(project_path); + // Track if we exit due to an error (for AG-UI error events) + let mut exit_error: Option = None; + + // Emit AG-UI RunStarted event and initial state for connected frontends + if let Some(ref bridge) = event_bridge { + bridge.start_run().await; + // Emit initial agent state snapshot + let state = build_agent_state(&session, &conversation_history); + if let Ok(state_json) = serde_json::to_value(&state) { + bridge.emit_state_snapshot(state_json).await; + } + } + loop { // Show conversation status if we have history if !conversation_history.is_empty() { @@ -253,6 +388,16 @@ pub async fn run_interactive( } else { println!("{}", "▶ standard mode".green()); } + // Emit AG-UI state delta for plan mode change + if let Some(ref bridge) = event_bridge { + bridge + .emit_state_delta(vec![serde_json::json!({ + "op": "replace", + "path": "/plan_mode", + "value": new_mode.is_planning() + })]) + .await; + } continue; } } @@ -509,6 +654,11 @@ pub async fn run_interactive( let mut current_input = input.clone(); let mut succeeded = false; + // Emit AG-UI step event for processing + if let Some(ref bridge) = event_bridge { + bridge.start_step("processing").await; + } + while retry_attempt < MAX_RETRIES && continuation_count < MAX_CONTINUATIONS && !succeeded { // Log if this is a continuation attempt if continuation_count > 0 { @@ -524,6 +674,11 @@ pub async fn run_interactive( // progress.state().set_layout(layout_state.clone()); hook.set_progress_state(progress.state()).await; + // Connect AG-UI EventBridge if provided (for streaming tool events to frontends) + if let Some(ref bridge) = event_bridge { + hook.set_event_bridge(bridge.clone()).await; + } + let project_path_buf = session.project_path.clone(); // Select prompt based on query type (analysis vs generation) and plan mode let preamble = get_system_prompt( @@ -544,6 +699,11 @@ pub async fn run_interactive( // This allows immediate cancellation, not just between tool calls let mut user_interrupted = false; + // Emit AG-UI thinking event before LLM call + if let Some(ref bridge) = event_bridge { + bridge.start_thinking(Some("Generating response")).await; + } + // API call with Ctrl+C interrupt support let response = tokio::select! { biased; // Check ctrl_c first for faster response @@ -594,6 +754,7 @@ pub async fn run_interactive( .tool(OpenProviderSettingsTool::new()) .tool(CheckProviderConnectionTool::new()) .tool(ListDeploymentCapabilitiesTool::new()) + .tool(ListHetznerAvailabilityTool::new()) // Deployment tools for service management .tool(CreateDeploymentConfigTool::new()) .tool(DeployServiceTool::new(project_path_buf.clone())) @@ -711,6 +872,7 @@ pub async fn run_interactive( .tool(OpenProviderSettingsTool::new()) .tool(CheckProviderConnectionTool::new()) .tool(ListDeploymentCapabilitiesTool::new()) + .tool(ListHetznerAvailabilityTool::new()) // Deployment tools for service management .tool(CreateDeploymentConfigTool::new()) .tool(DeployServiceTool::new(project_path_buf.clone())) @@ -819,6 +981,7 @@ pub async fn run_interactive( .tool(OpenProviderSettingsTool::new()) .tool(CheckProviderConnectionTool::new()) .tool(ListDeploymentCapabilitiesTool::new()) + .tool(ListHetznerAvailabilityTool::new()) // Deployment tools for service management .tool(CreateDeploymentConfigTool::new()) .tool(DeployServiceTool::new(project_path_buf.clone())) @@ -885,11 +1048,21 @@ pub async fn run_interactive( // Stop the progress indicator before handling the response progress.stop().await; + // End AG-UI thinking event + if let Some(ref bridge) = event_bridge { + bridge.end_thinking().await; + } + // Suppress unused variable warnings let _ = (&progress_state, user_interrupted); match response { Ok(text) => { + // Emit AG-UI text message event (for connected frontends) + if let Some(ref bridge) = event_bridge { + bridge.emit_message(&text).await; + } + // Show final response println!(); ResponseFormatter::print_response(&text); @@ -930,6 +1103,14 @@ pub async fn run_interactive( ui::colors::ansi::RESET ); + // Emit AG-UI state update with new token counts + if let Some(ref bridge) = event_bridge { + let state = build_agent_state(&session, &conversation_history); + if let Ok(state_json) = serde_json::to_value(&state) { + bridge.emit_state_snapshot(state_json).await; + } + } + // Extract tool calls from the hook state for history tracking let tool_calls = extract_tool_calls_from_hook(&hook).await; let batch_tool_count = tool_calls.len(); @@ -1374,6 +1555,7 @@ pub async fn run_interactive( "{}", "Try breaking your request into smaller parts.".dimmed() ); + exit_error = Some(e.to_string()); break; } } else if err_str.contains("timeout") || err_str.contains("Timeout") { @@ -1391,6 +1573,7 @@ pub async fn run_interactive( tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; } else { eprintln!("{}", "Request timed out. Please try again.".red()); + exit_error = Some("Request timed out".to_string()); break; } } else { @@ -1420,14 +1603,30 @@ pub async fn run_interactive( ) .dimmed() ); + exit_error = Some(e.to_string()); break; } } } } + + // End AG-UI step event for this turn + if let Some(ref bridge) = event_bridge { + bridge.end_step().await; + } + println!(); } + // Emit AG-UI run completion event for connected frontends + if let Some(ref bridge) = event_bridge { + if let Some(error_msg) = exit_error { + bridge.finish_run_with_error(&error_msg).await; + } else { + bridge.finish_run().await; + } + } + // Clean up terminal layout before exiting (disabled - layout not initialized) // if let Err(e) = terminal_layout.cleanup() { // eprintln!( @@ -2218,11 +2417,13 @@ fn build_continuation_prompt( } /// Run a single query and return the response +/// Note: event_bridge is accepted for API consistency but not used in single-query mode pub async fn run_query( project_path: &Path, query: &str, provider: ProviderType, model: Option, + _event_bridge: Option, ) -> AgentResult { use tools::*; @@ -2275,6 +2476,7 @@ pub async fn run_query( .tool(OpenProviderSettingsTool::new()) .tool(CheckProviderConnectionTool::new()) .tool(ListDeploymentCapabilitiesTool::new()) + .tool(ListHetznerAvailabilityTool::new()) // Deployment tools for service management .tool(CreateDeploymentConfigTool::new()) .tool(DeployServiceTool::new(project_path_buf.clone())) @@ -2360,6 +2562,7 @@ pub async fn run_query( .tool(OpenProviderSettingsTool::new()) .tool(CheckProviderConnectionTool::new()) .tool(ListDeploymentCapabilitiesTool::new()) + .tool(ListHetznerAvailabilityTool::new()) // Deployment tools for service management .tool(CreateDeploymentConfigTool::new()) .tool(DeployServiceTool::new(project_path_buf.clone())) @@ -2434,6 +2637,7 @@ pub async fn run_query( .tool(OpenProviderSettingsTool::new()) .tool(CheckProviderConnectionTool::new()) .tool(ListDeploymentCapabilitiesTool::new()) + .tool(ListHetznerAvailabilityTool::new()) // Deployment tools for service management .tool(CreateDeploymentConfigTool::new()) .tool(DeployServiceTool::new(project_path_buf.clone())) diff --git a/src/agent/tools/mod.rs b/src/agent/tools/mod.rs index 4ff99f9a..eba45470 100644 --- a/src/agent/tools/mod.rs +++ b/src/agent/tools/mod.rs @@ -174,8 +174,8 @@ pub use platform::{ CheckProviderConnectionTool, CreateDeploymentConfigTool, CurrentContextTool, DeployServiceTool, GetDeploymentStatusTool, GetServiceLogsTool, ListDeploymentCapabilitiesTool, ListDeploymentConfigsTool, ListDeploymentsTool, - ListOrganizationsTool, ListProjectsTool, OpenProviderSettingsTool, SelectProjectTool, - TriggerDeploymentTool, + ListHetznerAvailabilityTool, ListOrganizationsTool, ListProjectsTool, + OpenProviderSettingsTool, SelectProjectTool, TriggerDeploymentTool, }; pub use prometheus_connect::PrometheusConnectTool; pub use prometheus_discover::PrometheusDiscoverTool; diff --git a/src/agent/tools/platform/deploy_service.rs b/src/agent/tools/platform/deploy_service.rs index 9ee0b192..24d9f199 100644 --- a/src/agent/tools/platform/deploy_service.rs +++ b/src/agent/tools/platform/deploy_service.rs @@ -19,9 +19,17 @@ use crate::platform::api::{PlatformApiClient, PlatformApiError, TriggerDeploymen use crate::platform::PlatformSession; use crate::wizard::{ RecommendationInput, recommend_deployment, get_provider_deployment_statuses, + get_hetzner_regions_dynamic, get_hetzner_server_types_dynamic, HetznerFetchResult, + DynamicCloudRegion, DynamicMachineType, }; use std::process::Command; +/// Cached Hetzner availability data for smart recommendations +struct HetznerAvailabilityData { + regions: Vec, + server_types: Vec, +} + /// Arguments for the deploy service tool #[derive(Debug, Deserialize)] pub struct DeployServiceArgs { @@ -342,6 +350,95 @@ User: "deploy this service" let recommendation = recommend_deployment(recommendation_input); + // 6.5. For Hetzner deployments, fetch real-time availability and update recommendations + // We require real-time data - no static fallback allowed + let final_provider_for_check = args.provider + .as_ref() + .and_then(|p| CloudProvider::from_str(p).ok()) + .unwrap_or(recommendation.provider.clone()); + + // Store Hetzner availability data for smart recommendations + let mut hetzner_availability: Option = None; + + if final_provider_for_check == CloudProvider::Hetzner { + // Fetch real-time Hetzner regions and server types + let regions = match get_hetzner_regions_dynamic(&client, &project_id).await { + HetznerFetchResult::Success(r) if !r.is_empty() => r, + HetznerFetchResult::Success(_) => { + return Ok(format_error_for_llm( + "deploy_service", + ErrorCategory::ResourceUnavailable, + "No Hetzner regions available", + Some(vec![ + "Check your Hetzner account status", + "Use list_hetzner_availability to see current availability", + ]), + )); + } + HetznerFetchResult::NoCredentials => { + return Ok(format_error_for_llm( + "deploy_service", + ErrorCategory::PermissionDenied, + "Cannot recommend Hetzner deployment: Hetzner credentials not configured", + Some(vec![ + "Add your Hetzner API token in project settings", + "Use open_provider_settings to configure Hetzner", + "Or specify a different provider (e.g., provider='gcp')", + ]), + )); + } + HetznerFetchResult::ApiError(err) => { + return Ok(format_error_for_llm( + "deploy_service", + ErrorCategory::NetworkError, + &format!("Cannot recommend Hetzner deployment: Failed to fetch availability - {}", err), + Some(vec![ + "Use list_hetzner_availability to check current status", + "Or specify a different provider (e.g., provider='gcp')", + ]), + )); + } + }; + + // Fetch server types with optional location filter + let server_types = match get_hetzner_server_types_dynamic(&client, &project_id, args.region.as_deref()).await { + HetznerFetchResult::Success(s) if !s.is_empty() => s, + HetznerFetchResult::Success(_) => { + return Ok(format_error_for_llm( + "deploy_service", + ErrorCategory::ResourceUnavailable, + "No Hetzner server types available", + Some(vec![ + "Check your Hetzner account status", + "Use list_hetzner_availability to see current availability", + ]), + )); + } + HetznerFetchResult::NoCredentials => { + return Ok(format_error_for_llm( + "deploy_service", + ErrorCategory::PermissionDenied, + "Cannot recommend Hetzner deployment: Hetzner credentials not configured", + Some(vec![ + "Add your Hetzner API token in project settings", + "Use open_provider_settings to configure Hetzner", + ]), + )); + } + HetznerFetchResult::ApiError(err) => { + return Ok(format_error_for_llm( + "deploy_service", + ErrorCategory::NetworkError, + &format!("Cannot recommend Hetzner deployment: Failed to fetch server types - {}", err), + Some(vec!["Use list_hetzner_availability to check current status"]), + )); + } + }; + + // Store for later use in recommendations + hetzner_availability = Some(HetznerAvailabilityData { regions, server_types }); + } + // 7. Extract analysis summary let primary_language = analysis.languages.first() .map(|l| l.name.clone()) @@ -388,7 +485,7 @@ User: "deploy this service" vec![ "To deploy with these settings: call deploy_service with preview_only=false".to_string(), "To customize: specify provider, machine_type, region, or port parameters".to_string(), - "To see more options: check the alternatives section above".to_string(), + "To see more options: check the hetzner_availability section for current pricing".to_string(), ] ) }; @@ -400,6 +497,128 @@ User: "deploy this service" None }; + // For Hetzner, use real-time availability to select best options + let (final_machine_type, final_region, machine_reasoning, region_reasoning, price_monthly) = + if let Some(ref hetzner) = hetzner_availability { + // SMART SELECTION: Find the best region + machine combination + // Strategy: Find cheapest machine with 4GB+ that's actually available somewhere + + // First, find all server types that are actually available (non-empty available_in) + let available_types: Vec<_> = hetzner.server_types.iter() + .filter(|st| !st.available_in.is_empty()) + .collect(); + + // If user specified a region, check if anything is available there + let user_region = args.region.as_deref(); + + // Find best machine: cheapest with 4GB+ that's available + let best_machine_with_region = if let Some(region) = user_region { + // User specified region - find best machine for that region + available_types.iter() + .filter(|st| st.memory_gb >= 4.0 && st.available_in.contains(®ion.to_string())) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|st| (*st, region.to_string())) + .or_else(|| { + // No 4GB+ available in that region, try any machine + available_types.iter() + .filter(|st| st.available_in.contains(®ion.to_string())) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|st| (*st, region.to_string())) + }) + } else { + // No region specified - find globally cheapest 4GB+ machine and use its best region + available_types.iter() + .filter(|st| st.memory_gb >= 4.0) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|st| { + // Pick the first available region for this machine + let region = st.available_in.first() + .cloned() + .unwrap_or_else(|| "nbg1".to_string()); + (*st, region) + }) + .or_else(|| { + // No 4GB+ available anywhere, find any cheapest machine + available_types.iter() + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|st| { + let region = st.available_in.first() + .cloned() + .unwrap_or_else(|| "nbg1".to_string()); + (*st, region) + }) + }) + }; + + if let Some((machine, region_id)) = best_machine_with_region { + let region_name = hetzner.regions.iter() + .find(|r| r.id == region_id) + .map(|r| format!("{}, {}", r.name, r.location)) + .unwrap_or_else(|| region_id.clone()); + + let available_count = hetzner.regions.iter() + .find(|r| r.id == region_id) + .map(|r| r.available_server_types.len()) + .unwrap_or(0); + + ( + args.machine_type.clone().unwrap_or_else(|| machine.id.clone()), + region_id.clone(), + format!( + "Selected {} ({} vCPU, {:.0} GB RAM) - cheapest AVAILABLE option at €{:.2}/mo", + machine.id, machine.cores, machine.memory_gb, machine.price_monthly + ), + format!("Selected {} ({}) - {} server types available", region_id, region_name, available_count), + Some(machine.price_monthly), + ) + } else { + // No server types available anywhere - this shouldn't happen if we passed validation + ( + args.machine_type.clone().unwrap_or_else(|| recommendation.machine_type.clone()), + args.region.clone().unwrap_or_else(|| recommendation.region.clone()), + "WARNING: No server types currently available - using fallback".to_string(), + "Using fallback region".to_string(), + None, + ) + } + } else { + // Non-Hetzner provider - use static recommendation + ( + args.machine_type.clone().unwrap_or_else(|| recommendation.machine_type.clone()), + args.region.clone().unwrap_or_else(|| recommendation.region.clone()), + recommendation.machine_reasoning.clone(), + recommendation.region_reasoning.clone(), + None, + ) + }; + + // Build availability info for response + let hetzner_availability_info = hetzner_availability.as_ref().map(|h| { + json!({ + "regions": h.regions.iter().map(|r| json!({ + "id": r.id, + "name": r.name, + "country": r.location, + "available_server_types_count": r.available_server_types.len(), + })).collect::>(), + "server_types": h.server_types.iter().take(10).map(|st| json!({ + "id": st.id, + "cores": st.cores, + "memory_gb": st.memory_gb, + "price_monthly_eur": st.price_monthly, + "available_in": st.available_in, + })).collect::>(), + "cheapest_4gb": h.server_types.iter() + .filter(|st| st.memory_gb >= 4.0) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|st| json!({ + "id": st.id, + "specs": format!("{} vCPU, {:.0} GB RAM", st.cores, st.memory_gb), + "price_monthly_eur": st.price_monthly, + })), + }) + }); + let response = json!({ "status": "recommendation", "deployment_mode": deployment_mode, @@ -434,10 +653,11 @@ User: "deploy this service" "provider_reasoning": recommendation.provider_reasoning, "target": recommendation.target.as_str(), "target_reasoning": recommendation.target_reasoning, - "machine_type": recommendation.machine_type, - "machine_reasoning": recommendation.machine_reasoning, - "region": recommendation.region, - "region_reasoning": recommendation.region_reasoning, + "machine_type": final_machine_type, + "machine_reasoning": machine_reasoning, + "region": final_region, + "region_reasoning": region_reasoning, + "price_monthly_eur": price_monthly, "port": recommendation.port, "health_check_path": recommendation.health_check_path, "is_public": args.is_public, @@ -447,23 +667,45 @@ User: "deploy this service" "Service will be INTERNAL only (not accessible from internet)" }, "confidence": recommendation.confidence, + "availability_source": if hetzner_availability.is_some() { "real-time" } else { "static" }, }, + "hetzner_availability": hetzner_availability_info, "alternatives": { "providers": recommendation.alternatives.providers.iter().map(|p| json!({ "provider": p.provider.as_str(), "available": p.available, "reason_if_unavailable": p.reason_if_unavailable, })).collect::>(), - "machine_types": recommendation.alternatives.machine_types.iter().map(|m| json!({ - "machine_type": m.machine_type, - "vcpu": m.vcpu, - "memory_gb": m.memory_gb, - "description": m.description, - })).collect::>(), - "regions": recommendation.alternatives.regions.iter().map(|r| json!({ - "region": r.region, - "display_name": r.display_name, - })).collect::>(), + "machine_types": if hetzner_availability.is_some() { + // Use real-time data for Hetzner + hetzner_availability.as_ref().unwrap().server_types.iter().take(6).map(|st| json!({ + "machine_type": st.id, + "vcpu": st.cores, + "memory_gb": st.memory_gb, + "price_monthly_eur": st.price_monthly, + "available_in": st.available_in, + })).collect::>() + } else { + recommendation.alternatives.machine_types.iter().map(|m| json!({ + "machine_type": m.machine_type, + "vcpu": m.vcpu, + "memory_gb": m.memory_gb, + "description": m.description, + })).collect::>() + }, + "regions": if hetzner_availability.is_some() { + // Use real-time data for Hetzner + hetzner_availability.as_ref().unwrap().regions.iter().map(|r| json!({ + "region": r.id, + "display_name": format!("{}, {}", r.name, r.location), + "available_server_types_count": r.available_server_types.len(), + })).collect::>() + } else { + recommendation.alternatives.regions.iter().map(|r| json!({ + "region": r.region, + "display_name": r.display_name, + })).collect::>() + }, }, "service_name": service_name, "next_steps": next_steps, @@ -475,13 +717,15 @@ User: "deploy this service" if is_production { " ⚠️ (PRODUCTION)" } else { "" } ) } else { + let price_info = price_monthly.map(|p| format!(" (€{:.2}/mo)", p)).unwrap_or_default(); format!( - "Deploy NEW service '{}' to {} ({}) with {} in {} on {} environment?{}", + "Deploy NEW service '{}' to {} ({}) with {}{} in {} on {} environment?{}", service_name, recommendation.provider.display_name(), recommendation.target.display_name(), - recommendation.machine_type, - recommendation.region, + final_machine_type, + price_info, + final_region, resolved_env_name, if is_production { " ⚠️ (PRODUCTION)" } else { "" } ) @@ -538,13 +782,73 @@ User: "deploy this service" .and_then(|p| CloudProvider::from_str(p).ok()) .unwrap_or(recommendation.provider.clone()); - let final_machine = args.machine_type - .clone() - .unwrap_or(recommendation.machine_type.clone()); + // For Hetzner, use real-time availability data to select best options + let (final_machine, final_region) = if let Some(ref hetzner) = hetzner_availability { + // SMART SELECTION: Same logic as preview + + // Find all server types that are actually available (non-empty available_in) + let available_types: Vec<_> = hetzner.server_types.iter() + .filter(|st| !st.available_in.is_empty()) + .collect(); + + let user_region = args.region.as_deref(); + + // Find best machine: cheapest with 4GB+ that's available + let best_machine_with_region = if let Some(region) = user_region { + // User specified region - find best machine for that region + available_types.iter() + .filter(|st| st.memory_gb >= 4.0 && st.available_in.contains(®ion.to_string())) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|st| (st.id.clone(), region.to_string())) + .or_else(|| { + available_types.iter() + .filter(|st| st.available_in.contains(®ion.to_string())) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|st| (st.id.clone(), region.to_string())) + }) + } else { + // No region specified - find globally cheapest 4GB+ machine + available_types.iter() + .filter(|st| st.memory_gb >= 4.0) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|st| { + let region = st.available_in.first() + .cloned() + .unwrap_or_else(|| "nbg1".to_string()); + (st.id.clone(), region) + }) + .or_else(|| { + available_types.iter() + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|st| { + let region = st.available_in.first() + .cloned() + .unwrap_or_else(|| "nbg1".to_string()); + (st.id.clone(), region) + }) + }) + }; - let final_region = args.region - .clone() - .unwrap_or(recommendation.region.clone()); + if let Some((machine, region)) = best_machine_with_region { + ( + args.machine_type.clone().unwrap_or(machine), + args.region.clone().unwrap_or(region), + ) + } else { + // Fallback to static defaults + ( + args.machine_type.clone().unwrap_or_else(|| recommendation.machine_type.clone()), + args.region.clone().unwrap_or_else(|| recommendation.region.clone()), + ) + } + } else { + // Non-Hetzner or no availability data - use static defaults + let machine = args.machine_type.clone() + .unwrap_or_else(|| recommendation.machine_type.clone()); + let region = args.region.clone() + .unwrap_or_else(|| recommendation.region.clone()); + (machine, region) + }; let final_port = args.port .unwrap_or(recommendation.port); @@ -950,6 +1254,7 @@ mod tests { machine_type: None, region: None, port: None, + is_public: false, preview_only: true, }; diff --git a/src/agent/tools/platform/list_hetzner_availability.rs b/src/agent/tools/platform/list_hetzner_availability.rs new file mode 100644 index 00000000..38aca56e --- /dev/null +++ b/src/agent/tools/platform/list_hetzner_availability.rs @@ -0,0 +1,289 @@ +//! List Hetzner availability tool for the agent +//! +//! Fetches real-time Hetzner Cloud region and server type availability with pricing. +//! The agent uses this to make smart deployment decisions based on current capacity. + +use rig::completion::ToolDefinition; +use rig::tool::Tool; +use serde::{Deserialize, Serialize}; +use serde_json::json; + +use crate::agent::tools::error::{ErrorCategory, format_error_for_llm}; +use crate::platform::api::PlatformApiClient; +use crate::platform::PlatformSession; +use crate::wizard::{ + get_hetzner_regions_dynamic, get_hetzner_server_types_dynamic, + HetznerFetchResult, DynamicCloudRegion, DynamicMachineType, +}; + +/// Arguments for the list_hetzner_availability tool +#[derive(Debug, Deserialize)] +pub struct ListHetznerAvailabilityArgs { + /// Optional: filter server types by location + pub location: Option, +} + +/// Error type for availability operations +#[derive(Debug, thiserror::Error)] +#[error("Hetzner availability error: {0}")] +pub struct ListHetznerAvailabilityError(String); + +/// Tool to fetch real-time Hetzner Cloud availability +/// +/// Returns current regions/locations and server types with: +/// - Real-time availability per region +/// - Current pricing (hourly and monthly in EUR) +/// - CPU, memory, and disk specs for each server type +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ListHetznerAvailabilityTool; + +impl ListHetznerAvailabilityTool { + pub fn new() -> Self { + Self + } +} + +impl Default for ListHetznerAvailabilityTool { + fn default() -> Self { + Self::new() + } +} + +impl Tool for ListHetznerAvailabilityTool { + const NAME: &'static str = "list_hetzner_availability"; + + type Error = ListHetznerAvailabilityError; + type Args = ListHetznerAvailabilityArgs; + type Output = String; + + async fn definition(&self, _prompt: String) -> ToolDefinition { + ToolDefinition { + name: Self::NAME.to_string(), + description: r#"Fetch real-time Hetzner Cloud region and server type availability. + +**IMPORTANT:** Use this tool BEFORE recommending Hetzner regions or server types. +This provides current data directly from Hetzner API - never use hardcoded/static data. + +**What it returns:** +- Available regions/locations with: + - Region ID (e.g., "nbg1", "fsn1", "hel1", "ash", "hil", "sin") + - City name and country + - Network zone (eu-central, us-east, us-west, ap-southeast) + - List of server types currently available in that region + +- Available server types with: + - Server type ID (e.g., "cx22", "cx32", "cpx21") + - CPU cores and memory (GB) + - Disk size (GB) + - Current pricing (EUR/hour and EUR/month) + - Which regions this type is available in + +**When to use:** +- When user asks about Hetzner regions/locations +- When recommending infrastructure for Hetzner deployment +- When user wants to compare Hetzner server types and pricing +- Before deploying to Hetzner to verify availability + +**Parameters:** +- location: Optional. Filter server types by specific location (e.g., "nbg1") + +**Prerequisites:** +- User must be authenticated +- A project with Hetzner credentials must be selected"# + .to_string(), + parameters: json!({ + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "Optional: Filter server types by location (e.g., 'nbg1', 'fsn1')" + } + } + }), + } + } + + async fn call(&self, args: Self::Args) -> Result { + // Get API client + let client = match PlatformApiClient::new() { + Ok(c) => c, + Err(_) => { + return Ok(format_error_for_llm( + "list_hetzner_availability", + ErrorCategory::PermissionDenied, + "Not authenticated", + Some(vec!["Run: sync-ctl auth login"]), + )); + } + }; + + // Load platform session for project context + let session = match PlatformSession::load() { + Ok(s) => s, + Err(_) => { + return Ok(format_error_for_llm( + "list_hetzner_availability", + ErrorCategory::InternalError, + "Failed to load platform session", + Some(vec!["Try selecting a project with select_project"]), + )); + } + }; + + if !session.is_project_selected() { + return Ok(format_error_for_llm( + "list_hetzner_availability", + ErrorCategory::ValidationFailed, + "No project selected", + Some(vec!["Use select_project to choose a project first"]), + )); + } + + let project_id = session.project_id.clone().unwrap_or_default(); + + // Fetch regions + let regions: Vec = match get_hetzner_regions_dynamic(&client, &project_id).await { + HetznerFetchResult::Success(r) => r, + HetznerFetchResult::NoCredentials => { + return Ok(format_error_for_llm( + "list_hetzner_availability", + ErrorCategory::PermissionDenied, + "Hetzner credentials not configured for this project", + Some(vec![ + "Add Hetzner API token in project settings", + "Use open_provider_settings to configure Hetzner", + ]), + )); + } + HetznerFetchResult::ApiError(err) => { + return Ok(format_error_for_llm( + "list_hetzner_availability", + ErrorCategory::NetworkError, + &format!("Failed to fetch Hetzner regions: {}", err), + None, + )); + } + }; + + // Fetch server types + let server_types: Vec = match get_hetzner_server_types_dynamic( + &client, + &project_id, + args.location.as_deref(), + ).await { + HetznerFetchResult::Success(s) => s, + HetznerFetchResult::NoCredentials => Vec::new(), // Already handled above + HetznerFetchResult::ApiError(_) => Vec::new(), // Non-fatal, continue with regions + }; + + // Format response + let regions_json: Vec = regions + .iter() + .map(|r| json!({ + "id": r.id, + "name": r.name, + "country": r.location, + "network_zone": r.network_zone, + "available_server_types_count": r.available_server_types.len(), + "available_server_types": r.available_server_types, + })) + .collect(); + + let server_types_json: Vec = server_types + .iter() + .map(|s| json!({ + "id": s.id, + "name": s.name, + "cores": s.cores, + "memory_gb": s.memory_gb, + "disk_gb": s.disk_gb, + "price_hourly_eur": s.price_hourly, + "price_monthly_eur": s.price_monthly, + "available_in": s.available_in, + })) + .collect(); + + // Group server types by category for easier reading + let shared_cpu: Vec<&serde_json::Value> = server_types_json + .iter() + .filter(|s| s["id"].as_str().map_or(false, |id| id.starts_with("cx"))) + .collect(); + + let dedicated_cpu: Vec<&serde_json::Value> = server_types_json + .iter() + .filter(|s| s["id"].as_str().map_or(false, |id| id.starts_with("ccx"))) + .collect(); + + let performance: Vec<&serde_json::Value> = server_types_json + .iter() + .filter(|s| s["id"].as_str().map_or(false, |id| id.starts_with("cpx"))) + .collect(); + + let response = json!({ + "status": "success", + "summary": { + "total_regions": regions.len(), + "total_server_types": server_types.len(), + "filter_applied": args.location, + }, + "regions": regions_json, + "server_types": { + "shared_cpu_cx": shared_cpu, + "dedicated_cpu_ccx": dedicated_cpu, + "performance_cpx": performance, + "all": server_types_json, + }, + "recommendations": { + "cheapest": server_types.iter() + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|s| json!({ + "id": s.id, + "price_monthly_eur": s.price_monthly, + "specs": format!("{} vCPU, {:.0} GB RAM", s.cores, s.memory_gb), + })), + "best_value_4gb": server_types.iter() + .filter(|s| s.memory_gb >= 4.0) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|s| json!({ + "id": s.id, + "price_monthly_eur": s.price_monthly, + "specs": format!("{} vCPU, {:.0} GB RAM", s.cores, s.memory_gb), + })), + "best_value_8gb": server_types.iter() + .filter(|s| s.memory_gb >= 8.0) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) + .map(|s| json!({ + "id": s.id, + "price_monthly_eur": s.price_monthly, + "specs": format!("{} vCPU, {:.0} GB RAM", s.cores, s.memory_gb), + })), + }, + "usage_notes": [ + "Use region IDs (nbg1, fsn1, hel1, ash, hil, sin) when deploying", + "EU regions (nbg1, fsn1, hel1) have lowest pricing", + "CX series: shared CPU, best for most workloads", + "CCX series: dedicated CPU, best for CPU-intensive workloads", + "CPX series: AMD performance, good balance of price/performance", + ], + }); + + serde_json::to_string_pretty(&response) + .map_err(|e| ListHetznerAvailabilityError(format!("Failed to serialize: {}", e))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tool_name() { + assert_eq!(ListHetznerAvailabilityTool::NAME, "list_hetzner_availability"); + } + + #[test] + fn test_tool_creation() { + let tool = ListHetznerAvailabilityTool::new(); + assert!(format!("{:?}", tool).contains("ListHetznerAvailabilityTool")); + } +} diff --git a/src/agent/tools/platform/mod.rs b/src/agent/tools/platform/mod.rs index eada3331..0c162fd5 100644 --- a/src/agent/tools/platform/mod.rs +++ b/src/agent/tools/platform/mod.rs @@ -71,6 +71,7 @@ mod get_service_logs; mod list_deployment_capabilities; mod list_deployment_configs; mod list_deployments; +mod list_hetzner_availability; mod list_organizations; mod list_projects; mod open_provider_settings; @@ -89,6 +90,7 @@ pub use get_service_logs::GetServiceLogsTool; pub use list_deployment_capabilities::ListDeploymentCapabilitiesTool; pub use list_deployment_configs::ListDeploymentConfigsTool; pub use list_deployments::ListDeploymentsTool; +pub use list_hetzner_availability::ListHetznerAvailabilityTool; pub use list_organizations::ListOrganizationsTool; pub use list_projects::ListProjectsTool; pub use open_provider_settings::OpenProviderSettingsTool; diff --git a/src/agent/ui/hooks.rs b/src/agent/ui/hooks.rs index c1280188..f1b54ea1 100644 --- a/src/agent/ui/hooks.rs +++ b/src/agent/ui/hooks.rs @@ -43,6 +43,8 @@ pub struct ToolCallState { pub is_expanded: bool, pub is_collapsible: bool, pub status_ok: bool, + /// AG-UI tool call ID for event correlation + pub ag_ui_tool_call_id: Option, } /// Accumulated usage from API responses @@ -68,7 +70,6 @@ impl AccumulatedUsage { } /// Shared state for the display -#[derive(Default)] pub struct DisplayState { pub tool_calls: Vec, pub agent_messages: Vec, @@ -80,6 +81,23 @@ pub struct DisplayState { pub progress_state: Option>, /// Cancel signal from rig - stored for external cancellation trigger pub cancel_signal: Option, + /// Optional AG-UI EventBridge for streaming tool events to frontends + pub event_bridge: Option, +} + +impl Default for DisplayState { + fn default() -> Self { + Self { + tool_calls: Vec::new(), + agent_messages: Vec::new(), + current_tool_index: None, + last_expandable_index: None, + usage: AccumulatedUsage::default(), + progress_state: None, + cancel_signal: None, + event_bridge: None, + } + } } /// A hook that shows Claude Code style tool execution @@ -141,6 +159,18 @@ impl ToolDisplayHook { let state = self.state.lock().await; state.cancel_signal.is_some() } + + /// Set the AG-UI EventBridge for streaming tool events to frontends + pub async fn set_event_bridge(&self, bridge: crate::server::EventBridge) { + let mut state = self.state.lock().await; + state.event_bridge = Some(bridge); + } + + /// Clear the AG-UI EventBridge + pub async fn clear_event_bridge(&self) { + let mut state = self.state.lock().await; + state.event_bridge = None; + } } impl Default for ToolDisplayHook { @@ -203,6 +233,19 @@ where } } + // Emit AG-UI ToolCallStart event if bridge is connected + let ag_ui_tool_call_id = { + let s = state.lock().await; + if let Some(ref bridge) = s.event_bridge { + // Parse args as JSON for the event + let args_json: serde_json::Value = serde_json::from_str(&args_str) + .unwrap_or_else(|_| serde_json::json!({"raw": args_str})); + Some(bridge.start_tool_call(&name, &args_json).await) + } else { + None + } + }; + // Store in state let mut s = state.lock().await; let idx = s.tool_calls.len(); @@ -215,6 +258,7 @@ where is_expanded: false, is_collapsible: false, status_ok: true, + ag_ui_tool_call_id, }); s.current_tool_index = Some(idx); } @@ -238,9 +282,13 @@ where let (status_ok, output_lines, is_collapsible) = print_tool_result(&name, &args_str, &result_str); - // Update state + // Update state and emit AG-UI ToolCallEnd event let mut s = state.lock().await; if let Some(idx) = s.current_tool_index { + // Get tool call ID before mutating + let ag_ui_tool_call_id = s.tool_calls.get(idx) + .and_then(|t| t.ag_ui_tool_call_id.clone()); + if let Some(tool) = s.tool_calls.get_mut(idx) { tool.output = Some(result_str); tool.output_lines = output_lines; @@ -252,6 +300,11 @@ where if is_collapsible { s.last_expandable_index = Some(idx); } + + // Emit AG-UI ToolCallEnd event if bridge is connected + if let (Some(bridge), Some(tool_call_id)) = (&s.event_bridge, &ag_ui_tool_call_id) { + bridge.end_tool_call(tool_call_id).await; + } } s.current_tool_index = None; diff --git a/src/cli.rs b/src/cli.rs index 7def281c..7c21c1da 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -339,6 +339,14 @@ pub enum Commands { /// List available sessions for this project and exit #[arg(long)] list_sessions: bool, + + /// Start AG-UI server for frontend connectivity (SSE/WebSocket) + #[arg(long)] + ag_ui: bool, + + /// AG-UI server port (default: 9090) + #[arg(long, default_value = "9090", requires = "ag_ui")] + ag_ui_port: u16, }, /// Authenticate with the Syncable platform @@ -374,6 +382,29 @@ pub enum Commands { #[command(subcommand)] command: Option, }, + + /// Run as dedicated AG-UI agent server (headless mode for containers) + Agent { + /// Path to the project directory + #[arg(value_name = "PROJECT_PATH", default_value = ".")] + path: PathBuf, + + /// Port for AG-UI server + #[arg(long, short, default_value = "9090")] + port: u16, + + /// Host address to bind to + #[arg(long, default_value = "127.0.0.1")] + host: String, + + /// LLM provider to use + #[arg(long, value_enum, default_value = "auto")] + provider: ChatProvider, + + /// Model to use + #[arg(long)] + model: Option, + }, } #[derive(Subcommand)] diff --git a/src/lib.rs b/src/lib.rs index 5bfc79d3..9594d5d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ pub mod error; pub mod generator; pub mod handlers; pub mod platform; // Platform session state for project/org context +pub mod server; // AG-UI server for frontend connectivity pub mod telemetry; // Add telemetry module pub mod wizard; // Interactive deployment wizard @@ -23,7 +24,10 @@ pub use telemetry::{TelemetryClient, TelemetryConfig, UserId}; // Re-export tele /// The current version of the CLI tool pub const VERSION: &str = env!("CARGO_PKG_VERSION"); -pub async fn run_command(command: Commands) -> Result<()> { +pub async fn run_command( + command: Commands, + event_bridge: Option, +) -> Result<()> { match command { Commands::Analyze { path, @@ -166,6 +170,8 @@ pub async fn run_command(command: Commands) -> Result<()> { query, resume, list_sessions: _, // Handled in main.rs + ag_ui: _, // Handled in main.rs + ag_ui_port: _, // Handled in main.rs } => { use agent::ProviderType; use cli::ChatProvider; @@ -271,11 +277,11 @@ pub async fn run_command(command: Commands) -> Result<()> { if let Some(q) = query { let response = - agent::run_query(&project_path, &q, provider_type, effective_model).await?; + agent::run_query(&project_path, &q, provider_type, effective_model, event_bridge).await?; println!("{}", response); Ok(()) } else { - agent::run_interactive(&project_path, provider_type, effective_model).await?; + agent::run_interactive(&project_path, provider_type, effective_model, event_bridge).await?; Ok(()) } } @@ -626,6 +632,41 @@ pub async fn run_command(command: Commands) -> Result<()> { }, } } + Commands::Agent { + path, + port, + host, + provider, + model, + } => { + use agent::ProviderType; + use cli::ChatProvider; + + // Determine provider type + let provider_type = match provider { + ChatProvider::Openai => ProviderType::OpenAI, + ChatProvider::Anthropic => ProviderType::Anthropic, + ChatProvider::Bedrock => ProviderType::Bedrock, + ChatProvider::Ollama => { + eprintln!("Ollama support coming soon. Using OpenAI as fallback."); + ProviderType::OpenAI + } + ChatProvider::Auto => { + // Load from saved config + let agent_config = config::load_agent_config(); + match agent_config.default_provider.as_str() { + "openai" => ProviderType::OpenAI, + "anthropic" => ProviderType::Anthropic, + "bedrock" => ProviderType::Bedrock, + _ => ProviderType::OpenAI, + } + } + }; + + let project_path = path.canonicalize().unwrap_or(path); + agent::run_agent_server(&project_path, provider_type, model, &host, port).await?; + Ok(()) + } Commands::Deploy { .. } => { // Deploy commands are handled in main.rs directly unreachable!("Deploy commands should be handled in main.rs") diff --git a/src/main.rs b/src/main.rs index 55277160..c61fef5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -119,6 +119,7 @@ async fn run() -> syncable_cli::Result<()> { Commands::Org { .. } => "org", Commands::Env { .. } => "env", Commands::Deploy { .. } => "deploy", + Commands::Agent { .. } => "agent", }; log::debug!("Command name: {}", command_name); @@ -619,6 +620,8 @@ async fn run() -> syncable_cli::Result<()> { query, resume, list_sessions, + ag_ui, + ag_ui_port, } => { // Handle --list-sessions flag first (before starting chat) if list_sessions { @@ -677,27 +680,57 @@ async fn run() -> syncable_cli::Result<()> { telemetry_client.track_event("chat", properties.clone()); } - syncable_cli::run_command(Commands::Chat { - path, - provider, - model, - query, - resume, - list_sessions, - }) + // Start AG-UI server if requested and get the event bridge + let event_bridge = if ag_ui { + use syncable_cli::server::{AgUiServer, AgUiConfig}; + + let config = AgUiConfig::new().port(ag_ui_port); + let server = AgUiServer::new(config); + let bridge = server.event_bridge(); + + // Spawn server in background + tokio::spawn(async move { + if let Err(e) = server.run().await { + eprintln!("AG-UI server error: {}", e); + } + }); + + println!("AG-UI server started on http://127.0.0.1:{}", ag_ui_port); + println!(" SSE endpoint: /sse"); + println!(" WebSocket endpoint: /ws"); + println!(" Health check: /health\n"); + + Some(bridge) + } else { + None + }; + + syncable_cli::run_command( + Commands::Chat { + path, + provider, + model, + query, + resume, + list_sessions, + ag_ui, + ag_ui_port, + }, + event_bridge, + ) .await } Commands::Auth { command } => { // Auth commands are handled by lib.rs - syncable_cli::run_command(Commands::Auth { command }).await + syncable_cli::run_command(Commands::Auth { command }, None).await } Commands::Project { command } => { // Project commands are handled by lib.rs - syncable_cli::run_command(Commands::Project { command }).await + syncable_cli::run_command(Commands::Project { command }, None).await } Commands::Org { command } => { // Org commands are handled by lib.rs - syncable_cli::run_command(Commands::Org { command }).await + syncable_cli::run_command(Commands::Org { command }, None).await } Commands::Env { command } => { use syncable_cli::auth::credentials; @@ -831,6 +864,26 @@ async fn run() -> syncable_cli::Result<()> { } } } + Commands::Agent { + path, + port, + host, + provider, + model, + } => { + // Agent command is handled by lib.rs + syncable_cli::run_command( + Commands::Agent { + path, + port, + host, + provider, + model, + }, + None, + ) + .await + } Commands::Deploy { path, command } => { use syncable_cli::auth::credentials; use syncable_cli::cli::DeployCommand; @@ -1088,15 +1141,20 @@ async fn run() -> syncable_cli::Result<()> { "\n{} Starting agent to help create Dockerfile...\n", "→".cyan() ); - // Transition to chat mode with the prompt - syncable_cli::run_command(Commands::Chat { - path: wizard_path, - provider: ChatProvider::Auto, - model: None, - query: Some(prompt), - resume: None, - list_sessions: false, - }) + // Transition to chat mode with the prompt (no AG-UI in wizard mode) + syncable_cli::run_command( + Commands::Chat { + path: wizard_path, + provider: ChatProvider::Auto, + model: None, + query: Some(prompt), + resume: None, + list_sessions: false, + ag_ui: false, + ag_ui_port: 9090, + }, + None, + ) .await } WizardResult::Cancelled => { @@ -1187,15 +1245,20 @@ async fn run() -> syncable_cli::Result<()> { "\n{} Starting agent to help create Dockerfile...\n", "→".cyan() ); - // Transition to chat mode with the prompt - syncable_cli::run_command(Commands::Chat { - path: path.clone(), - provider: ChatProvider::Auto, - model: None, - query: Some(prompt), - resume: None, - list_sessions: false, - }) + // Transition to chat mode with the prompt (no AG-UI in wizard mode) + syncable_cli::run_command( + Commands::Chat { + path: path.clone(), + provider: ChatProvider::Auto, + model: None, + query: Some(prompt), + resume: None, + list_sessions: false, + ag_ui: false, + ag_ui_port: 9090, + }, + None, + ) .await } WizardResult::Cancelled => { diff --git a/src/platform/api/client.rs b/src/platform/api/client.rs index f3bae4fc..9a97835f 100644 --- a/src/platform/api/client.rs +++ b/src/platform/api/client.rs @@ -851,6 +851,102 @@ impl PlatformApiClient { .await } + // ========================================================================= + // Hetzner Availability API methods (Dynamic Resource Fetching) + // ========================================================================= + + /// Get Hetzner options (locations and server types) with real-time data + /// + /// Uses the /api/v1/cloud-runner/hetzner/options endpoint which returns + /// both locations and server types in one call. This is the same endpoint + /// used by the frontend for Hetzner infrastructure selection. + /// + /// Endpoint: GET /api/v1/cloud-runner/hetzner/options?projectId=:projectId + pub async fn get_hetzner_options( + &self, + project_id: &str, + ) -> Result { + let response: super::types::HetznerOptionsResponse = self + .get(&format!( + "/api/v1/cloud-runner/hetzner/options?projectId={}", + urlencoding::encode(project_id) + )) + .await?; + Ok(response.data) + } + + /// Get Hetzner locations with real-time availability information + /// + /// Returns all Hetzner locations with the server types currently available + /// at each location. Uses the customer's Hetzner API token stored in their + /// cloud credentials to query the Hetzner API. + /// + /// This enables dynamic resource selection instead of relying on hardcoded values. + /// + /// Endpoint: GET /api/deployments/availability/locations?projectId=:projectId + pub async fn get_hetzner_locations( + &self, + project_id: &str, + ) -> Result> { + let response: super::types::LocationsAvailabilityResponse = self + .get(&format!( + "/api/deployments/availability/locations?projectId={}", + urlencoding::encode(project_id) + )) + .await?; + Ok(response.data) + } + + /// Get Hetzner server types with pricing and availability + /// + /// Returns all non-deprecated Hetzner server types sorted by monthly price, + /// with availability information showing which locations have capacity. + /// + /// Use this to dynamically populate server type selection UI and enable + /// smart resource recommendations based on real pricing data. + /// + /// Endpoint: GET /api/deployments/availability/server-types?projectId=:projectId&preferredLocation=:location + pub async fn get_hetzner_server_types( + &self, + project_id: &str, + preferred_location: Option<&str>, + ) -> Result> { + let mut path = format!( + "/api/deployments/availability/server-types?projectId={}", + urlencoding::encode(project_id) + ); + if let Some(location) = preferred_location { + path.push_str(&format!("&preferredLocation={}", urlencoding::encode(location))); + } + let response: super::types::ServerTypesResponse = self.get(&path).await?; + Ok(response.data) + } + + /// Check if a specific server type is available at a location + /// + /// Returns availability status with: + /// - Whether the server type is available + /// - Reason if unavailable (capacity vs unsupported) + /// - Alternative locations where it IS available + /// + /// Use this before deployment to detect capacity issues early and suggest alternatives. + /// + /// Endpoint: GET /api/deployments/availability/check?projectId=:projectId&location=:location&serverType=:serverType + pub async fn check_hetzner_availability( + &self, + project_id: &str, + location: &str, + server_type: &str, + ) -> Result { + self.get(&format!( + "/api/deployments/availability/check?projectId={}&location={}&serverType={}", + urlencoding::encode(project_id), + urlencoding::encode(location), + urlencoding::encode(server_type) + )) + .await + } + // ========================================================================= // Health Check API methods // ========================================================================= diff --git a/src/platform/api/types.rs b/src/platform/api/types.rs index 122a6305..178bc946 100644 --- a/src/platform/api/types.rs +++ b/src/platform/api/types.rs @@ -1219,6 +1219,143 @@ pub struct InitializeGitOpsResponse { pub installation_id: i64, } +// ============================================================================= +// Hetzner Availability Types (Dynamic Resource Fetching) +// ============================================================================= + +/// Hetzner location with geographic metadata (from Hetzner API) +/// NOTE: Backend returns snake_case for this type (network_zone), not camelCase +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HetznerLocation { + /// Location ID (e.g., 1) + pub id: i64, + /// Location code (e.g., "fsn1", "nbg1") + pub name: String, + /// Location description + pub description: String, + /// Country code + pub country: String, + /// City name + pub city: String, + /// Geographic latitude + pub latitude: f64, + /// Geographic longitude + pub longitude: f64, + /// Network zone (e.g., "eu-central") + pub network_zone: String, +} + +/// Location with available server types (from availability API) +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LocationWithAvailability { + /// Location details + pub location: HetznerLocation, + /// Server type names available at this location + pub available_server_types: Vec, +} + +/// Server type summary with availability and pricing (from availability API) +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ServerTypeSummary { + /// Server type ID + pub id: i64, + /// Server type name (e.g., "cx22", "cx32") + pub name: String, + /// Number of vCPUs + pub cores: i32, + /// Memory in GB + pub memory_gb: f64, + /// Disk size in GB + pub disk_gb: i64, + /// Hourly price in EUR (gross) + pub price_hourly: f64, + /// Monthly price in EUR (gross) + pub price_monthly: f64, + /// Locations where this server type is currently available + pub available_in: Vec, +} + +/// Availability check result for a specific server type at a location +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AvailabilityCheckResult { + /// Whether the server type is available + pub available: bool, + /// The location that was checked + pub location: String, + /// The server type that was checked + pub server_type: String, + /// Reason if unavailable: "capacity" or "unsupported" + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option, + /// Alternative locations where this server type IS available + #[serde(skip_serializing_if = "Option::is_none")] + pub alternative_locations: Option>, +} + +/// Response wrapper for locations with availability +#[derive(Debug, Clone, Deserialize)] +pub struct LocationsAvailabilityResponse { + pub data: Vec, +} + +/// Response wrapper for server types +#[derive(Debug, Clone, Deserialize)] +pub struct ServerTypesResponse { + pub data: Vec, +} + +// ============================================================================= +// Hetzner Options Types (from /api/v1/cloud-runner/hetzner/options) +// ============================================================================= + +/// Simple Hetzner location (from getHetznerOptions endpoint) +/// NOTE: Backend returns snake_case for network_zone field +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HetznerSimpleLocation { + pub id: i64, + pub name: String, + pub description: String, + pub city: String, + pub country: String, + pub network_zone: String, +} + +/// Hetzner server type with pricing (from getHetznerOptions endpoint) +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct HetznerSimpleServerType { + pub id: i64, + pub name: String, + pub description: String, + pub cores: i32, + pub memory: f64, + pub disk: i64, + pub cpu_type: String, + pub architecture: String, + pub deprecated: bool, + #[serde(default)] + pub available_locations: Vec, + #[serde(default)] + pub price_monthly: f64, +} + +/// Combined Hetzner options response +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct HetznerOptionsData { + pub locations: Vec, + pub server_types: Vec, +} + +/// Wrapped response for getHetznerOptions +#[derive(Debug, Clone, Deserialize)] +pub struct HetznerOptionsResponse { + pub data: HetznerOptionsData, +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/server/bridge.rs b/src/server/bridge.rs new file mode 100644 index 00000000..274141f5 --- /dev/null +++ b/src/server/bridge.rs @@ -0,0 +1,583 @@ +//! Event Bridge - Converts agent events to AG-UI protocol events. +//! +//! This module provides the `EventBridge` which is the main integration +//! point between the syncable-cli agent and the AG-UI protocol. +//! +//! # Usage +//! +//! ```rust,ignore +//! let bridge = server.event_bridge(); +//! +//! // Start a run +//! bridge.start_run().await; +//! +//! // Emit text message events +//! bridge.start_message().await; +//! bridge.emit_text_chunk("Hello, ").await; +//! bridge.emit_text_chunk("world!").await; +//! bridge.end_message().await; +//! +//! // Emit tool call events +//! let tool_id = bridge.start_tool_call("analyze", &args).await; +//! bridge.emit_tool_args_chunk(&tool_id, "partial args").await; +//! bridge.end_tool_call(&tool_id).await; +//! +//! // Finish the run +//! bridge.finish_run().await; +//! ``` + +use std::sync::Arc; + +use ag_ui_core::{ + BaseEvent, Event, InterruptInfo, JsonValue, MessageId, Role, RunFinishedEvent, + RunFinishedOutcome, RunId, RunStartedEvent, TextMessageContentEvent, TextMessageEndEvent, + TextMessageStartEvent, ThreadId, ToolCallArgsEvent, ToolCallEndEvent, ToolCallId, + ToolCallStartEvent, +}; +use tokio::sync::{broadcast, RwLock}; + +/// Bridge between agent code and AG-UI protocol events. +/// +/// This is the main interface for emitting events from agent code. +/// It handles the AG-UI protocol details like run IDs, message IDs, +/// and event sequencing. +#[derive(Clone)] +pub struct EventBridge { + event_tx: broadcast::Sender>, + thread_id: Arc>, + run_id: Arc>>, + current_message_id: Arc>>, + current_step_name: Arc>>, +} + +impl EventBridge { + /// Creates a new event bridge. + pub fn new( + event_tx: broadcast::Sender>, + thread_id: Arc>, + run_id: Arc>>, + ) -> Self { + Self { + event_tx, + thread_id, + run_id, + current_message_id: Arc::new(RwLock::new(None)), + current_step_name: Arc::new(RwLock::new(None)), + } + } + + /// Emits an event to all connected clients. + fn emit(&self, event: Event) { + // Ignore errors - clients may have disconnected + let _ = self.event_tx.send(event); + } + + // ========================================================================= + // Run Lifecycle + // ========================================================================= + + /// Starts a new agent run. + /// + /// Call this at the beginning of an agent interaction. + pub async fn start_run(&self) { + let thread_id = self.thread_id.read().await.clone(); + let run_id = RunId::random(); + + // Store the run ID + *self.run_id.write().await = Some(run_id.clone()); + + self.emit(Event::RunStarted(RunStartedEvent { + base: BaseEvent::with_current_timestamp(), + thread_id, + run_id, + })); + } + + /// Finishes the current run successfully. + pub async fn finish_run(&self) { + let thread_id = self.thread_id.read().await.clone(); + let run_id = self.run_id.write().await.take(); + let Some(run_id) = run_id else { + return; // No active run + }; + + self.emit(Event::RunFinished(RunFinishedEvent { + base: BaseEvent::with_current_timestamp(), + thread_id, + run_id, + outcome: Some(RunFinishedOutcome::Success), + result: None, + interrupt: None, + })); + } + + /// Finishes the current run with an error. + pub async fn finish_run_with_error(&self, message: &str) { + let _run_id = self.run_id.write().await.take(); + + self.emit(Event::RunError(ag_ui_core::RunErrorEvent { + base: BaseEvent::with_current_timestamp(), + message: message.to_string(), + code: None, + })); + } + + // ========================================================================= + // Human-in-the-Loop Interrupts + // ========================================================================= + + /// Interrupt the current run for human-in-the-loop interaction. + /// + /// This emits a `RunFinished` event with `outcome: Interrupt`, signaling + /// that the frontend should show approval UI and resume with user input. + /// + /// # Arguments + /// * `reason` - Optional interrupt reason (e.g., "file_write", "deployment") + /// * `payload` - Optional JSON payload with context for the approval UI + pub async fn interrupt(&self, reason: Option<&str>, payload: Option) { + let thread_id = self.thread_id.read().await.clone(); + let run_id = self.run_id.write().await.take(); + let Some(run_id) = run_id else { + return; // No active run + }; + + let mut info = InterruptInfo::new(); + if let Some(r) = reason { + info = info.with_reason(r); + } + if let Some(p) = payload { + info = info.with_payload(p); + } + + self.emit(Event::RunFinished(RunFinishedEvent { + base: BaseEvent::with_current_timestamp(), + thread_id, + run_id, + outcome: Some(RunFinishedOutcome::Interrupt), + result: None, + interrupt: Some(info), + })); + } + + /// Interrupt with a tracking ID for correlation. + /// + /// The interrupt ID can be used by the client to correlate the resume + /// request with the original interrupt. + pub async fn interrupt_with_id( + &self, + id: &str, + reason: Option<&str>, + payload: Option, + ) { + let thread_id = self.thread_id.read().await.clone(); + let run_id = self.run_id.write().await.take(); + let Some(run_id) = run_id else { + return; // No active run + }; + + let mut info = InterruptInfo::new().with_id(id); + if let Some(r) = reason { + info = info.with_reason(r); + } + if let Some(p) = payload { + info = info.with_payload(p); + } + + self.emit(Event::RunFinished(RunFinishedEvent { + base: BaseEvent::with_current_timestamp(), + thread_id, + run_id, + outcome: Some(RunFinishedOutcome::Interrupt), + result: None, + interrupt: Some(info), + })); + } + + // ========================================================================= + // Text Messages (Agent Response) + // ========================================================================= + + /// Starts a new text message from the assistant. + pub async fn start_message(&self) -> MessageId { + let message_id = MessageId::random(); + *self.current_message_id.write().await = Some(message_id.clone()); + + self.emit(Event::TextMessageStart(TextMessageStartEvent { + base: BaseEvent::with_current_timestamp(), + message_id: message_id.clone(), + role: Role::Assistant, + })); + + message_id + } + + /// Emits a text chunk as part of the current message. + pub async fn emit_text_chunk(&self, delta: &str) { + let message_id = self.current_message_id.read().await.clone(); + if let Some(message_id) = message_id { + self.emit(Event::TextMessageContent( + TextMessageContentEvent::new_unchecked(message_id, delta), + )); + } + } + + /// Ends the current text message. + pub async fn end_message(&self) { + let message_id = self.current_message_id.write().await.take(); + if let Some(message_id) = message_id { + self.emit(Event::TextMessageEnd(TextMessageEndEvent { + base: BaseEvent::with_current_timestamp(), + message_id, + })); + } + } + + /// Convenience: Emits a complete text message (start + content + end). + pub async fn emit_message(&self, content: &str) { + let _message_id = self.start_message().await; + self.emit_text_chunk(content).await; + self.end_message().await; + } + + // ========================================================================= + // Tool Calls + // ========================================================================= + + /// Starts a tool call. + /// + /// Returns the tool call ID for use with subsequent events. + pub async fn start_tool_call(&self, name: &str, args: &JsonValue) -> ToolCallId { + let tool_call_id = ToolCallId::random(); + + // Get current message ID or create one + let message_id = { + let mut current = self.current_message_id.write().await; + if current.is_none() { + *current = Some(MessageId::random()); + } + current.clone().unwrap() + }; + + self.emit(Event::ToolCallStart(ToolCallStartEvent { + base: BaseEvent::with_current_timestamp(), + tool_call_id: tool_call_id.clone(), + tool_call_name: name.to_string(), + parent_message_id: Some(message_id), + })); + + // Emit initial args if provided + if !args.is_null() { + if let Ok(args_str) = serde_json::to_string(args) { + self.emit(Event::ToolCallArgs(ToolCallArgsEvent { + base: BaseEvent::with_current_timestamp(), + tool_call_id: tool_call_id.clone(), + delta: args_str, + })); + } + } + + tool_call_id + } + + /// Emits a chunk of tool call arguments (for streaming args). + pub async fn emit_tool_args_chunk(&self, tool_call_id: &ToolCallId, delta: &str) { + self.emit(Event::ToolCallArgs(ToolCallArgsEvent { + base: BaseEvent::with_current_timestamp(), + tool_call_id: tool_call_id.clone(), + delta: delta.to_string(), + })); + } + + /// Ends a tool call. + /// + /// Note: Tool results are handled separately via messages in AG-UI protocol. + pub async fn end_tool_call(&self, tool_call_id: &ToolCallId) { + self.emit(Event::ToolCallEnd(ToolCallEndEvent { + base: BaseEvent::with_current_timestamp(), + tool_call_id: tool_call_id.clone(), + })); + } + + /// Convenience: Emits a complete tool call (start + end). + pub async fn emit_tool_call(&self, name: &str, args: &JsonValue) { + let tool_call_id = self.start_tool_call(name, args).await; + self.end_tool_call(&tool_call_id).await; + } + + // ========================================================================= + // State Updates + // ========================================================================= + + /// Emits a state snapshot. + pub async fn emit_state_snapshot(&self, state: JsonValue) { + self.emit(Event::StateSnapshot(ag_ui_core::StateSnapshotEvent { + base: BaseEvent::with_current_timestamp(), + snapshot: state, + })); + } + + /// Emits a state delta (JSON Patch). + pub async fn emit_state_delta(&self, delta: Vec) { + self.emit(Event::StateDelta(ag_ui_core::StateDeltaEvent { + base: BaseEvent::with_current_timestamp(), + delta, + })); + } + + // ========================================================================= + // Thinking/Progress + // ========================================================================= + + /// Starts a thinking/processing step. + pub async fn start_thinking(&self, title: Option<&str>) { + self.emit(Event::ThinkingStart(ag_ui_core::ThinkingStartEvent { + base: BaseEvent::with_current_timestamp(), + title: title.map(|s| s.to_string()), + })); + } + + /// Ends the current thinking step. + pub async fn end_thinking(&self) { + self.emit(Event::ThinkingEnd(ag_ui_core::ThinkingEndEvent { + base: BaseEvent::with_current_timestamp(), + })); + } + + /// Starts a step in the agent workflow. + pub async fn start_step(&self, name: &str) { + *self.current_step_name.write().await = Some(name.to_string()); + self.emit(Event::StepStarted(ag_ui_core::StepStartedEvent { + base: BaseEvent::with_current_timestamp(), + step_name: name.to_string(), + })); + } + + /// Ends the current step. + pub async fn end_step(&self) { + let step_name = self.current_step_name.write().await.take() + .unwrap_or_else(|| "unknown".to_string()); + self.emit(Event::StepFinished(ag_ui_core::StepFinishedEvent { + base: BaseEvent::with_current_timestamp(), + step_name, + })); + } + + // ========================================================================= + // Custom Events + // ========================================================================= + + /// Emits a custom event. + pub async fn emit_custom(&self, name: &str, value: JsonValue) { + self.emit(Event::Custom(ag_ui_core::CustomEvent { + base: BaseEvent::with_current_timestamp(), + name: name.to_string(), + value, + })); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn create_bridge() -> EventBridge { + let (tx, _) = broadcast::channel(100); + EventBridge::new( + tx, + Arc::new(RwLock::new(ThreadId::random())), + Arc::new(RwLock::new(None)), + ) + } + + #[tokio::test] + async fn test_start_and_finish_run() { + let bridge = create_bridge(); + + bridge.start_run().await; + assert!(bridge.run_id.read().await.is_some()); + + bridge.finish_run().await; + assert!(bridge.run_id.read().await.is_none()); + } + + #[tokio::test] + async fn test_message_lifecycle() { + let bridge = create_bridge(); + + let _msg_id = bridge.start_message().await; + assert!(bridge.current_message_id.read().await.is_some()); + + bridge.emit_text_chunk("Hello").await; + bridge.end_message().await; + + assert!(bridge.current_message_id.read().await.is_none()); + } + + #[tokio::test] + async fn test_emit_complete_message() { + let bridge = create_bridge(); + bridge.emit_message("Hello, world!").await; + // Should not panic + } + + #[tokio::test] + async fn test_tool_call() { + let bridge = create_bridge(); + + let tool_id = bridge.start_tool_call("test", &serde_json::json!({"key": "value"})).await; + bridge.emit_tool_args_chunk(&tool_id, "more args").await; + bridge.end_tool_call(&tool_id).await; + // Should not panic + } + + #[tokio::test] + async fn test_interrupt() { + let bridge = create_bridge(); + + bridge.start_run().await; + assert!(bridge.run_id.read().await.is_some()); + + bridge.interrupt(Some("file_write"), None).await; + // Run ID should be cleared after interrupt + assert!(bridge.run_id.read().await.is_none()); + } + + #[tokio::test] + async fn test_interrupt_with_payload() { + let bridge = create_bridge(); + + bridge.start_run().await; + bridge + .interrupt( + Some("deployment"), + Some(serde_json::json!({"file": "main.rs", "action": "write"})), + ) + .await; + assert!(bridge.run_id.read().await.is_none()); + } + + #[tokio::test] + async fn test_interrupt_with_id() { + let bridge = create_bridge(); + + bridge.start_run().await; + bridge + .interrupt_with_id("int-123", Some("deployment"), None) + .await; + assert!(bridge.run_id.read().await.is_none()); + } + + #[tokio::test] + async fn test_interrupt_without_run() { + let bridge = create_bridge(); + + // Interrupt without an active run should do nothing (not panic) + bridge.interrupt(Some("test"), None).await; + } + + #[tokio::test] + async fn test_events_received_by_subscriber() { + let (tx, mut rx) = broadcast::channel(100); + let bridge = EventBridge::new( + tx, + Arc::new(RwLock::new(ThreadId::random())), + Arc::new(RwLock::new(None)), + ); + + // Start a run + bridge.start_run().await; + + // Receive the RunStarted event + let event = rx.recv().await.expect("Should receive event"); + match event { + Event::RunStarted(_) => {} + _ => panic!("Expected RunStarted event"), + } + + // Emit a message + bridge.emit_message("Hello").await; + + // Should receive TextMessageStart, TextMessageContent, TextMessageEnd + let event = rx.recv().await.expect("Should receive event"); + match event { + Event::TextMessageStart(_) => {} + _ => panic!("Expected TextMessageStart"), + } + + let event = rx.recv().await.expect("Should receive event"); + match event { + Event::TextMessageContent(_) => {} + _ => panic!("Expected TextMessageContent"), + } + + let event = rx.recv().await.expect("Should receive event"); + match event { + Event::TextMessageEnd(_) => {} + _ => panic!("Expected TextMessageEnd"), + } + } + + #[tokio::test] + async fn test_step_and_thinking_events() { + let (tx, mut rx) = broadcast::channel(100); + let bridge = EventBridge::new( + tx, + Arc::new(RwLock::new(ThreadId::random())), + Arc::new(RwLock::new(None)), + ); + + bridge.start_step("processing").await; + let event = rx.recv().await.expect("Should receive event"); + match event { + Event::StepStarted(_) => {} + _ => panic!("Expected StepStarted"), + } + + bridge.start_thinking(Some("Analyzing")).await; + let event = rx.recv().await.expect("Should receive event"); + match event { + Event::ThinkingStart(_) => {} + _ => panic!("Expected ThinkingStart"), + } + + bridge.end_thinking().await; + let event = rx.recv().await.expect("Should receive event"); + match event { + Event::ThinkingEnd(_) => {} + _ => panic!("Expected ThinkingEnd"), + } + + bridge.end_step().await; + let event = rx.recv().await.expect("Should receive event"); + match event { + Event::StepFinished(_) => {} + _ => panic!("Expected StepFinished"), + } + } + + #[tokio::test] + async fn test_state_snapshot_event() { + let (tx, mut rx) = broadcast::channel(100); + let bridge = EventBridge::new( + tx, + Arc::new(RwLock::new(ThreadId::random())), + Arc::new(RwLock::new(None)), + ); + + let state = serde_json::json!({ + "model": "gpt-4", + "turn_count": 5 + }); + + bridge.emit_state_snapshot(state).await; + + let event = rx.recv().await.expect("Should receive event"); + match event { + Event::StateSnapshot(e) => { + assert_eq!(e.snapshot["model"], "gpt-4"); + assert_eq!(e.snapshot["turn_count"], 5); + } + _ => panic!("Expected StateSnapshot"), + } + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs new file mode 100644 index 00000000..d6f9b129 --- /dev/null +++ b/src/server/mod.rs @@ -0,0 +1,659 @@ +//! AG-UI Server Integration +//! +//! This module provides the AG-UI protocol server for syncable-cli, +//! enabling frontend applications to connect and receive real-time +//! updates as the agent works. +//! +//! # Architecture +//! +//! ```text +//! Frontend (tanstack) +//! ↓ SSE/WebSocket +//! AgUiServer (this module) +//! ↓ Event Bridge +//! Agent (ToolDisplayHook) +//! ↓ +//! LLM Provider (OpenAI/Anthropic/Bedrock) +//! ``` +//! +//! # Usage +//! +//! ```rust,ignore +//! use syncable_cli::server::{AgUiServer, AgUiConfig}; +//! +//! // Start the AG-UI server +//! let config = AgUiConfig::default().port(9090); +//! let server = AgUiServer::new(config); +//! let event_sender = server.event_sender(); +//! +//! // Run server in background +//! tokio::spawn(server.run()); +//! +//! // In agent code, emit events +//! let bridge = server.event_bridge(); +//! bridge.start_run().await; +//! let tool_id = bridge.start_tool_call("analyze", &args).await; +//! bridge.emit_text_chunk("Processing...").await; +//! bridge.end_tool_call(&tool_id).await; +//! bridge.finish_run().await; +//! ``` + +pub mod bridge; +pub mod processor; +pub mod routes; + +use std::net::SocketAddr; +use std::sync::Arc; + +use ag_ui_core::{Event, JsonValue, RunId, ThreadId}; +use axum::{routing::{get, post}, Router}; +use tower_http::cors::{Any, CorsLayer}; +use tokio::sync::{broadcast, mpsc, RwLock}; + +pub use bridge::EventBridge; +pub use processor::{AgentProcessor, ProcessorConfig, ThreadSession}; + +// Re-export types needed for message handling +pub use ag_ui_core::types::{Context, Message as AgUiMessage, RunAgentInput, Tool}; + +/// Message from frontend to agent processor. +/// Wraps RunAgentInput with optional response channel for acknowledgments. +#[derive(Debug, Clone)] +pub struct AgentMessage { + /// The AG-UI protocol input from the frontend. + pub input: RunAgentInput, +} + +impl AgentMessage { + /// Creates a new agent message from RunAgentInput. + pub fn new(input: RunAgentInput) -> Self { + Self { input } + } +} + +/// Configuration for the AG-UI server. +#[derive(Debug, Clone)] +pub struct AgUiConfig { + /// Port to listen on. + pub port: u16, + /// Host address to bind to. + pub host: String, + /// Maximum number of concurrent connections. + pub max_connections: usize, + /// Whether to start the agent processor. + pub enable_processor: bool, + /// Configuration for the agent processor (if enabled). + pub processor_config: Option, +} + +impl Default for AgUiConfig { + fn default() -> Self { + Self { + port: 9090, + host: "127.0.0.1".to_string(), + max_connections: 100, + enable_processor: false, + processor_config: None, + } + } +} + +impl AgUiConfig { + /// Creates a new configuration with default values. + pub fn new() -> Self { + Self::default() + } + + /// Sets the port number. + pub fn port(mut self, port: u16) -> Self { + self.port = port; + self + } + + /// Sets the host address. + pub fn host(mut self, host: impl Into) -> Self { + self.host = host.into(); + self + } + + /// Enables or disables the agent processor. + /// + /// When enabled, the server will spawn an AgentProcessor that + /// consumes messages from the message channel and processes them. + pub fn with_processor(mut self, enable: bool) -> Self { + self.enable_processor = enable; + if enable && self.processor_config.is_none() { + self.processor_config = Some(ProcessorConfig::default()); + } + self + } + + /// Sets the processor configuration. + pub fn with_processor_config(mut self, config: ProcessorConfig) -> Self { + self.processor_config = Some(config); + self.enable_processor = true; + self + } +} + +/// Shared state for the AG-UI server. +#[derive(Clone)] +pub struct ServerState { + /// Broadcast channel for events (outgoing to clients). + event_tx: broadcast::Sender>, + /// Channel for incoming messages from frontends. + message_tx: mpsc::Sender, + /// Receiver stored in Arc for extraction (only one consumer). + message_rx: Arc>>>, + /// Current thread ID for the session. + thread_id: Arc>, + /// Current run ID (if agent is running). + run_id: Arc>>, +} + +impl ServerState { + /// Creates new server state. + pub fn new() -> Self { + let (event_tx, _) = broadcast::channel(1000); + let (message_tx, message_rx) = mpsc::channel(100); + Self { + event_tx, + message_tx, + message_rx: Arc::new(RwLock::new(Some(message_rx))), + thread_id: Arc::new(RwLock::new(ThreadId::random())), + run_id: Arc::new(RwLock::new(None)), + } + } + + /// Gets the event sender for emitting events. + pub fn event_sender(&self) -> EventBridge { + EventBridge::new( + self.event_tx.clone(), + Arc::clone(&self.thread_id), + Arc::clone(&self.run_id), + ) + } + + /// Subscribes to the event stream. + pub fn subscribe(&self) -> broadcast::Receiver> { + self.event_tx.subscribe() + } + + /// Gets the message sender for routing incoming messages. + pub fn message_sender(&self) -> mpsc::Sender { + self.message_tx.clone() + } + + /// Takes the message receiver (can only be called once). + /// + /// This is used by the agent processor to receive messages from frontends. + /// Returns None if the receiver has already been taken. + pub async fn take_message_receiver(&self) -> Option> { + self.message_rx.write().await.take() + } +} + +impl Default for ServerState { + fn default() -> Self { + Self::new() + } +} + +/// The AG-UI server that enables frontend connectivity. +pub struct AgUiServer { + config: AgUiConfig, + state: ServerState, +} + +impl AgUiServer { + /// Creates a new AG-UI server with the given configuration. + pub fn new(config: AgUiConfig) -> Self { + Self { + config, + state: ServerState::new(), + } + } + + /// Creates a new server with default configuration. + pub fn with_defaults() -> Self { + Self::new(AgUiConfig::default()) + } + + /// Gets the event bridge for emitting events from agent code. + pub fn event_bridge(&self) -> EventBridge { + self.state.event_sender() + } + + /// Gets the server state for sharing with routes. + pub fn state(&self) -> ServerState { + self.state.clone() + } + + /// Runs the AG-UI server. + /// + /// This method blocks until the server is shut down. + /// If the processor is enabled in config, it will be spawned as a background task. + pub async fn run(self) -> Result<(), std::io::Error> { + let addr: SocketAddr = format!("{}:{}", self.config.host, self.config.port) + .parse() + .expect("Invalid address"); + + // Optionally start the agent processor + if self.config.enable_processor { + let processor_config = self.config.processor_config.clone() + .unwrap_or_default(); + + if let Some(msg_rx) = self.state.take_message_receiver().await { + let event_bridge = self.state.event_sender(); + let mut processor = AgentProcessor::new(msg_rx, event_bridge, processor_config); + + // Spawn processor in background + tokio::spawn(async move { + processor.run().await; + }); + + println!("Agent processor started"); + } + } + + // Configure CORS to allow requests from any origin (for development) + let cors = CorsLayer::new() + .allow_origin(Any) + .allow_methods(Any) + .allow_headers(Any); + + let app = Router::new() + .route("/", get(routes::health).post(routes::post_message)) + .route("/info", get(routes::info)) + .route("/sse", get(routes::sse_handler)) + .route("/ws", get(routes::ws_handler)) + .route("/message", post(routes::post_message)) + .route("/health", get(routes::health)) + .layer(cors) + .with_state(self.state); + + println!("AG-UI server listening on http://{}", addr); + + let listener = tokio::net::TcpListener::bind(addr).await?; + axum::serve(listener, app).await + } + + /// Returns the address the server will listen on. + pub fn addr(&self) -> String { + format!("{}:{}", self.config.host, self.config.port) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_config_default() { + let config = AgUiConfig::default(); + assert_eq!(config.port, 9090); + assert_eq!(config.host, "127.0.0.1"); + } + + #[test] + fn test_config_builder() { + let config = AgUiConfig::new() + .port(8080) + .host("0.0.0.0"); + assert_eq!(config.port, 8080); + assert_eq!(config.host, "0.0.0.0"); + } + + #[test] + fn test_server_state_new() { + let state = ServerState::new(); + let _bridge = state.event_sender(); + let _rx = state.subscribe(); + } + + #[test] + fn test_server_addr() { + let server = AgUiServer::with_defaults(); + assert_eq!(server.addr(), "127.0.0.1:9090"); + } + + #[test] + fn test_event_bridge_from_state() { + let state = ServerState::new(); + let bridge1 = state.event_sender(); + let bridge2 = state.event_sender(); + + // Both bridges should share the same channel + // (they'll both send to the same subscribers) + let _ = state.subscribe(); + + // Just verify we can create multiple bridges without panic + drop(bridge1); + drop(bridge2); + } + + #[tokio::test] + async fn test_server_event_flow() { + use ag_ui_core::Event; + + let state = ServerState::new(); + let bridge = state.event_sender(); + let mut rx = state.subscribe(); + + // Start a run + bridge.start_run().await; + + // Receive the event + let event = rx.recv().await.expect("Should receive RunStarted"); + assert!(matches!(event, Event::RunStarted(_))); + } + + #[tokio::test] + async fn test_message_channel() { + use ag_ui_core::types::{RunAgentInput, Message}; + + let state = ServerState::new(); + let msg_tx = state.message_sender(); + let mut msg_rx = state.take_message_receiver().await.expect("Should get receiver"); + + // Create a RunAgentInput using builder pattern + let input = RunAgentInput::new(ThreadId::random(), RunId::random()) + .with_messages(vec![Message::new_user("Hello agent")]); + + // Send message + let agent_msg = AgentMessage::new(input); + msg_tx.send(agent_msg).await.expect("Should send"); + + // Receive message + let received = msg_rx.recv().await.expect("Should receive message"); + assert_eq!(received.input.messages.len(), 1); + } + + #[tokio::test] + async fn test_message_receiver_only_once() { + let state = ServerState::new(); + + // First take succeeds + let rx1 = state.take_message_receiver().await; + assert!(rx1.is_some()); + + // Second take fails + let rx2 = state.take_message_receiver().await; + assert!(rx2.is_none()); + } + + #[test] + fn test_config_with_processor() { + let config = AgUiConfig::new().with_processor(true); + assert!(config.enable_processor); + assert!(config.processor_config.is_some()); + } + + #[test] + fn test_config_with_processor_config() { + let processor_config = ProcessorConfig::new() + .with_provider("anthropic") + .with_model("claude-3-sonnet"); + + let config = AgUiConfig::new() + .with_processor_config(processor_config); + + assert!(config.enable_processor); + let proc_config = config.processor_config.unwrap(); + assert_eq!(proc_config.provider, "anthropic"); + assert_eq!(proc_config.model, "claude-3-sonnet"); + } + + #[tokio::test] + async fn test_processor_integration_with_state() { + use ag_ui_core::types::{Message, RunAgentInput}; + use ag_ui_core::Event; + + // Create state and get components + let state = ServerState::new(); + let msg_tx = state.message_sender(); + let mut event_rx = state.subscribe(); + let msg_rx = state.take_message_receiver().await.expect("Should get receiver"); + + // Create and spawn processor + let event_bridge = state.event_sender(); + let mut processor = AgentProcessor::with_defaults(msg_rx, event_bridge); + + let handle = tokio::spawn(async move { + processor.run().await; + }); + + // Send a message + let thread_id = ThreadId::random(); + let run_id = RunId::random(); + let input = RunAgentInput::new(thread_id.clone(), run_id.clone()) + .with_messages(vec![Message::new_user("Integration test message")]); + + msg_tx.send(AgentMessage::new(input)).await.expect("Should send"); + + // Verify events are emitted + let event = tokio::time::timeout( + std::time::Duration::from_millis(200), + event_rx.recv() + ).await.expect("Should receive in time").expect("Should have event"); + + assert!(matches!(event, Event::RunStarted(_))); + + // Stop processor by dropping sender + drop(msg_tx); + + // Wait for processor to finish + let _ = tokio::time::timeout( + std::time::Duration::from_millis(200), + handle + ).await; + } + + // ============================================================================= + // E2E Integration Tests (Phase 25) + // ============================================================================= + + /// Helper to collect events until RunFinished or RunError + async fn collect_until_finished(rx: &mut tokio::sync::broadcast::Receiver) -> Vec { + use ag_ui_core::Event; + let mut events = Vec::new(); + loop { + match tokio::time::timeout(std::time::Duration::from_secs(5), rx.recv()).await { + Ok(Ok(event)) => { + let is_finished = matches!(&event, Event::RunFinished(_) | Event::RunError(_)); + events.push(event); + if is_finished { break; } + } + _ => break, + } + } + events + } + + /// Helper to drain events until run is finished + async fn drain_events_until_run_finished(rx: &mut tokio::sync::broadcast::Receiver) { + use ag_ui_core::Event; + loop { + match tokio::time::timeout(std::time::Duration::from_secs(30), rx.recv()).await { + Ok(Ok(Event::RunFinished(_))) => break, + Ok(Ok(Event::RunError(_))) => break, + Ok(Ok(_)) => continue, + _ => panic!("Timeout or error waiting for RunFinished"), + } + } + } + + #[tokio::test] + async fn test_multi_turn_conversation() { + use ag_ui_core::types::{Message, RunAgentInput}; + + // Create state and components + let state = ServerState::new(); + let msg_tx = state.message_sender(); + let mut event_rx = state.subscribe(); + let msg_rx = state.take_message_receiver().await.expect("Should get receiver"); + + // Create processor + let event_bridge = state.event_sender(); + let mut processor = AgentProcessor::with_defaults(msg_rx, event_bridge); + + let handle = tokio::spawn(async move { + processor.run().await; + }); + + let thread_id = ThreadId::random(); + + // Send first message + let input1 = RunAgentInput::new(thread_id.clone(), RunId::random()) + .with_messages(vec![Message::new_user("Hello")]); + msg_tx.send(AgentMessage::new(input1)).await.expect("Should send"); + + // Wait for first response + drain_events_until_run_finished(&mut event_rx).await; + + // Send follow-up message (same thread) + let input2 = RunAgentInput::new(thread_id.clone(), RunId::random()) + .with_messages(vec![Message::new_user("Follow up message")]); + msg_tx.send(AgentMessage::new(input2)).await.expect("Should send"); + + // Verify second run completes + drain_events_until_run_finished(&mut event_rx).await; + + drop(msg_tx); + let _ = tokio::time::timeout(std::time::Duration::from_millis(200), handle).await; + } + + #[tokio::test] + async fn test_event_sequence() { + use ag_ui_core::types::{Message, RunAgentInput}; + use ag_ui_core::Event; + + // Setup server state + let state = ServerState::new(); + let msg_tx = state.message_sender(); + let mut event_rx = state.subscribe(); + let msg_rx = state.take_message_receiver().await.expect("receiver"); + let event_bridge = state.event_sender(); + let mut processor = AgentProcessor::with_defaults(msg_rx, event_bridge); + + tokio::spawn(async move { processor.run().await; }); + + // Send message + let thread_id = ThreadId::random(); + let input = RunAgentInput::new(thread_id, RunId::random()) + .with_messages(vec![Message::new_user("Test event sequence")]); + msg_tx.send(AgentMessage::new(input)).await.unwrap(); + + // Collect events + let events = collect_until_finished(&mut event_rx).await; + + // Verify sequence + assert!(!events.is_empty(), "Should receive at least one event"); + assert!(matches!(events[0], Event::RunStarted(_)), "First event should be RunStarted"); + + // Should end with RunFinished or RunError + assert!( + matches!(events.last(), Some(Event::RunFinished(_) | Event::RunError(_))), + "Last event should be RunFinished or RunError" + ); + + // When successful (API key available), we expect at least: + // RunStarted -> StepStarted -> StepFinished -> TextMessageStart -> TextMessageContent* -> TextMessageEnd -> RunFinished + // Without API key, we get: RunStarted -> StepStarted -> StepFinished -> RunError + // Either way, verify we have multiple events + assert!(events.len() >= 2, "Should have at least RunStarted and terminal event"); + + drop(msg_tx); + } + + #[tokio::test] + async fn test_empty_message_error() { + use ag_ui_core::types::RunAgentInput; + use ag_ui_core::Event; + + let state = ServerState::new(); + let msg_tx = state.message_sender(); + let mut event_rx = state.subscribe(); + let msg_rx = state.take_message_receiver().await.expect("receiver"); + let event_bridge = state.event_sender(); + let mut processor = AgentProcessor::with_defaults(msg_rx, event_bridge); + + tokio::spawn(async move { processor.run().await; }); + + // Send message with no user content + let input = RunAgentInput::new(ThreadId::random(), RunId::random()); + msg_tx.send(AgentMessage::new(input)).await.unwrap(); + + // Collect events + let events = collect_until_finished(&mut event_rx).await; + + // Should get RunStarted then RunError + assert!(matches!(events[0], Event::RunStarted(_)), "First should be RunStarted"); + assert!( + matches!(events.last(), Some(Event::RunError(_))), + "Should end with RunError for empty message" + ); + + drop(msg_tx); + } + + #[tokio::test] + async fn test_invalid_provider_error() { + use ag_ui_core::types::{Message, RunAgentInput}; + use ag_ui_core::Event; + + let state = ServerState::new(); + let msg_tx = state.message_sender(); + let mut event_rx = state.subscribe(); + let msg_rx = state.take_message_receiver().await.expect("receiver"); + let event_bridge = state.event_sender(); + + // Configure with invalid provider + let config = ProcessorConfig::new().with_provider("invalid_provider_xyz"); + let mut processor = AgentProcessor::new(msg_rx, event_bridge, config); + + tokio::spawn(async move { processor.run().await; }); + + let input = RunAgentInput::new(ThreadId::random(), RunId::random()) + .with_messages(vec![Message::new_user("Test invalid provider")]); + msg_tx.send(AgentMessage::new(input)).await.unwrap(); + + // Collect events + let events = collect_until_finished(&mut event_rx).await; + + // Should error due to unsupported provider + assert!( + matches!(events.last(), Some(Event::RunError(_))), + "Should end with RunError for invalid provider" + ); + + drop(msg_tx); + } + + #[tokio::test] + async fn test_custom_system_prompt() { + use ag_ui_core::types::{Message, RunAgentInput}; + + let state = ServerState::new(); + let msg_tx = state.message_sender(); + let mut event_rx = state.subscribe(); + let msg_rx = state.take_message_receiver().await.expect("receiver"); + let event_bridge = state.event_sender(); + + // Configure with custom system prompt + let config = ProcessorConfig::new() + .with_system_prompt("You are a DevOps assistant. Always respond with deployment advice."); + let mut processor = AgentProcessor::new(msg_rx, event_bridge, config); + + tokio::spawn(async move { processor.run().await; }); + + let input = RunAgentInput::new(ThreadId::random(), RunId::random()) + .with_messages(vec![Message::new_user("Hello")]); + msg_tx.send(AgentMessage::new(input)).await.unwrap(); + + // Should complete (may error without API key, but should not panic) + drain_events_until_run_finished(&mut event_rx).await; + + drop(msg_tx); + } +} diff --git a/src/server/processor.rs b/src/server/processor.rs new file mode 100644 index 00000000..fc13f688 --- /dev/null +++ b/src/server/processor.rs @@ -0,0 +1,1199 @@ +//! Agent Processor - Routes frontend messages to agent for processing. +//! +//! This module provides the `AgentProcessor` which consumes messages from +//! the frontend (via WebSocket/POST) and processes them through the LLM, +//! emitting AG-UI events for the response. +//! +//! # Architecture +//! +//! ```text +//! Frontend → WebSocket/POST → message channel → AgentProcessor +//! ↓ +//! LLM (multi_turn with tools) +//! ↓ +//! EventBridge → SSE/WS → Frontend +//! ``` + +use std::collections::HashMap; +use std::path::PathBuf; +use std::time::Instant; + +use ag_ui_core::{Role, RunId, ThreadId}; +use rig::client::{CompletionClient, ProviderClient}; +use rig::completion::message::{AssistantContent, UserContent}; +use rig::completion::Message as RigMessage; +use rig::completion::Prompt; +use rig::one_or_many::OneOrMany; +use rig::providers::{anthropic, openai}; +use tokio::sync::mpsc; +use tracing::{debug, error, info, warn}; + +use super::{AgentMessage, EventBridge}; +use crate::agent::prompts; +use crate::agent::tools::{ + // Core analysis tools + AnalyzeTool, ListDirectoryTool, ReadFileTool, SecurityScanTool, VulnerabilitiesTool, + // Linting tools + HadolintTool, DclintTool, KubelintTool, HelmlintTool, + // K8s tools + K8sOptimizeTool, K8sCostsTool, K8sDriftTool, + // Terraform tools + TerraformFmtTool, TerraformValidateTool, TerraformInstallTool, + // Web and retrieval tools + WebFetchTool, RetrieveOutputTool, ListOutputsTool, + // Write tools for generation + WriteFileTool, WriteFilesTool, ShellTool, +}; + +use ag_ui_core::ToolCallId; +use ag_ui_core::state::StateManager; +use rig::agent::CancelSignal; +use rig::completion::{CompletionModel, CompletionResponse, Message as RigPromptMessage}; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; +use tokio::sync::Mutex; + +/// Step status for generative UI progress display. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum StepStatus { + Pending, + Completed, +} + +/// A step in the agent's execution for generative UI. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AgentStep { + pub description: String, + pub status: StepStatus, +} + +/// Result of a tool call for rich UI rendering. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ToolResult { + /// Name of the tool that was called. + pub tool_name: String, + /// Arguments passed to the tool. + pub args: serde_json::Value, + /// Result data from the tool (parsed JSON when possible). + pub result: serde_json::Value, + /// Whether the result is an error. + #[serde(default)] + pub is_error: bool, +} + +/// Agent state for generative UI rendering. +/// +/// This state is streamed to frontends via STATE_SNAPSHOT events +/// and can be rendered using CopilotKit's `useCoAgentStateRender`. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct AgentUiState { + /// Steps showing progress of agent execution. + pub steps: Vec, + /// Current tool being executed (if any). + #[serde(skip_serializing_if = "Option::is_none")] + pub current_tool: Option, + /// Tool results for rich UI rendering. + #[serde(default)] + pub tool_results: Vec, + /// Additional metadata for custom rendering. + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, +} + +impl AgentUiState { + /// Creates a new empty agent state. + pub fn new() -> Self { + Self::default() + } + + /// Adds a pending step. + pub fn add_step(&mut self, description: impl Into) { + self.steps.push(AgentStep { + description: description.into(), + status: StepStatus::Pending, + }); + } + + /// Marks a step as completed by index. + pub fn complete_step(&mut self, index: usize) { + if let Some(step) = self.steps.get_mut(index) { + step.status = StepStatus::Completed; + } + } + + /// Marks the first pending step as completed. + pub fn complete_current_step(&mut self) { + for step in &mut self.steps { + if step.status == StepStatus::Pending { + step.status = StepStatus::Completed; + break; + } + } + } + + /// Sets the current tool being executed. + pub fn set_current_tool(&mut self, tool: Option) { + self.current_tool = tool; + } + + /// Adds a tool result for rich UI rendering. + pub fn add_tool_result(&mut self, tool_name: String, args: serde_json::Value, result: serde_json::Value, is_error: bool) { + self.tool_results.push(ToolResult { + tool_name, + args, + result, + is_error, + }); + } + + /// Converts to JSON value for state events. + pub fn to_json(&self) -> serde_json::Value { + serde_json::to_value(self).unwrap_or_default() + } +} + +/// Info about a tool call in progress. +#[derive(Clone)] +struct ToolCallInfo { + id: ToolCallId, + name: String, + args: serde_json::Value, +} + +/// AG-UI Hook for streaming tool call events and state updates to frontends. +/// +/// This hook implements rig's PromptHook trait to capture tool calls +/// and emit AG-UI protocol events via the EventBridge. +/// It also maintains agent state for generative UI rendering. +#[derive(Clone)] +pub struct AgUiHook { + event_bridge: EventBridge, + /// Current tool call info for tracking (shared across async calls) + current_tool_call: Arc>>, + /// Agent state for generative UI (shared across async calls) + state: Arc>, +} + +impl AgUiHook { + /// Creates a new AG-UI hook with the given event bridge. + pub fn new(event_bridge: EventBridge) -> Self { + Self { + event_bridge, + current_tool_call: Arc::new(Mutex::new(None)), + state: Arc::new(Mutex::new(AgentUiState::new())), + } + } + + /// Emits the current state as a STATE_SNAPSHOT event. + async fn emit_state(&self) { + let state = self.state.lock().await; + self.event_bridge.emit_state_snapshot(state.to_json()).await; + } + + /// Adds a step and emits state update. + pub async fn add_step(&self, description: impl Into) { + { + let mut state = self.state.lock().await; + state.add_step(description); + } + self.emit_state().await; + } + + /// Completes the current step and emits state update. + pub async fn complete_current_step(&self) { + { + let mut state = self.state.lock().await; + state.complete_current_step(); + } + self.emit_state().await; + } +} + +impl rig::agent::PromptHook for AgUiHook +where + M: CompletionModel, +{ + fn on_tool_call( + &self, + tool_name: &str, + _tool_call_id: Option, + args: &str, + _cancel: CancelSignal, + ) -> impl std::future::Future + Send { + let bridge = self.event_bridge.clone(); + let name = tool_name.to_string(); + let args_str = args.to_string(); + let current_call = Arc::clone(&self.current_tool_call); + let state = Arc::clone(&self.state); + + async move { + debug!(tool = %name, "AgUiHook: on_tool_call triggered"); + + // Parse args as JSON for the event + let args_json: serde_json::Value = serde_json::from_str(&args_str) + .unwrap_or_else(|_| serde_json::json!({"raw": args_str})); + + // Update state for generative UI - add step for this tool + { + let mut s = state.lock().await; + // Create human-readable description from tool name + let description = match name.as_str() { + // Core analysis tools + "analyze_project" => "Analyzing project structure...".to_string(), + "read_file" => format!("Reading file: {}", args_json.get("path").and_then(|v| v.as_str()).unwrap_or("...")), + "list_directory" => format!("Listing directory: {}", args_json.get("path").and_then(|v| v.as_str()).unwrap_or("...")), + // Security tools + "security_scan" => "Running security scan...".to_string(), + "check_vulnerabilities" => "Checking for vulnerabilities...".to_string(), + // Linting tools + "hadolint" => "Linting Dockerfiles...".to_string(), + "dclint" => "Linting docker-compose files...".to_string(), + "kubelint" => "Linting Kubernetes manifests...".to_string(), + "helmlint" => "Linting Helm charts...".to_string(), + // K8s tools + "k8s_optimize" => "Analyzing Kubernetes resource optimization...".to_string(), + "k8s_costs" => "Calculating Kubernetes costs...".to_string(), + "k8s_drift" => "Detecting configuration drift...".to_string(), + // Terraform tools + "terraform_fmt" => "Formatting Terraform files...".to_string(), + "terraform_validate" => "Validating Terraform configuration...".to_string(), + "terraform_install" => "Installing Terraform...".to_string(), + // Web tools + "web_fetch" => format!("Fetching: {}", args_json.get("url").and_then(|v| v.as_str()).unwrap_or("...")), + // Retrieval tools + "retrieve_output" => "Retrieving stored output...".to_string(), + "list_outputs" => "Listing available outputs...".to_string(), + // Write tools + "write_file" => format!("Writing file: {}", args_json.get("path").and_then(|v| v.as_str()).unwrap_or("...")), + "write_files" => "Writing multiple files...".to_string(), + // Shell tool + "shell" => format!("Running command: {}", args_json.get("command").and_then(|v| v.as_str()).map(|s| if s.len() > 50 { format!("{}...", &s[..50]) } else { s.to_string() }).unwrap_or("...".to_string())), + _ => format!("Running {}...", name.replace('_', " ")), + }; + s.add_step(description); + s.set_current_tool(Some(name.clone())); + } + + // Emit state update for generative UI + let s = state.lock().await; + bridge.emit_state_snapshot(s.to_json()).await; + drop(s); + + // Emit ToolCallStart event + let tool_call_id = bridge.start_tool_call(&name, &args_json).await; + + // Store the tool call info (id, name, args) for the result handler + let mut call_guard = current_call.lock().await; + *call_guard = Some(ToolCallInfo { + id: tool_call_id, + name: name.clone(), + args: args_json.clone(), + }); + } + } + + fn on_tool_result( + &self, + _tool_name: &str, + _tool_call_id: Option, + _args: &str, + result: &str, + _cancel: CancelSignal, + ) -> impl std::future::Future + Send { + let bridge = self.event_bridge.clone(); + let current_call = Arc::clone(&self.current_tool_call); + let state = Arc::clone(&self.state); + let result_str = result.to_string(); + + async move { + // Get and clear the stored tool call info + let tool_call_info = { + let mut call_guard = current_call.lock().await; + call_guard.take() + }; + + // Parse result as JSON (if possible) + let result_json: serde_json::Value = serde_json::from_str(&result_str) + .unwrap_or_else(|_| serde_json::json!({"raw": result_str})); + + // Check if this looks like an error result + // Only check for explicit error fields, not substring matches + let is_error = result_json.get("error").is_some() + || result_json.get("success").and_then(|v| v.as_bool()) == Some(false) + || result_json.get("status").and_then(|v| v.as_str()) == Some("error") + || result_json.get("status").and_then(|v| v.as_str()) == Some("ERROR"); + + // Update state - mark current step as completed and add tool result + { + let mut s = state.lock().await; + s.complete_current_step(); + s.set_current_tool(None); + + // Add tool result for rich UI rendering + if let Some(ref info) = tool_call_info { + debug!( + tool = %info.name, + result_size = result_str.len(), + "AgUiHook: capturing tool result for UI" + ); + s.add_tool_result( + info.name.clone(), + info.args.clone(), + result_json.clone(), + is_error, + ); + } + } + + // Emit state update for generative UI + let s = state.lock().await; + bridge.emit_state_snapshot(s.to_json()).await; + drop(s); + + // Emit ToolCallEnd event + if let Some(info) = tool_call_info { + bridge.end_tool_call(&info.id).await; + } + } + } + + fn on_completion_response( + &self, + _prompt: &RigPromptMessage, + _response: &CompletionResponse, + _cancel: CancelSignal, + ) -> impl std::future::Future + Send { + // No-op for AG-UI - we don't need to track usage here + async {} + } +} + +/// Errors that can occur during message processing. +#[derive(Debug, thiserror::Error)] +pub enum ProcessorError { + #[error("Unsupported provider: {0}")] + UnsupportedProvider(String), + #[error("LLM completion failed: {0}")] + CompletionFailed(String), + #[error("Missing API key for provider: {0}")] + MissingApiKey(String), +} + +/// Configuration for the agent processor. +#[derive(Debug, Clone)] +pub struct ProcessorConfig { + /// LLM provider to use (openai, anthropic, bedrock). + pub provider: String, + /// Model name/ID. + pub model: String, + /// Maximum number of tool call iterations. + pub max_turns: usize, + /// System prompt for agent behavior (if None, uses prompts module based on project_path). + pub system_prompt: Option, + /// Project/workspace path for context-aware prompts and tools. + pub project_path: PathBuf, +} + +impl Default for ProcessorConfig { + fn default() -> Self { + Self { + provider: "openai".to_string(), + model: "gpt-4o".to_string(), + max_turns: 50, + system_prompt: None, + project_path: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")), + } + } +} + +impl ProcessorConfig { + /// Creates a new config with default values. + pub fn new() -> Self { + Self::default() + } + + /// Sets the provider. + pub fn with_provider(mut self, provider: impl Into) -> Self { + self.provider = provider.into(); + self + } + + /// Sets the model. + pub fn with_model(mut self, model: impl Into) -> Self { + self.model = model.into(); + self + } + + /// Sets the maximum number of turns. + pub fn with_max_turns(mut self, max_turns: usize) -> Self { + self.max_turns = max_turns; + self + } + + /// Sets the system prompt (overrides auto-generated prompt). + pub fn with_system_prompt(mut self, prompt: impl Into) -> Self { + self.system_prompt = Some(prompt.into()); + self + } + + /// Sets the project path for context-aware prompts and tools. + pub fn with_project_path(mut self, path: impl Into) -> Self { + self.project_path = path.into(); + self + } + + /// Gets the effective system prompt. + /// If a custom prompt is set, returns that. + /// Otherwise, generates appropriate prompt based on project_path. + pub fn effective_system_prompt(&self, query: Option<&str>) -> String { + if let Some(ref prompt) = self.system_prompt { + return prompt.clone(); + } + // Use analysis prompt by default (covers most use cases) + // For generation tasks, the prompts module can detect and switch + if let Some(q) = query { + if prompts::is_code_development_query(q) { + return prompts::get_code_development_prompt(&self.project_path); + } + if prompts::is_generation_query(q) { + return prompts::get_devops_prompt(&self.project_path, Some(q)); + } + } + prompts::get_analysis_prompt(&self.project_path) + } +} + +/// Per-thread session state for conversation isolation. +#[derive(Debug)] +pub struct ThreadSession { + /// Thread ID for this session. + pub thread_id: ThreadId, + /// Raw chat history for multi-turn conversations. + pub history: Vec, + /// When this session was created. + pub created_at: Instant, + /// Number of turns in this session. + pub turn_count: usize, +} + +impl ThreadSession { + /// Creates a new thread session. + pub fn new(thread_id: ThreadId) -> Self { + Self { + thread_id, + history: Vec::new(), + created_at: Instant::now(), + turn_count: 0, + } + } + + /// Adds a user message to history. + pub fn add_user_message(&mut self, content: &str) { + self.history.push(RigMessage::User { + content: OneOrMany::one(UserContent::text(content)), + }); + } + + /// Adds an assistant message to history. + pub fn add_assistant_message(&mut self, content: &str) { + self.history.push(RigMessage::Assistant { + id: None, + content: OneOrMany::one(AssistantContent::text(content)), + }); + self.turn_count += 1; + } + + /// Injects context that appears at start of conversation. + /// This is useful for adding system-level context to the conversation. + pub fn inject_context(&mut self, context: &str) { + // Add as a system-like user message at the beginning + // (rig doesn't have a System variant, so we use User with context prefix) + let context_msg = RigMessage::User { + content: OneOrMany::one(UserContent::text(format!("[Context]: {}", context))), + }; + self.history.insert(0, context_msg); + } +} + +/// Processes frontend messages through the LLM agent. +/// +/// The processor maintains per-thread sessions for conversation isolation +/// and emits AG-UI events via the EventBridge during processing. +pub struct AgentProcessor { + /// Receiver for messages from frontend. + message_rx: mpsc::Receiver, + /// Event bridge for emitting AG-UI events. + event_bridge: EventBridge, + /// Per-thread session state. + sessions: HashMap, + /// Processor configuration. + config: ProcessorConfig, +} + +impl AgentProcessor { + /// Creates a new agent processor. + /// + /// # Arguments + /// * `message_rx` - Receiver for messages from frontend + /// * `event_bridge` - Bridge for emitting AG-UI events + /// * `config` - Processor configuration + pub fn new( + message_rx: mpsc::Receiver, + event_bridge: EventBridge, + config: ProcessorConfig, + ) -> Self { + Self { + message_rx, + event_bridge, + sessions: HashMap::new(), + config, + } + } + + /// Creates a processor with default configuration. + pub fn with_defaults( + message_rx: mpsc::Receiver, + event_bridge: EventBridge, + ) -> Self { + Self::new(message_rx, event_bridge, ProcessorConfig::default()) + } + + /// Gets or creates a session for the given thread ID. + fn get_or_create_session(&mut self, thread_id: &ThreadId) -> &mut ThreadSession { + self.sessions + .entry(thread_id.clone()) + .or_insert_with(|| ThreadSession::new(thread_id.clone())) + } + + /// Gets the current session count. + pub fn session_count(&self) -> usize { + self.sessions.len() + } + + /// Gets the configuration. + pub fn config(&self) -> &ProcessorConfig { + &self.config + } + + /// Extracts the user message content from RunAgentInput messages. + /// + /// Returns the last user message content, or None if no user messages. + fn extract_user_input( + &self, + messages: &[ag_ui_core::types::Message], + ) -> Option { + // Find the last user message and extract its content + messages + .iter() + .rev() + .find(|m| m.role() == Role::User) + .and_then(|m| m.content().map(|s| s.to_string())) + } + + /// Runs the message processing loop. + /// + /// This method consumes messages from the channel and processes each one + /// through the agent. It runs until the channel is closed. + pub async fn run(&mut self) { + info!("AgentProcessor starting message processing loop"); + + while let Some(msg) = self.message_rx.recv().await { + let input = msg.input; + let thread_id = input.thread_id.clone(); + let run_id = input.run_id.clone(); + + debug!( + thread_id = %thread_id, + run_id = %run_id, + message_count = input.messages.len(), + "Received message from frontend" + ); + + // Check for dynamic provider/model/apiKey from forwardedProps + self.apply_forwarded_props(&input.forwarded_props); + + // Extract user input from messages + match self.extract_user_input(&input.messages) { + Some(user_input) => { + self.process_message(thread_id, run_id, user_input).await; + } + None => { + debug!( + thread_id = %thread_id, + "No user message found in input, skipping" + ); + // Emit error event + self.event_bridge.start_run().await; + self.event_bridge + .finish_run_with_error("No user message found in input") + .await; + } + } + } + + info!("AgentProcessor message channel closed, shutting down"); + } + + /// Apply settings from forwardedProps (provider, model, apiKey, awsRegion) + fn apply_forwarded_props(&mut self, forwarded_props: &serde_json::Value) { + if let Some(obj) = forwarded_props.as_object() { + // Update provider + if let Some(provider) = obj.get("provider").and_then(|v| v.as_str()) { + if !provider.is_empty() { + debug!(provider = %provider, "Applying provider from forwardedProps"); + self.config.provider = provider.to_string(); + } + } + + // Update model + if let Some(model) = obj.get("model").and_then(|v| v.as_str()) { + if !model.is_empty() { + debug!(model = %model, "Applying model from forwardedProps"); + self.config.model = model.to_string(); + } + } + + // Update API key (set in environment for the provider client) + if let Some(api_key) = obj.get("apiKey").and_then(|v| v.as_str()) { + if !api_key.is_empty() { + let provider = self.config.provider.to_lowercase(); + match provider.as_str() { + "openai" => { + debug!("Setting OPENAI_API_KEY from forwardedProps"); + // SAFETY: Single-threaded CLI context + unsafe { std::env::set_var("OPENAI_API_KEY", api_key); } + } + "anthropic" => { + debug!("Setting ANTHROPIC_API_KEY from forwardedProps"); + unsafe { std::env::set_var("ANTHROPIC_API_KEY", api_key); } + } + _ => {} + } + } + } + + // Update AWS region for Bedrock + if let Some(region) = obj.get("awsRegion").and_then(|v| v.as_str()) { + if !region.is_empty() { + debug!(region = %region, "Setting AWS_REGION from forwardedProps"); + unsafe { std::env::set_var("AWS_REGION", region); } + } + } + } + } + + /// Processes a single message through the agent. + /// + /// This is the core processing method that: + /// 1. Emits RunStarted + /// 2. Processes through LLM + /// 3. Emits TextMessage events + /// 4. Updates session history + /// 5. Emits RunFinished + async fn process_message( + &mut self, + thread_id: ThreadId, + _run_id: RunId, + user_input: String, + ) { + info!( + thread_id = %thread_id, + input_len = user_input.len(), + provider = %self.config.provider, + model = %self.config.model, + "Processing message through LLM" + ); + + // Get or create session + let session = self.get_or_create_session(&thread_id); + session.add_user_message(&user_input); + + // Emit run started + self.event_bridge.start_run().await; + + // Start thinking + self.event_bridge.start_thinking(Some("Thinking")).await; + + // Call LLM based on provider + let response = self.call_llm(&thread_id, &user_input).await; + + self.event_bridge.end_thinking().await; + + match response { + Ok(response_text) => { + // Emit the response as text message + self.event_bridge.start_message().await; + + // Stream the response in chunks for better UX + for chunk in response_text.chars().collect::>().chunks(50) { + let chunk_str: String = chunk.iter().collect(); + self.event_bridge.emit_text_chunk(&chunk_str).await; + } + + self.event_bridge.end_message().await; + + // Update session with assistant response + let session = self.get_or_create_session(&thread_id); + session.add_assistant_message(&response_text); + + debug!( + thread_id = %thread_id, + turn_count = session.turn_count, + response_len = response_text.len(), + "Message processed successfully" + ); + + // Finish the run + self.event_bridge.finish_run().await; + } + Err(e) => { + error!( + thread_id = %thread_id, + error = %e, + "LLM call failed" + ); + self.event_bridge.finish_run_with_error(&e.to_string()).await; + } + } + } + + /// Calls the LLM with the user input and conversation history. + async fn call_llm( + &mut self, + thread_id: &ThreadId, + user_input: &str, + ) -> Result { + // Clone config values and event_bridge to avoid borrow conflicts + // Use query-aware system prompt for better context + let preamble = self.config.effective_system_prompt(Some(user_input)); + let provider = self.config.provider.to_lowercase(); + let model = self.config.model.clone(); + let max_turns = self.config.max_turns; + let project_path = self.config.project_path.clone(); + let event_bridge = self.event_bridge.clone(); + + // Get mutable reference to session history + let session = self.get_or_create_session(thread_id); + let history = &mut session.history; + + debug!( + provider = %provider, + model = %model, + project_path = %project_path.display(), + history_len = history.len(), + "Calling LLM with tools" + ); + + match provider.as_str() { + "openai" => { + // Check for API key + if std::env::var("OPENAI_API_KEY").is_err() { + warn!("OPENAI_API_KEY not set"); + return Err(ProcessorError::MissingApiKey("OPENAI_API_KEY".to_string())); + } + + // Create AG-UI hook for streaming tool events to frontend + let hook = AgUiHook::new(event_bridge.clone()); + + let client = openai::Client::from_env(); + let agent = client + .agent(model) + .preamble(&preamble) + .max_tokens(4096) + // Core tools for file exploration and analysis + .tool(AnalyzeTool::new(project_path.clone())) + .tool(ReadFileTool::new(project_path.clone())) + .tool(ListDirectoryTool::new(project_path.clone())) + // Security and linting tools + .tool(SecurityScanTool::new(project_path.clone())) + .tool(VulnerabilitiesTool::new(project_path.clone())) + .tool(HadolintTool::new(project_path.clone())) + .tool(DclintTool::new(project_path.clone())) + .tool(KubelintTool::new(project_path.clone())) + .tool(HelmlintTool::new(project_path.clone())) + // K8s optimization and analysis tools + .tool(K8sOptimizeTool::new(project_path.clone())) + .tool(K8sCostsTool::new(project_path.clone())) + .tool(K8sDriftTool::new(project_path.clone())) + // Terraform tools + .tool(TerraformFmtTool::new(project_path.clone())) + .tool(TerraformValidateTool::new(project_path.clone())) + .tool(TerraformInstallTool::new()) + // Web and retrieval tools + .tool(WebFetchTool::new()) + .tool(RetrieveOutputTool::new()) + .tool(ListOutputsTool::new()) + // Write and shell tools for generation + .tool(WriteFileTool::new(project_path.clone())) + .tool(WriteFilesTool::new(project_path.clone())) + .tool(ShellTool::new(project_path.clone())) + .build(); + + agent + .prompt(user_input) + .with_history(history) + .with_hook(hook) // AG-UI hook for streaming tool events + .multi_turn(max_turns) + .await + .map_err(|e| ProcessorError::CompletionFailed(e.to_string())) + } + "anthropic" => { + // Check for API key + if std::env::var("ANTHROPIC_API_KEY").is_err() { + warn!("ANTHROPIC_API_KEY not set"); + return Err(ProcessorError::MissingApiKey("ANTHROPIC_API_KEY".to_string())); + } + + // Need fresh hook for anthropic (hook may be consumed by openai path) + let hook = AgUiHook::new(event_bridge.clone()); + + let client = anthropic::Client::from_env(); + let agent = client + .agent(model) + .preamble(&preamble) + .max_tokens(4096) + // Core tools for file exploration and analysis + .tool(AnalyzeTool::new(project_path.clone())) + .tool(ReadFileTool::new(project_path.clone())) + .tool(ListDirectoryTool::new(project_path.clone())) + // Security and linting tools + .tool(SecurityScanTool::new(project_path.clone())) + .tool(VulnerabilitiesTool::new(project_path.clone())) + .tool(HadolintTool::new(project_path.clone())) + .tool(DclintTool::new(project_path.clone())) + .tool(KubelintTool::new(project_path.clone())) + .tool(HelmlintTool::new(project_path.clone())) + // K8s optimization and analysis tools + .tool(K8sOptimizeTool::new(project_path.clone())) + .tool(K8sCostsTool::new(project_path.clone())) + .tool(K8sDriftTool::new(project_path.clone())) + // Terraform tools + .tool(TerraformFmtTool::new(project_path.clone())) + .tool(TerraformValidateTool::new(project_path.clone())) + .tool(TerraformInstallTool::new()) + // Web and retrieval tools + .tool(WebFetchTool::new()) + .tool(RetrieveOutputTool::new()) + .tool(ListOutputsTool::new()) + // Write and shell tools for generation + .tool(WriteFileTool::new(project_path.clone())) + .tool(WriteFilesTool::new(project_path.clone())) + .tool(ShellTool::new(project_path.clone())) + .build(); + + agent + .prompt(user_input) + .with_history(history) + .with_hook(hook) // AG-UI hook for streaming tool events + .multi_turn(max_turns) + .await + .map_err(|e| ProcessorError::CompletionFailed(e.to_string())) + } + "bedrock" | "aws" | "aws-bedrock" => { + // Need fresh hook for bedrock + let hook = AgUiHook::new(event_bridge.clone()); + + // Bedrock uses AWS credentials from environment + let client = crate::bedrock::client::Client::from_env(); + let agent = client + .agent(model) + .preamble(&preamble) + .max_tokens(4096) + // Core tools for file exploration and analysis + .tool(AnalyzeTool::new(project_path.clone())) + .tool(ReadFileTool::new(project_path.clone())) + .tool(ListDirectoryTool::new(project_path.clone())) + // Security and linting tools + .tool(SecurityScanTool::new(project_path.clone())) + .tool(VulnerabilitiesTool::new(project_path.clone())) + .tool(HadolintTool::new(project_path.clone())) + .tool(DclintTool::new(project_path.clone())) + .tool(KubelintTool::new(project_path.clone())) + .tool(HelmlintTool::new(project_path.clone())) + // K8s optimization and analysis tools + .tool(K8sOptimizeTool::new(project_path.clone())) + .tool(K8sCostsTool::new(project_path.clone())) + .tool(K8sDriftTool::new(project_path.clone())) + // Terraform tools + .tool(TerraformFmtTool::new(project_path.clone())) + .tool(TerraformValidateTool::new(project_path.clone())) + .tool(TerraformInstallTool::new()) + // Web and retrieval tools + .tool(WebFetchTool::new()) + .tool(RetrieveOutputTool::new()) + .tool(ListOutputsTool::new()) + // Write and shell tools for generation + .tool(WriteFileTool::new(project_path.clone())) + .tool(WriteFilesTool::new(project_path.clone())) + .tool(ShellTool::new(project_path)) + .build(); + + agent + .prompt(user_input) + .with_history(history) + .with_hook(hook) // AG-UI hook for streaming tool events + .multi_turn(max_turns) + .await + .map_err(|e| ProcessorError::CompletionFailed(e.to_string())) + } + _ => { + Err(ProcessorError::UnsupportedProvider(provider.to_string())) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tokio::sync::broadcast; + use std::sync::Arc; + use tokio::sync::RwLock; + + fn create_test_processor() -> (AgentProcessor, mpsc::Sender) { + let (msg_tx, msg_rx) = mpsc::channel(100); + let (event_tx, _) = broadcast::channel(100); + let bridge = EventBridge::new( + event_tx, + Arc::new(RwLock::new(ThreadId::random())), + Arc::new(RwLock::new(None)), + ); + let processor = AgentProcessor::with_defaults(msg_rx, bridge); + (processor, msg_tx) + } + + #[test] + fn test_processor_config_default() { + let config = ProcessorConfig::default(); + assert_eq!(config.provider, "openai"); + assert_eq!(config.model, "gpt-4o"); + assert_eq!(config.max_turns, 50); + } + + #[test] + fn test_processor_config_builder() { + let config = ProcessorConfig::new() + .with_provider("anthropic") + .with_model("claude-3-opus") + .with_max_turns(100); + assert_eq!(config.provider, "anthropic"); + assert_eq!(config.model, "claude-3-opus"); + assert_eq!(config.max_turns, 100); + } + + #[test] + fn test_processor_config_system_prompt() { + // Default system prompt (uses analysis prompt from prompts module) + let config = ProcessorConfig::default(); + assert!(config.system_prompt.is_none()); + // Analysis prompt contains agent identity section + assert!(config.effective_system_prompt(None).contains("DevOps/Platform Engineer")); + + // Custom system prompt overrides auto-generated + let config = ProcessorConfig::new() + .with_system_prompt("You are a DevOps expert."); + assert_eq!(config.system_prompt, Some("You are a DevOps expert.".to_string())); + assert_eq!(config.effective_system_prompt(None), "You are a DevOps expert."); + } + + #[test] + fn test_thread_session_inject_context() { + let mut session = ThreadSession::new(ThreadId::random()); + + // Add some messages first + session.add_user_message("Hello"); + session.add_assistant_message("Hi there!"); + assert_eq!(session.history.len(), 2); + + // Inject context - should be at the beginning + session.inject_context("Working on project: my-app"); + assert_eq!(session.history.len(), 3); + + // Verify the context message is at the start (index 0) + if let RigMessage::User { content } = &session.history[0] { + let content_str = format!("{:?}", content); + assert!(content_str.contains("[Context]")); + assert!(content_str.contains("my-app")); + } else { + panic!("Expected User message at index 0"); + } + } + + #[test] + fn test_thread_session_new() { + let thread_id = ThreadId::random(); + let session = ThreadSession::new(thread_id.clone()); + assert_eq!(session.thread_id, thread_id); + assert!(session.history.is_empty()); + assert_eq!(session.turn_count, 0); + } + + #[test] + fn test_thread_session_add_messages() { + let mut session = ThreadSession::new(ThreadId::random()); + + session.add_user_message("Hello"); + assert_eq!(session.history.len(), 1); + assert_eq!(session.turn_count, 0); // User message doesn't increment turn + + session.add_assistant_message("Hi there!"); + assert_eq!(session.history.len(), 2); + assert_eq!(session.turn_count, 1); // Assistant message increments turn + } + + #[test] + fn test_processor_creation() { + let (processor, _tx) = create_test_processor(); + assert_eq!(processor.session_count(), 0); + assert_eq!(processor.config().provider, "openai"); + } + + #[test] + fn test_get_or_create_session() { + let (mut processor, _tx) = create_test_processor(); + let thread_id = ThreadId::random(); + + // First call creates new session + let session = processor.get_or_create_session(&thread_id); + assert_eq!(session.turn_count, 0); + + // Add a message + session.add_user_message("test"); + + // Second call returns same session + let session = processor.get_or_create_session(&thread_id); + assert_eq!(session.history.len(), 1); + } + + #[tokio::test] + async fn test_process_message() { + let (mut processor, _tx) = create_test_processor(); + let thread_id = ThreadId::random(); + let run_id = RunId::random(); + + processor.process_message( + thread_id.clone(), + run_id, + "Hello, agent!".to_string(), + ).await; + + // Check session was created and user message was added + assert_eq!(processor.session_count(), 1); + let session = processor.sessions.get(&thread_id).unwrap(); + + // User message should always be added + assert!(session.history.len() >= 1, "User message should be in history"); + + // If API keys are available, turn_count and history should include assistant response + // If not, the LLM call fails gracefully and only user message is present + if std::env::var("OPENAI_API_KEY").is_ok() { + // With API key, expect full conversation + assert_eq!(session.turn_count, 1); + assert_eq!(session.history.len(), 2); // user + assistant + } else { + // Without API key, LLM call fails - only user message present + assert_eq!(session.turn_count, 0); + assert_eq!(session.history.len(), 1); // just user + } + } + + #[tokio::test] + async fn test_run_processes_messages() { + use ag_ui_core::types::{Message as AgUiProtocolMessage, RunAgentInput}; + use ag_ui_core::Event; + use tokio::sync::broadcast; + + let (msg_tx, msg_rx) = mpsc::channel(100); + let (event_tx, mut event_rx) = broadcast::channel(100); + + let bridge = EventBridge::new( + event_tx, + Arc::new(RwLock::new(ThreadId::random())), + Arc::new(RwLock::new(None)), + ); + + let mut processor = AgentProcessor::with_defaults(msg_rx, bridge); + + // Spawn processor in background + let handle = tokio::spawn(async move { + processor.run().await; + }); + + // Send a message + let thread_id = ThreadId::random(); + let run_id = RunId::random(); + let input = RunAgentInput::new(thread_id.clone(), run_id.clone()) + .with_messages(vec![AgUiProtocolMessage::new_user("Hello from test")]); + + let agent_msg = super::super::AgentMessage::new(input); + msg_tx.send(agent_msg).await.expect("Should send"); + + // Verify we receive RunStarted event + let event = tokio::time::timeout( + std::time::Duration::from_millis(100), + event_rx.recv() + ).await.expect("Should receive event in time").expect("Should have event"); + + assert!(matches!(event, Event::RunStarted(_))); + + // Drop sender to close channel and stop processor + drop(msg_tx); + + // Wait for processor to finish + tokio::time::timeout( + std::time::Duration::from_millis(100), + handle + ).await.expect("Processor should finish").expect("Should not panic"); + } + + #[tokio::test] + async fn test_run_handles_empty_messages() { + use ag_ui_core::types::RunAgentInput; + use ag_ui_core::Event; + use tokio::sync::broadcast; + + let (msg_tx, msg_rx) = mpsc::channel(100); + let (event_tx, mut event_rx) = broadcast::channel(100); + + let bridge = EventBridge::new( + event_tx, + Arc::new(RwLock::new(ThreadId::random())), + Arc::new(RwLock::new(None)), + ); + + let mut processor = AgentProcessor::with_defaults(msg_rx, bridge); + + // Spawn processor in background + let handle = tokio::spawn(async move { + processor.run().await; + }); + + // Send a message with no user content + let thread_id = ThreadId::random(); + let run_id = RunId::random(); + let input = RunAgentInput::new(thread_id.clone(), run_id.clone()); + // Note: no messages added + + let agent_msg = super::super::AgentMessage::new(input); + msg_tx.send(agent_msg).await.expect("Should send"); + + // Should receive RunStarted then RunError + let event = tokio::time::timeout( + std::time::Duration::from_millis(100), + event_rx.recv() + ).await.expect("Should receive event").expect("Should have event"); + + assert!(matches!(event, Event::RunStarted(_))); + + let event = tokio::time::timeout( + std::time::Duration::from_millis(100), + event_rx.recv() + ).await.expect("Should receive event").expect("Should have event"); + + assert!(matches!(event, Event::RunError(_))); + + drop(msg_tx); + let _ = tokio::time::timeout(std::time::Duration::from_millis(100), handle).await; + } +} diff --git a/src/server/routes.rs b/src/server/routes.rs new file mode 100644 index 00000000..b9a170d8 --- /dev/null +++ b/src/server/routes.rs @@ -0,0 +1,540 @@ +//! HTTP Routes for AG-UI Server +//! +//! This module provides the HTTP endpoints for AG-UI protocol: +//! - `/sse` - Server-Sent Events endpoint +//! - `/ws` - WebSocket endpoint +//! - `/health` - Health check endpoint + +use std::convert::Infallible; + +use axum::{ + extract::{ + ws::{Message, WebSocket, WebSocketUpgrade}, + State, + }, + response::{ + sse::{Event as SseEvent, KeepAlive, Sse}, + IntoResponse, Response, + }, + Json, +}; +use futures_util::{SinkExt, Stream, StreamExt}; +use serde::Deserialize; +use serde_json::json; +use tokio_stream::wrappers::BroadcastStream; +use tracing::{debug, warn}; + +use super::{AgentMessage, RunAgentInput, ServerState}; + +/// Health check endpoint. +pub async fn health() -> Json { + Json(json!({ + "status": "ok", + "service": "syncable-cli-agent", + "protocol": "ag-ui" + })) +} + +/// Runtime info endpoint for CopilotKit. +/// +/// CopilotKit expects this endpoint to return information about +/// available agents and actions. Agents must be an object/map with +/// agent ID as key, not an array. +pub async fn info() -> Json { + Json(json!({ + "version": "1.0.0", + "agents": { + "syncable": { + "name": "syncable", + "className": "HttpAgent", + "description": "Syncable CLI Agent - Kubernetes and DevOps assistant" + } + }, + "actions": {}, + "audioFileTranscriptionEnabled": false + })) +} + +/// CopilotKit request body structure. +/// CopilotKit wraps requests in an envelope with method, params, and body. +#[derive(Debug, Clone, Deserialize)] +pub struct CopilotKitRequest { + /// The method being called (e.g., "agent/run") + pub method: Option, + /// Method parameters + pub params: Option, + /// The actual request body + pub body: Option, + /// Direct fields for RunAgentInput format (non-envelope) + #[serde(rename = "threadId")] + pub thread_id: Option, + #[serde(rename = "runId")] + pub run_id: Option, + pub messages: Option>, + pub tools: Option>, + pub context: Option>, + pub state: Option, + #[serde(rename = "forwardedProps")] + pub forwarded_props: Option, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct CopilotKitParams { + #[serde(rename = "agentId")] + pub agent_id: Option, + #[serde(rename = "threadId")] + pub thread_id: Option, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct CopilotKitBody { + pub messages: Option>, + #[serde(rename = "threadId")] + pub thread_id: Option, + #[serde(rename = "runId")] + pub run_id: Option, + pub tools: Option>, + pub context: Option>, + pub state: Option, + #[serde(rename = "forwardedProps")] + pub forwarded_props: Option, +} + +/// POST endpoint for receiving messages via HTTP. +/// +/// Accepts both CopilotKit envelope format and direct RunAgentInput format. +/// Routes messages to the agent processor and returns an SSE stream of events. +/// Also handles CopilotKit's "info" method requests. +pub async fn post_message( + State(state): State, + Json(raw): Json, +) -> Response { + debug!("Received POST request body: {}", serde_json::to_string_pretty(&raw).unwrap_or_default()); + + // Try to parse as CopilotKit request + let copilot_req: Result = serde_json::from_value(raw.clone()); + + // Track original thread/run IDs for response (may not be valid UUIDs) + let (input, original_thread_id, original_run_id) = match copilot_req { + Ok(req) => { + // Check if this is an envelope format (has method field) + if let Some(ref method) = req.method { + debug!("Detected CopilotKit envelope format, method: {:?}", method); + + // Handle "info" method - return runtime info + if method == "info" { + return Json(json!({ + "version": "1.0.0", + "agents": { + "syncable": { + "name": "syncable", + "className": "HttpAgent", + "description": "Syncable CLI Agent - Kubernetes and DevOps assistant" + } + }, + "actions": {}, + "audioFileTranscriptionEnabled": false + })).into_response(); + } + + // Extract from envelope body + let body = req.body.unwrap_or(CopilotKitBody { + messages: None, + thread_id: None, + run_id: None, + tools: None, + context: None, + state: None, + forwarded_props: None, + }); + + let thread_id_str = body.thread_id + .or(req.params.as_ref().and_then(|p| p.thread_id.clone())) + .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); + let run_id_str = body.run_id + .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); + + // Parse IDs, falling back to random if invalid UUID + let thread_id: ag_ui_core::ThreadId = thread_id_str.parse() + .unwrap_or_else(|_| ag_ui_core::ThreadId::random()); + let run_id: ag_ui_core::RunId = run_id_str.parse() + .unwrap_or_else(|_| ag_ui_core::RunId::random()); + + // Convert messages from JSON to Message type + let messages = convert_messages(body.messages.unwrap_or_default()); + let tools = convert_tools(body.tools.unwrap_or_default()); + let context = convert_context(body.context.unwrap_or_default()); + + let input = RunAgentInput::new(thread_id, run_id) + .with_messages(messages) + .with_tools(tools) + .with_context(context) + .with_state(body.state.unwrap_or(serde_json::Value::Null)) + .with_forwarded_props(body.forwarded_props.unwrap_or(serde_json::Value::Null)); + + (input, thread_id_str, run_id_str) + } else if req.thread_id.is_some() || req.messages.is_some() { + // Direct RunAgentInput format + debug!("Detected direct RunAgentInput format"); + + let thread_id_str = req.thread_id + .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); + let run_id_str = req.run_id + .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); + + // Parse IDs, falling back to random if invalid UUID + let thread_id: ag_ui_core::ThreadId = thread_id_str.parse() + .unwrap_or_else(|_| ag_ui_core::ThreadId::random()); + let run_id: ag_ui_core::RunId = run_id_str.parse() + .unwrap_or_else(|_| ag_ui_core::RunId::random()); + + let messages = convert_messages(req.messages.unwrap_or_default()); + let tools = convert_tools(req.tools.unwrap_or_default()); + let context = convert_context(req.context.unwrap_or_default()); + + let input = RunAgentInput::new(thread_id, run_id) + .with_messages(messages) + .with_tools(tools) + .with_context(context) + .with_state(req.state.unwrap_or(serde_json::Value::Null)) + .with_forwarded_props(req.forwarded_props.unwrap_or(serde_json::Value::Null)); + + (input, thread_id_str, run_id_str) + } else { + warn!("Could not parse request format: {:?}", raw); + return Json(json!({ + "status": "error", + "message": "Invalid request format" + })).into_response(); + } + } + Err(e) => { + warn!("Failed to parse request: {}", e); + return Json(json!({ + "status": "error", + "message": format!("Failed to parse request: {}", e) + })).into_response(); + } + }; + + // Use original string IDs for response (preserves non-UUID IDs like "thread-123") + let thread_id = original_thread_id; + let run_id = original_run_id; + + debug!( + thread_id = %thread_id, + run_id = %run_id, + message_count = input.messages.len(), + "Processed RunAgentInput via POST" + ); + + // Subscribe to events BEFORE sending message to avoid race condition + let mut event_rx = state.subscribe(); + + let message_tx = state.message_sender(); + let agent_msg = AgentMessage::new(input); + + if let Err(e) = message_tx.send(agent_msg).await { + warn!("Failed to route message to agent processor: {}", e); + return Json(json!({ + "status": "error", + "message": "Failed to route message to agent processor" + })).into_response(); + } + + // Create SSE stream that filters events and ends on RunFinished/RunError + let stream = async_stream::stream! { + use ag_ui_core::Event; + + loop { + match event_rx.recv().await { + Ok(event) => { + let is_terminal = matches!(&event, Event::RunFinished(_) | Event::RunError(_)); + + // Serialize event to JSON + if let Ok(json) = serde_json::to_string(&event) { + let event_type = event.event_type().as_str().to_string(); + yield Ok::<_, Infallible>(SseEvent::default() + .event(event_type) + .data(json)); + } + + // Stop streaming after terminal event + if is_terminal { + break; + } + } + Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => { + // Missed some events, continue + continue; + } + Err(tokio::sync::broadcast::error::RecvError::Closed) => { + // Channel closed, stop streaming + break; + } + } + } + }; + + Sse::new(stream).keep_alive(KeepAlive::default()).into_response() +} + +/// Convert JSON messages to AG-UI Message type +fn convert_messages(raw_messages: Vec) -> Vec { + use ag_ui_core::MessageId; + + raw_messages + .into_iter() + .filter_map(|msg| { + let role = msg.get("role")?.as_str()?; + let content = msg.get("content").and_then(|c| c.as_str()).unwrap_or(""); + let id_str = msg.get("id") + .and_then(|i| i.as_str()) + .map(|s| s.to_string()) + .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); + + // Parse ID, falling back to random if invalid UUID format + let id: MessageId = id_str.parse().unwrap_or_else(|_| MessageId::random()); + + match role { + "user" => Some(ag_ui_core::types::Message::User { + id, + content: content.to_string(), + name: msg.get("name").and_then(|n| n.as_str()).map(String::from), + }), + "assistant" => Some(ag_ui_core::types::Message::Assistant { + id, + content: Some(content.to_string()), + name: msg.get("name").and_then(|n| n.as_str()).map(String::from), + tool_calls: None, + }), + "system" => Some(ag_ui_core::types::Message::System { + id, + content: content.to_string(), + name: msg.get("name").and_then(|n| n.as_str()).map(String::from), + }), + _ => { + debug!("Unknown message role: {}", role); + None + } + } + }) + .collect() +} + +/// Convert JSON tools to AG-UI Tool type +fn convert_tools(raw_tools: Vec) -> Vec { + raw_tools + .into_iter() + .filter_map(|tool| { + let name = tool.get("name")?.as_str()?.to_string(); + let description = tool.get("description") + .and_then(|d| d.as_str()) + .unwrap_or("") + .to_string(); + let parameters = tool.get("parameters") + .cloned() + .unwrap_or(serde_json::json!({})); + + Some(ag_ui_core::types::Tool::new(name, description, parameters)) + }) + .collect() +} + +/// Convert JSON context to AG-UI Context type +fn convert_context(raw_context: Vec) -> Vec { + raw_context + .into_iter() + .filter_map(|ctx| { + let description = ctx.get("description")?.as_str()?.to_string(); + let value = ctx.get("value")?.as_str()?.to_string(); + Some(ag_ui_core::types::Context::new(description, value)) + }) + .collect() +} + +/// SSE endpoint for streaming AG-UI events. +pub async fn sse_handler( + State(state): State, +) -> Sse>> { + let rx = state.subscribe(); + let stream = BroadcastStream::new(rx); + + let event_stream = stream.filter_map(|result| async move { + match result { + Ok(event) => { + // Serialize event to JSON + let json = serde_json::to_string(&event).ok()?; + let event_type = event.event_type().as_str().to_string(); + + Some(Ok(SseEvent::default() + .event(event_type) + .data(json))) + } + Err(_) => None, // Lagged, skip this event + } + }); + + Sse::new(event_stream).keep_alive(KeepAlive::default()) +} + +/// WebSocket endpoint for streaming AG-UI events. +pub async fn ws_handler( + ws: WebSocketUpgrade, + State(state): State, +) -> Response { + ws.on_upgrade(move |socket| handle_websocket(socket, state)) +} + +/// Handles a WebSocket connection. +async fn handle_websocket(socket: WebSocket, state: ServerState) { + let (mut sender, mut receiver) = socket.split(); + let mut event_rx = state.subscribe(); + let message_tx = state.message_sender(); + + // Spawn task to send events to client + let send_task = tokio::spawn(async move { + while let Ok(event) = event_rx.recv().await { + if let Ok(json) = serde_json::to_string(&event) { + if sender.send(Message::Text(json.into())).await.is_err() { + break; // Client disconnected + } + } + } + }); + + // Handle incoming messages from client + let recv_task = tokio::spawn(async move { + while let Some(msg) = receiver.next().await { + match msg { + Ok(Message::Close(_)) => break, + Ok(Message::Ping(_)) => { + // Pong is handled automatically by axum + } + Ok(Message::Text(text)) => { + // Parse as RunAgentInput and route to agent processor + match serde_json::from_str::(&text) { + Ok(input) => { + debug!( + thread_id = %input.thread_id, + run_id = %input.run_id, + message_count = input.messages.len(), + "Received RunAgentInput via WebSocket" + ); + let agent_msg = AgentMessage::new(input); + if let Err(e) = message_tx.send(agent_msg).await { + warn!("Failed to send message to agent processor: {}", e); + } + } + Err(e) => { + warn!("Failed to parse WebSocket message as RunAgentInput: {}", e); + // Log but continue - don't crash the connection + } + } + } + Ok(Message::Binary(_)) => { + // Binary messages not supported yet + debug!("Received binary WebSocket message, ignoring"); + } + Ok(Message::Pong(_)) => { + // Pong response, ignore + } + Err(e) => { + warn!("WebSocket error: {}", e); + break; + } + } + } + }); + + // Wait for either task to complete + tokio::select! { + _ = send_task => {} + _ = recv_task => {} + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ag_ui_core::types::Message as AgUiProtocolMessage; + use ag_ui_core::{RunId, ThreadId}; + use axum::extract::State; + + #[tokio::test] + async fn test_health_endpoint() { + let response = health().await; + assert_eq!(response.0["status"], "ok"); + assert_eq!(response.0["protocol"], "ag-ui"); + } + + #[tokio::test] + async fn test_post_message_accepted() { + use crate::server::ServerState; + + let state = ServerState::new(); + let mut msg_rx = state.take_message_receiver().await.expect("Should get receiver"); + + // Create RunAgentInput as JSON value + let thread_id = ThreadId::random(); + let run_id = RunId::random(); + let input_json = json!({ + "threadId": thread_id.to_string(), + "runId": run_id.to_string(), + "messages": [{ + "id": "msg-1", + "role": "user", + "content": "Hello from POST" + }], + "tools": [], + "context": [], + "state": null, + "forwardedProps": null + }); + + // Call post_message handler with raw JSON + let response = post_message(State(state), Json(input_json)).await; + + // Verify response + assert_eq!(response.0["status"], "accepted"); + assert_eq!(response.0["thread_id"], thread_id.to_string()); + assert_eq!(response.0["run_id"], run_id.to_string()); + + // Verify message was routed + let received = msg_rx.recv().await.expect("Should receive message"); + assert_eq!(received.input.messages.len(), 1); + } + + #[tokio::test] + async fn test_post_message_copilotkit_envelope() { + use crate::server::ServerState; + + let state = ServerState::new(); + let mut msg_rx = state.take_message_receiver().await.expect("Should get receiver"); + + // Create CopilotKit envelope format + let input_json = json!({ + "method": "agent/run", + "params": { "agentId": "syncable" }, + "body": { + "threadId": "thread-123", + "messages": [{ + "id": "msg-1", + "role": "user", + "content": "Hello from CopilotKit" + }] + } + }); + + // Call post_message handler + let response = post_message(State(state), Json(input_json)).await; + + // Verify response + assert_eq!(response.0["status"], "accepted"); + assert_eq!(response.0["thread_id"], "thread-123"); + + // Verify message was routed + let received = msg_rx.recv().await.expect("Should receive message"); + assert_eq!(received.input.messages.len(), 1); + } +} diff --git a/src/wizard/cloud_provider_data.rs b/src/wizard/cloud_provider_data.rs index 1058078d..398c08ae 100644 --- a/src/wizard/cloud_provider_data.rs +++ b/src/wizard/cloud_provider_data.rs @@ -1,25 +1,29 @@ //! Cloud provider regions and machine types for the deployment wizard //! -//! This module contains static data for cloud provider options, -//! matching the frontend's cloudProviderData.ts for consistency. +//! For Hetzner: Uses DYNAMIC fetching from Hetzner API for real-time +//! availability and pricing. No hardcoded fallback - ensures agent always +//! uses current data for smart resource selection. +//! +//! For GCP: Uses static data (dynamic fetching not yet implemented). -use crate::platform::api::types::CloudProvider; +use crate::platform::api::client::PlatformApiClient; +use crate::platform::api::types::{CloudProvider, LocationWithAvailability, ServerTypeSummary}; -/// A cloud region/location option +/// A cloud region/location option (static data for non-Hetzner providers) #[derive(Debug, Clone)] pub struct CloudRegion { - /// Region ID (e.g., "nbg1", "us-central1") + /// Region ID (e.g., "us-central1") pub id: &'static str, - /// Human-readable name (e.g., "Nuremberg", "Iowa") + /// Human-readable name (e.g., "Iowa") pub name: &'static str, - /// Geographic location (e.g., "Germany", "US Central") + /// Geographic location (e.g., "US Central") pub location: &'static str, } -/// A machine/instance type option +/// A machine/instance type option (static data for non-Hetzner providers) #[derive(Debug, Clone)] pub struct MachineType { - /// Machine type ID (e.g., "cx22", "e2-small") + /// Machine type ID (e.g., "e2-small") pub id: &'static str, /// Display name pub name: &'static str, @@ -27,56 +31,12 @@ pub struct MachineType { pub cpu: &'static str, /// Memory amount (e.g., "4 GB") pub memory: &'static str, - /// Optional description (e.g., "Shared Intel", "ARM64") + /// Optional description (e.g., "Shared-core") pub description: Option<&'static str>, } // ============================================================================= -// Hetzner Cloud -// ============================================================================= - -/// Hetzner Cloud locations -pub static HETZNER_LOCATIONS: &[CloudRegion] = &[ - // Europe - CloudRegion { id: "nbg1", name: "Nuremberg", location: "Germany" }, - CloudRegion { id: "fsn1", name: "Falkenstein", location: "Germany" }, - CloudRegion { id: "hel1", name: "Helsinki", location: "Finland" }, - // Americas - CloudRegion { id: "ash", name: "Ashburn", location: "US East" }, - CloudRegion { id: "hil", name: "Hillsboro", location: "US West" }, - // Asia Pacific - CloudRegion { id: "sin", name: "Singapore", location: "Southeast Asia" }, -]; - -/// Hetzner Cloud server types (updated January 2026 naming) -pub static HETZNER_SERVER_TYPES: &[MachineType] = &[ - // Shared vCPU - CX Series (Intel/AMD cost-optimized) - MachineType { id: "cx23", name: "CX23", cpu: "2", memory: "4 GB", description: Some("Shared Intel/AMD") }, - MachineType { id: "cx33", name: "CX33", cpu: "4", memory: "8 GB", description: Some("Shared Intel/AMD") }, - MachineType { id: "cx43", name: "CX43", cpu: "8", memory: "16 GB", description: Some("Shared Intel/AMD") }, - MachineType { id: "cx53", name: "CX53", cpu: "16", memory: "32 GB", description: Some("Shared Intel/AMD") }, - // Shared vCPU - CPX Series (AMD regular) - MachineType { id: "cpx22", name: "CPX22", cpu: "2", memory: "4 GB", description: Some("Shared AMD") }, - MachineType { id: "cpx32", name: "CPX32", cpu: "4", memory: "8 GB", description: Some("Shared AMD") }, - MachineType { id: "cpx42", name: "CPX42", cpu: "8", memory: "16 GB", description: Some("Shared AMD") }, - MachineType { id: "cpx52", name: "CPX52", cpu: "12", memory: "24 GB", description: Some("Shared AMD") }, - MachineType { id: "cpx62", name: "CPX62", cpu: "16", memory: "32 GB", description: Some("Shared AMD") }, - // Dedicated vCPU - CCX Series (AMD) - MachineType { id: "ccx13", name: "CCX13", cpu: "2", memory: "8 GB", description: Some("Dedicated AMD") }, - MachineType { id: "ccx23", name: "CCX23", cpu: "4", memory: "16 GB", description: Some("Dedicated AMD") }, - MachineType { id: "ccx33", name: "CCX33", cpu: "8", memory: "32 GB", description: Some("Dedicated AMD") }, - MachineType { id: "ccx43", name: "CCX43", cpu: "16", memory: "64 GB", description: Some("Dedicated AMD") }, - MachineType { id: "ccx53", name: "CCX53", cpu: "32", memory: "128 GB", description: Some("Dedicated AMD") }, - MachineType { id: "ccx63", name: "CCX63", cpu: "48", memory: "192 GB", description: Some("Dedicated AMD") }, - // ARM - CAX Series (Ampere) - MachineType { id: "cax11", name: "CAX11", cpu: "2", memory: "4 GB", description: Some("ARM64 Ampere") }, - MachineType { id: "cax21", name: "CAX21", cpu: "4", memory: "8 GB", description: Some("ARM64 Ampere") }, - MachineType { id: "cax31", name: "CAX31", cpu: "8", memory: "16 GB", description: Some("ARM64 Ampere") }, - MachineType { id: "cax41", name: "CAX41", cpu: "16", memory: "32 GB", description: Some("ARM64 Ampere") }, -]; - -// ============================================================================= -// GCP (Google Cloud Platform) +// GCP (Google Cloud Platform) - Static data // ============================================================================= /// GCP regions @@ -116,24 +76,26 @@ pub static GCP_MACHINE_TYPES: &[MachineType] = &[ ]; // ============================================================================= -// Helper Functions +// Static Helper Functions (for non-Hetzner providers only) // ============================================================================= -/// Get regions for a cloud provider +/// Get static regions for a cloud provider +/// NOTE: For Hetzner, returns empty - use get_hetzner_regions_dynamic() instead pub fn get_regions_for_provider(provider: &CloudProvider) -> &'static [CloudRegion] { match provider { - CloudProvider::Hetzner => HETZNER_LOCATIONS, + CloudProvider::Hetzner => &[], // Use dynamic fetching for Hetzner CloudProvider::Gcp => GCP_REGIONS, - _ => &[], // AWS, Azure not yet supported for Cloud Runner + _ => &[], // AWS, Azure not yet supported } } -/// Get machine types for a cloud provider +/// Get static machine types for a cloud provider +/// NOTE: For Hetzner, returns empty - use get_hetzner_server_types_dynamic() instead pub fn get_machine_types_for_provider(provider: &CloudProvider) -> &'static [MachineType] { match provider { - CloudProvider::Hetzner => HETZNER_SERVER_TYPES, + CloudProvider::Hetzner => &[], // Use dynamic fetching for Hetzner CloudProvider::Gcp => GCP_MACHINE_TYPES, - _ => &[], // AWS, Azure not yet supported for Cloud Runner + _ => &[], // AWS, Azure not yet supported } } @@ -149,42 +111,307 @@ pub fn get_default_region(provider: &CloudProvider) -> &'static str { /// Get default machine type for a provider pub fn get_default_machine_type(provider: &CloudProvider) -> &'static str { match provider { - CloudProvider::Hetzner => "cx23", + CloudProvider::Hetzner => "cx22", CloudProvider::Gcp => "e2-small", _ => "", } } -/// Format region for display: "Nuremberg (Germany)" -pub fn format_region_display(region: &CloudRegion) -> String { - format!("{} ({})", region.name, region.location) +// ============================================================================= +// Dynamic Types and Fetching (Hetzner) +// ============================================================================= + +/// Dynamic cloud region with real-time availability info +#[derive(Debug, Clone)] +pub struct DynamicCloudRegion { + /// Region ID (e.g., "nbg1") + pub id: String, + /// Human-readable name (e.g., "Nuremberg") + pub name: String, + /// Geographic location (e.g., "Germany") + pub location: String, + /// Network zone (e.g., "eu-central") + pub network_zone: String, + /// Server types currently available in this region + pub available_server_types: Vec, } -/// Format machine type for display: "cx22 · 2 vCPU · 4 GB" -pub fn format_machine_type_display(machine: &MachineType) -> String { - let base = format!("{} · {} vCPU · {}", machine.name, machine.cpu, machine.memory); - if let Some(desc) = machine.description { - format!("{} · {}", base, desc) - } else { - base +/// Dynamic machine type with real-time pricing and availability +#[derive(Debug, Clone)] +pub struct DynamicMachineType { + /// Machine type ID (e.g., "cx22") + pub id: String, + /// Display name + pub name: String, + /// Number of vCPUs + pub cores: i32, + /// Memory in GB + pub memory_gb: f64, + /// Disk size in GB + pub disk_gb: i64, + /// Monthly price in EUR (from Hetzner API) + pub price_monthly: f64, + /// Hourly price in EUR (from Hetzner API) + pub price_hourly: f64, + /// Locations where this type is currently available + pub available_in: Vec, +} + +/// Result of dynamic Hetzner data fetch +#[derive(Debug)] +pub enum HetznerFetchResult { + /// Successfully fetched data + Success(T), + /// Failed to fetch - requires Hetzner credentials + NoCredentials, + /// Failed to fetch - API error + ApiError(String), +} + +/// Convert API LocationWithAvailability to DynamicCloudRegion +fn location_to_dynamic_region(loc: &LocationWithAvailability) -> DynamicCloudRegion { + DynamicCloudRegion { + id: loc.location.name.clone(), + name: loc.location.city.clone(), + location: loc.location.country.clone(), + network_zone: loc.location.network_zone.clone(), + available_server_types: loc.available_server_types.clone(), } } -#[cfg(test)] -mod tests { - use super::*; +/// Convert API ServerTypeSummary to DynamicMachineType +fn server_type_to_dynamic(st: &ServerTypeSummary) -> DynamicMachineType { + DynamicMachineType { + id: st.name.clone(), + name: st.name.clone(), + cores: st.cores, + memory_gb: st.memory_gb, + disk_gb: st.disk_gb, + price_monthly: st.price_monthly, + price_hourly: st.price_hourly, + available_in: st.available_in.clone(), + } +} - #[test] - fn test_hetzner_locations() { - assert!(!HETZNER_LOCATIONS.is_empty()); - assert!(HETZNER_LOCATIONS.iter().any(|r| r.id == "nbg1")); +/// Fetch Hetzner regions dynamically with REAL-TIME availability +/// +/// Uses the /api/deployments/availability/locations endpoint which checks +/// Hetzner's datacenter API for actual capacity - not just what exists. +/// Returns only regions where server types are CURRENTLY available. +/// +/// # Errors +/// Returns error if credentials are missing or API call fails. +pub async fn get_hetzner_regions_dynamic( + client: &PlatformApiClient, + project_id: &str, +) -> HetznerFetchResult> { + match client.get_hetzner_locations(project_id).await { + Ok(locations) => { + HetznerFetchResult::Success(locations.iter().map(location_to_dynamic_region).collect()) + } + Err(e) => { + let error_msg = e.to_string(); + // Check for various credential-related error patterns + if error_msg.contains("credentials") + || error_msg.contains("Unauthorized") + || error_msg.contains("token") + || error_msg.contains("API token") + || error_msg.contains("401") + || error_msg.contains("412") // failedPrecondition + { + HetznerFetchResult::NoCredentials + } else { + HetznerFetchResult::ApiError(error_msg) + } + } + } +} + +/// Fetch Hetzner server types dynamically with REAL-TIME availability and pricing +/// +/// Uses the /api/deployments/availability/server-types endpoint which returns +/// server types sorted by price with ACTUAL availability per datacenter. +/// Only returns server types that are currently in stock. +/// +/// # Errors +/// Returns error if credentials are missing or API call fails. +pub async fn get_hetzner_server_types_dynamic( + client: &PlatformApiClient, + project_id: &str, + preferred_location: Option<&str>, +) -> HetznerFetchResult> { + match client.get_hetzner_server_types(project_id, preferred_location).await { + Ok(server_types) => { + HetznerFetchResult::Success(server_types.iter().map(server_type_to_dynamic).collect()) + } + Err(e) => { + let error_msg = e.to_string(); + // Check for various credential-related error patterns + if error_msg.contains("credentials") + || error_msg.contains("Unauthorized") + || error_msg.contains("token") + || error_msg.contains("API token") + || error_msg.contains("401") + || error_msg.contains("412") // failedPrecondition + { + HetznerFetchResult::NoCredentials + } else { + HetznerFetchResult::ApiError(error_msg) + } + } } +} - #[test] - fn test_hetzner_machine_types() { - assert!(!HETZNER_SERVER_TYPES.is_empty()); - assert!(HETZNER_SERVER_TYPES.iter().any(|m| m.id == "cx23")); +/// Check availability of a specific server type at a location +/// +/// Returns (available, reason, alternative_locations): +/// - available: true if the server type can be provisioned now +/// - reason: None if available, Some("capacity"|"unsupported") if not +/// - alternative_locations: Other locations where this server type IS available +/// +/// The agent uses this for pre-deployment validation and smart fallback. +pub async fn check_hetzner_availability( + client: &PlatformApiClient, + project_id: &str, + location: &str, + server_type: &str, +) -> (bool, Option, Vec) { + match client.check_hetzner_availability(project_id, location, server_type).await { + Ok(result) => ( + result.available, + result.reason, + result.alternative_locations.unwrap_or_default(), + ), + Err(e) => { + // On error, return unavailable with error message + (false, Some(format!("Failed to check: {}", e)), vec![]) + } } +} + +/// Get recommended server type for a workload profile +/// +/// Fetches real-time pricing and returns the cheapest server type meeting requirements: +/// - minimal: 1 core, 2GB RAM (development/testing) +/// - standard: 2 cores, 4GB RAM (small production) +/// - performance: 4 cores, 8GB RAM with dedicated CPU (production) +/// - high-memory: 2 cores, 16GB RAM (memory-intensive workloads) +/// +/// The agent uses this for intelligent resource recommendations. +pub async fn get_recommended_server_type( + client: &PlatformApiClient, + project_id: &str, + profile: &str, + preferred_location: Option<&str>, +) -> Option { + let (min_cores, min_memory, prefer_dedicated) = match profile { + "minimal" => (1, 2.0, false), + "standard" => (2, 4.0, false), + "performance" => (4, 8.0, true), + "high-memory" => (2, 16.0, false), + _ => (2, 4.0, false), // Default to standard + }; + + let server_types = match get_hetzner_server_types_dynamic(client, project_id, preferred_location).await { + HetznerFetchResult::Success(types) => types, + _ => return None, + }; + + // Filter by requirements and find cheapest + server_types + .into_iter() + .filter(|st| { + st.cores >= min_cores + && st.memory_gb >= min_memory + && (!prefer_dedicated || st.name.starts_with("ccx")) + }) + .filter(|st| { + // If preferred location is set, only include types available there + preferred_location.map_or(true, |loc| st.available_in.contains(&loc.to_string())) + }) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) +} + +/// Find the best region for a workload based on availability +/// +/// Returns the region with the most available server types, +/// preferring regions in the specified network zone. +pub async fn find_best_region( + client: &PlatformApiClient, + project_id: &str, + preferred_zone: Option<&str>, +) -> Option { + let regions = match get_hetzner_regions_dynamic(client, project_id).await { + HetznerFetchResult::Success(r) => r, + _ => return None, + }; + + // Sort by availability count, preferring specified zone + let mut sorted_regions = regions; + sorted_regions.sort_by(|a, b| { + let a_zone_match = preferred_zone.map_or(false, |z| a.network_zone == z); + let b_zone_match = preferred_zone.map_or(false, |z| b.network_zone == z); + + match (a_zone_match, b_zone_match) { + (true, false) => std::cmp::Ordering::Less, + (false, true) => std::cmp::Ordering::Greater, + _ => b.available_server_types.len().cmp(&a.available_server_types.len()), + } + }); + + sorted_regions.into_iter().next() +} + +/// Find cheapest available server type for a region +/// +/// Returns the cheapest server type that is currently available +/// in the specified region. +pub async fn find_cheapest_available( + client: &PlatformApiClient, + project_id: &str, + region: &str, +) -> Option { + let server_types = match get_hetzner_server_types_dynamic(client, project_id, Some(region)).await { + HetznerFetchResult::Success(types) => types, + _ => return None, + }; + + // Filter to only available types in this region, sort by price + server_types + .into_iter() + .filter(|st| st.available_in.contains(®ion.to_string())) + .min_by(|a, b| a.price_monthly.partial_cmp(&b.price_monthly).unwrap()) +} + +// ============================================================================= +// Display Formatting +// ============================================================================= + +/// Format dynamic region for display +pub fn format_dynamic_region_display(region: &DynamicCloudRegion) -> String { + if region.available_server_types.is_empty() { + format!("{} ({}) - checking availability...", region.name, region.location) + } else { + format!( + "{} ({}) · {} server types available", + region.name, + region.location, + region.available_server_types.len() + ) + } +} + +/// Format dynamic machine type for display with pricing +pub fn format_dynamic_machine_type_display(machine: &DynamicMachineType) -> String { + format!( + "{} · {} vCPU · {:.0} GB · €{:.2}/mo", + machine.name, machine.cores, machine.memory_gb, machine.price_monthly + ) +} + +#[cfg(test)] +mod tests { + use super::*; #[test] fn test_gcp_regions() { @@ -199,28 +426,62 @@ mod tests { } #[test] - fn test_get_regions_for_provider() { - let hetzner_regions = get_regions_for_provider(&CloudProvider::Hetzner); - assert!(!hetzner_regions.is_empty()); + fn test_hetzner_returns_empty_static() { + // Hetzner should return empty from static functions + // because we want to force dynamic fetching + let regions = get_regions_for_provider(&CloudProvider::Hetzner); + assert!(regions.is_empty()); + + let machines = get_machine_types_for_provider(&CloudProvider::Hetzner); + assert!(machines.is_empty()); + } + + #[test] + fn test_gcp_returns_static_data() { + let regions = get_regions_for_provider(&CloudProvider::Gcp); + assert!(!regions.is_empty()); + + let machines = get_machine_types_for_provider(&CloudProvider::Gcp); + assert!(!machines.is_empty()); + } - let gcp_regions = get_regions_for_provider(&CloudProvider::Gcp); - assert!(!gcp_regions.is_empty()); + #[test] + fn test_defaults() { + assert_eq!(get_default_region(&CloudProvider::Hetzner), "nbg1"); + assert_eq!(get_default_region(&CloudProvider::Gcp), "us-central1"); + assert_eq!(get_default_machine_type(&CloudProvider::Hetzner), "cx22"); + assert_eq!(get_default_machine_type(&CloudProvider::Gcp), "e2-small"); } #[test] - fn test_format_region_display() { - let region = &HETZNER_LOCATIONS[0]; - let display = format_region_display(region); + fn test_dynamic_region_display() { + let region = DynamicCloudRegion { + id: "nbg1".to_string(), + name: "Nuremberg".to_string(), + location: "Germany".to_string(), + network_zone: "eu-central".to_string(), + available_server_types: vec!["cx22".to_string(), "cx32".to_string()], + }; + let display = format_dynamic_region_display(®ion); assert!(display.contains("Nuremberg")); - assert!(display.contains("Germany")); + assert!(display.contains("2 server types")); } #[test] - fn test_format_machine_type_display() { - let machine = &HETZNER_SERVER_TYPES[0]; - let display = format_machine_type_display(machine); - assert!(display.contains("CX23")); + fn test_dynamic_machine_display() { + let machine = DynamicMachineType { + id: "cx22".to_string(), + name: "cx22".to_string(), + cores: 2, + memory_gb: 4.0, + disk_gb: 40, + price_monthly: 5.95, + price_hourly: 0.008, + available_in: vec!["nbg1".to_string()], + }; + let display = format_dynamic_machine_type_display(&machine); + assert!(display.contains("cx22")); assert!(display.contains("2 vCPU")); - assert!(display.contains("4 GB")); + assert!(display.contains("€5.95/mo")); } } diff --git a/src/wizard/infrastructure_selection.rs b/src/wizard/infrastructure_selection.rs index 347b9981..0a3f4210 100644 --- a/src/wizard/infrastructure_selection.rs +++ b/src/wizard/infrastructure_selection.rs @@ -1,11 +1,19 @@ //! Infrastructure selection step for the deployment wizard //! //! Handles region and machine type selection for Cloud Runner deployments. +//! +//! For Hetzner: Uses DYNAMIC fetching from Hetzner API - no hardcoded fallback. +//! The agent gets real-time availability and pricing for smart resource selection. +//! +//! For GCP: Uses static data (dynamic fetching not yet implemented). +use crate::platform::api::client::PlatformApiClient; use crate::platform::api::types::CloudProvider; use crate::wizard::cloud_provider_data::{ - get_default_machine_type, get_default_region, get_machine_types_for_provider, - get_regions_for_provider, CloudRegion, MachineType, + get_default_machine_type, get_default_region, + get_hetzner_regions_dynamic, get_hetzner_server_types_dynamic, + get_machine_types_for_provider, get_regions_for_provider, + DynamicCloudRegion, DynamicMachineType, HetznerFetchResult, }; use crate::wizard::render::{display_step_header, wizard_render_config}; use colored::Colorize; @@ -26,58 +34,90 @@ pub enum InfrastructureSelectionResult { Cancelled, } -/// Wrapper for displaying region options in the selection menu -struct RegionOption<'a> { - region: &'a CloudRegion, +/// Wrapper for displaying dynamic region options with availability info +struct DynamicRegionOption { + region: DynamicCloudRegion, } -impl<'a> fmt::Display for RegionOption<'a> { +impl fmt::Display for DynamicRegionOption { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let availability = if !self.region.available_server_types.is_empty() { + format!(" · {} types available", self.region.available_server_types.len()) + } else { + String::new() + }; write!( f, - "{} {}", + "{} {}{}", self.region.id.cyan(), - format!("{} ({})", self.region.name, self.region.location).dimmed() + format!("{} ({})", self.region.name, self.region.location).dimmed(), + availability.green() ) } } -/// Wrapper for displaying machine type options in the selection menu -struct MachineTypeOption<'a> { - machine: &'a MachineType, +/// Wrapper for displaying dynamic machine type options with pricing +struct DynamicMachineTypeOption { + machine: DynamicMachineType, } -impl<'a> fmt::Display for MachineTypeOption<'a> { +impl fmt::Display for DynamicMachineTypeOption { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let specs = format!("{} vCPU · {}", self.machine.cpu, self.machine.memory); - let desc = self - .machine - .description - .map(|d| format!(" · {}", d)) - .unwrap_or_default(); + let specs = format!("{} vCPU · {:.0} GB", self.machine.cores, self.machine.memory_gb); + let price = if self.machine.price_monthly > 0.0 { + format!(" · €{:.2}/mo", self.machine.price_monthly) + } else { + String::new() + }; write!( f, "{} {}{}", self.machine.name.cyan(), specs.dimmed(), - desc.dimmed() + price.green() ) } } /// Select region and machine type for Cloud Runner deployment -pub fn select_infrastructure( +/// +/// Uses dynamic fetching for Hetzner to get real-time availability and pricing. +/// Falls back to static data for other providers or if API fails. +pub async fn select_infrastructure( provider: &CloudProvider, step_number: u8, + client: Option<&PlatformApiClient>, + project_id: Option<&str>, ) -> InfrastructureSelectionResult { // Select region first - let region = match select_region(provider, step_number) { + let region = match select_region(provider, step_number, client, project_id).await { Some(r) => r, None => return InfrastructureSelectionResult::Back, }; // Then select machine type - match select_machine_type(provider, ®ion) { + match select_machine_type(provider, ®ion, client, project_id).await { + Some(machine_type) => InfrastructureSelectionResult::Selected { + region, + machine_type, + }, + None => InfrastructureSelectionResult::Back, + } +} + +/// Legacy synchronous version for backward compatibility +pub fn select_infrastructure_sync( + provider: &CloudProvider, + step_number: u8, +) -> InfrastructureSelectionResult { + // Select region first using static data + let region = match select_region_static(provider, step_number) { + Some(r) => r, + None => return InfrastructureSelectionResult::Back, + }; + + // Then select machine type using static data + match select_machine_type_static(provider) { Some(machine_type) => InfrastructureSelectionResult::Selected { region, machine_type, @@ -86,8 +126,13 @@ pub fn select_infrastructure( } } -/// Select region/location for deployment -fn select_region(provider: &CloudProvider, step_number: u8) -> Option { +/// Select region/location for deployment with dynamic fetching +async fn select_region( + provider: &CloudProvider, + step_number: u8, + client: Option<&PlatformApiClient>, + project_id: Option<&str>, +) -> Option { let provider_name = match provider { CloudProvider::Hetzner => "Hetzner", CloudProvider::Gcp => "GCP", @@ -100,6 +145,111 @@ fn select_region(provider: &CloudProvider, step_number: u8) -> Option { "Choose the geographic location for your deployment.", ); + // For Hetzner: REQUIRE dynamic fetching - no static fallback + if *provider == CloudProvider::Hetzner { + if let (Some(client), Some(project_id)) = (client, project_id) { + match get_hetzner_regions_dynamic(client, project_id).await { + HetznerFetchResult::Success(regions) => { + if regions.is_empty() { + println!( + "\n{} No Hetzner regions available. Please check your Hetzner account.", + "✗".red() + ); + return None; + } + return select_region_from_dynamic(regions, provider); + } + HetznerFetchResult::NoCredentials => { + println!( + "\n{} Hetzner credentials not configured for this project.", + "✗".red() + ); + println!( + " {} Please add your Hetzner API token in project settings.", + "→".dimmed() + ); + return None; + } + HetznerFetchResult::ApiError(err) => { + println!( + "\n{} Failed to fetch Hetzner regions: {}", + "✗".red(), + err + ); + return None; + } + } + } else { + println!( + "\n{} Cannot fetch Hetzner regions without authentication.", + "✗".red() + ); + return None; + } + } + + // For other providers: Use static data + select_region_static(provider, step_number) +} + +/// Select region from dynamic data with availability info +fn select_region_from_dynamic( + regions: Vec, + provider: &CloudProvider, +) -> Option { + if regions.is_empty() { + println!( + "\n{} No regions available for this provider.", + "⚠".yellow() + ); + return None; + } + + let default_region = get_default_region(provider); + let default_index = regions + .iter() + .position(|r| r.id == default_region) + .unwrap_or(0); + + let options: Vec = regions + .into_iter() + .map(|r| DynamicRegionOption { region: r }) + .collect(); + + let selection = Select::new("Select region:", options) + .with_render_config(wizard_render_config()) + .with_starting_cursor(default_index) + .with_help_message("Use ↑/↓ to navigate, Enter to select · Real-time availability shown") + .prompt(); + + match selection { + Ok(selected) => { + println!( + "\n{} Selected region: {} ({})", + "✓".green(), + selected.region.name.cyan(), + selected.region.id + ); + Some(selected.region.id) + } + Err(InquireError::OperationCanceled) => None, + Err(InquireError::OperationInterrupted) => None, + Err(_) => None, + } +} + +/// Select region using static data (fallback) +fn select_region_static(provider: &CloudProvider, step_number: u8) -> Option { + display_step_header( + step_number, + &format!("Select {} Region", match provider { + CloudProvider::Hetzner => "Hetzner", + CloudProvider::Gcp => "GCP", + _ => "Cloud", + }), + "Choose the geographic location for your deployment.", + ); + let regions = get_regions_for_provider(provider); if regions.is_empty() { println!( @@ -115,7 +265,19 @@ fn select_region(provider: &CloudProvider, step_number: u8) -> Option { .position(|r| r.id == default_region) .unwrap_or(0); - let options: Vec = regions.iter().map(|r| RegionOption { region: r }).collect(); + // Convert static regions to dynamic format for consistent display + let options: Vec = regions + .iter() + .map(|r| DynamicRegionOption { + region: DynamicCloudRegion { + id: r.id.to_string(), + name: r.name.to_string(), + location: r.location.to_string(), + network_zone: String::new(), + available_server_types: vec![], + }, + }) + .collect(); let selection = Select::new("Select region:", options) .with_render_config(wizard_render_config()) @@ -131,7 +293,7 @@ fn select_region(provider: &CloudProvider, step_number: u8) -> Option { selected.region.name.cyan(), selected.region.id ); - Some(selected.region.id.to_string()) + Some(selected.region.id) } Err(InquireError::OperationCanceled) => None, Err(InquireError::OperationInterrupted) => None, @@ -139,8 +301,13 @@ fn select_region(provider: &CloudProvider, step_number: u8) -> Option { } } -/// Select machine/instance type for deployment -fn select_machine_type(provider: &CloudProvider, _region: &str) -> Option { +/// Select machine/instance type for deployment with dynamic fetching +async fn select_machine_type( + provider: &CloudProvider, + region: &str, + client: Option<&PlatformApiClient>, + project_id: Option<&str>, +) -> Option { println!(); println!( "{}", @@ -151,6 +318,124 @@ fn select_machine_type(provider: &CloudProvider, _region: &str) -> Option { + if machine_types.is_empty() { + println!( + "\n{} No Hetzner server types available. Please check your Hetzner account.", + "✗".red() + ); + return None; + } + return select_machine_type_from_dynamic(machine_types, provider, region); + } + HetznerFetchResult::NoCredentials => { + println!( + "\n{} Hetzner credentials not configured for this project.", + "✗".red() + ); + println!( + " {} Please add your Hetzner API token in project settings.", + "→".dimmed() + ); + return None; + } + HetznerFetchResult::ApiError(err) => { + println!( + "\n{} Failed to fetch Hetzner server types: {}", + "✗".red(), + err + ); + return None; + } + } + } else { + println!( + "\n{} Cannot fetch Hetzner server types without authentication.", + "✗".red() + ); + return None; + } + } + + // For other providers: Use static data + select_machine_type_static(provider) +} + +/// Select machine type from dynamic data with pricing info +fn select_machine_type_from_dynamic( + machine_types: Vec, + provider: &CloudProvider, + region: &str, +) -> Option { + if machine_types.is_empty() { + println!( + "\n{} No machine types available for this provider.", + "⚠".yellow() + ); + return None; + } + + // Filter to only show types available in selected region + let available_types: Vec = machine_types + .into_iter() + .filter(|m| m.available_in.is_empty() || m.available_in.contains(®ion.to_string())) + .collect(); + + if available_types.is_empty() { + println!( + "\n{} No machine types available in {} region.", + "⚠".yellow(), + region + ); + return None; + } + + let default_machine = get_default_machine_type(provider); + let default_index = available_types + .iter() + .position(|m| m.id == default_machine) + .unwrap_or(0); + + let options: Vec = available_types + .into_iter() + .map(|m| DynamicMachineTypeOption { machine: m }) + .collect(); + + let selection = Select::new("Select machine type:", options) + .with_render_config(wizard_render_config()) + .with_starting_cursor(default_index) + .with_help_message("Sorted by price · Real-time pricing shown") + .prompt(); + + match selection { + Ok(selected) => { + let price_info = if selected.machine.price_monthly > 0.0 { + format!(" · €{:.2}/mo", selected.machine.price_monthly) + } else { + String::new() + }; + println!( + "\n{} Selected: {} ({} vCPU, {:.0} GB){}", + "✓".green(), + selected.machine.name.cyan(), + selected.machine.cores, + selected.machine.memory_gb, + price_info.green() + ); + Some(selected.machine.id) + } + Err(InquireError::OperationCanceled) => None, + Err(InquireError::OperationInterrupted) => None, + Err(_) => None, + } +} + +/// Select machine type using static data (fallback) +fn select_machine_type_static(provider: &CloudProvider) -> Option { let machine_types = get_machine_types_for_provider(provider); if machine_types.is_empty() { println!( @@ -166,9 +451,21 @@ fn select_machine_type(provider: &CloudProvider, _region: &str) -> Option = machine_types + // Convert static machine types to dynamic format for consistent display + let options: Vec = machine_types .iter() - .map(|m| MachineTypeOption { machine: m }) + .map(|m| DynamicMachineTypeOption { + machine: DynamicMachineType { + id: m.id.to_string(), + name: m.name.to_string(), + cores: m.cpu.parse().unwrap_or(2), + memory_gb: m.memory.replace(" GB", "").parse().unwrap_or(4.0), + disk_gb: 40, + price_monthly: 0.0, + price_hourly: 0.0, + available_in: vec![], + }, + }) .collect(); let selection = Select::new("Select machine type:", options) @@ -180,13 +477,13 @@ fn select_machine_type(provider: &CloudProvider, _region: &str) -> Option { println!( - "\n{} Selected: {} ({} vCPU, {})", + "\n{} Selected: {} ({} vCPU, {:.0} GB)", "✓".green(), selected.machine.name.cyan(), - selected.machine.cpu, - selected.machine.memory + selected.machine.cores, + selected.machine.memory_gb ); - Some(selected.machine.id.to_string()) + Some(selected.machine.id) } Err(InquireError::OperationCanceled) => None, Err(InquireError::OperationInterrupted) => None, @@ -199,32 +496,57 @@ mod tests { use super::*; #[test] - fn test_region_option_display() { - let region = CloudRegion { - id: "nbg1", - name: "Nuremberg", - location: "Germany", + fn test_dynamic_region_option_display() { + let region = DynamicCloudRegion { + id: "nbg1".to_string(), + name: "Nuremberg".to_string(), + location: "Germany".to_string(), + network_zone: "eu-central".to_string(), + available_server_types: vec!["cx22".to_string(), "cx32".to_string()], }; - let option = RegionOption { region: ®ion }; + let option = DynamicRegionOption { region }; let display = format!("{}", option); assert!(display.contains("nbg1")); assert!(display.contains("Nuremberg")); + assert!(display.contains("2 types available")); } #[test] - fn test_machine_type_option_display() { - let machine = MachineType { - id: "cx22", - name: "CX22", - cpu: "2", - memory: "4 GB", - description: Some("Shared Intel"), + fn test_dynamic_machine_type_option_display() { + let machine = DynamicMachineType { + id: "cx22".to_string(), + name: "CX22".to_string(), + cores: 2, + memory_gb: 4.0, + disk_gb: 40, + price_monthly: 5.95, + price_hourly: 0.008, + available_in: vec!["nbg1".to_string()], }; - let option = MachineTypeOption { machine: &machine }; + let option = DynamicMachineTypeOption { machine }; let display = format!("{}", option); assert!(display.contains("CX22")); assert!(display.contains("2 vCPU")); assert!(display.contains("4 GB")); + assert!(display.contains("€5.95/mo")); + } + + #[test] + fn test_dynamic_machine_type_option_display_no_price() { + let machine = DynamicMachineType { + id: "cx22".to_string(), + name: "CX22".to_string(), + cores: 2, + memory_gb: 4.0, + disk_gb: 40, + price_monthly: 0.0, + price_hourly: 0.0, + available_in: vec![], + }; + let option = DynamicMachineTypeOption { machine }; + let display = format!("{}", option); + assert!(display.contains("CX22")); + assert!(!display.contains("€")); } #[test] diff --git a/src/wizard/mod.rs b/src/wizard/mod.rs index 8d2c15f6..25d54e24 100644 --- a/src/wizard/mod.rs +++ b/src/wizard/mod.rs @@ -21,13 +21,20 @@ mod target_selection; pub use cloud_provider_data::{ get_default_machine_type, get_default_region, get_machine_types_for_provider, get_regions_for_provider, CloudRegion, MachineType, + // Dynamic Hetzner availability functions for agent use + get_hetzner_regions_dynamic, get_hetzner_server_types_dynamic, + check_hetzner_availability, get_recommended_server_type, + find_best_region, find_cheapest_available, + DynamicCloudRegion, DynamicMachineType, HetznerFetchResult, }; pub use cluster_selection::{select_cluster, ClusterSelectionResult}; pub use config_form::{collect_config, ConfigFormResult}; pub use dockerfile_selection::{select_dockerfile, DockerfileSelectionResult}; pub use environment_creation::{create_environment_wizard, EnvironmentCreationResult}; pub use environment_selection::{select_environment, EnvironmentSelectionResult}; -pub use infrastructure_selection::{select_infrastructure, InfrastructureSelectionResult}; +pub use infrastructure_selection::{ + select_infrastructure, select_infrastructure_sync, InfrastructureSelectionResult, +}; pub use orchestrator::{run_wizard, WizardResult}; pub use provider_selection::{ get_provider_deployment_statuses, select_provider, ProviderSelectionResult, diff --git a/src/wizard/orchestrator.rs b/src/wizard/orchestrator.rs index b6d6aa57..ec5172a7 100644 --- a/src/wizard/orchestrator.rs +++ b/src/wizard/orchestrator.rs @@ -178,7 +178,8 @@ pub async fn run_wizard( // Step 3: Infrastructure selection for Cloud Runner OR Cluster selection for K8s let (cluster_id, region, machine_type) = if target == DeploymentTarget::CloudRunner { // Cloud Runner: Select region and machine type - match select_infrastructure(&provider, 3) { + // Pass client and project_id for dynamic Hetzner availability fetching + match select_infrastructure(&provider, 3, Some(client), Some(project_id)).await { InfrastructureSelectionResult::Selected { region, machine_type, diff --git a/src/wizard/recommendations.rs b/src/wizard/recommendations.rs index d275a1dd..152cbb70 100644 --- a/src/wizard/recommendations.rs +++ b/src/wizard/recommendations.rs @@ -475,6 +475,7 @@ fn build_alternatives(selected_provider: &CloudProvider, available_providers: &[ .collect(); // Build machine type options for selected provider + // For Hetzner, returns empty - agent must use list_hetzner_availability tool let machine_types: Vec = get_machine_types_for_provider(selected_provider) .iter() .map(|m| MachineOption { @@ -486,6 +487,7 @@ fn build_alternatives(selected_provider: &CloudProvider, available_providers: &[ .collect(); // Build region options for selected provider + // For Hetzner, returns empty - agent must use list_hetzner_availability tool let regions: Vec = get_regions_for_provider(selected_provider) .iter() .map(|r| RegionOption { diff --git a/tests/ag-ui-app/backend/.dockerignore b/tests/ag-ui-app/backend/.dockerignore new file mode 100644 index 00000000..08cf4d3e --- /dev/null +++ b/tests/ag-ui-app/backend/.dockerignore @@ -0,0 +1,42 @@ +# Dependencies +node_modules + +# Build output +dist + +# Development files +.git +.gitignore +.vscode +.idea +*.md + +# Environment files (sensitive) +.env +.env.* +!.env.example + +# Test files +*.test.ts +*.spec.ts +__tests__ +coverage + +# Docker files +Dockerfile* +docker-compose* +.dockerignore + +# SQLite database files (will be created at runtime) +*.db +*.sqlite +*.sqlite3 +data/ + +# Logs +logs +*.log + +# OS files +.DS_Store +Thumbs.db diff --git a/tests/ag-ui-app/backend/.gitignore b/tests/ag-ui-app/backend/.gitignore new file mode 100644 index 00000000..133f9fb2 --- /dev/null +++ b/tests/ag-ui-app/backend/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +.env +data/*.db +data/*.db-wal +data/*.db-shm diff --git a/tests/ag-ui-app/backend/.npmrc b/tests/ag-ui-app/backend/.npmrc new file mode 100644 index 00000000..2da0e971 --- /dev/null +++ b/tests/ag-ui-app/backend/.npmrc @@ -0,0 +1 @@ +onlyBuiltDependencies=["better-sqlite3"] diff --git a/tests/ag-ui-app/backend/Dockerfile b/tests/ag-ui-app/backend/Dockerfile new file mode 100644 index 00000000..b4b74c8b --- /dev/null +++ b/tests/ag-ui-app/backend/Dockerfile @@ -0,0 +1,52 @@ +# ============================================================================= +# Backend Dockerfile - Smart Reply API (Hono + better-sqlite3) +# Using Debian slim for better native module compatibility +# ============================================================================= + +FROM node:20-slim + +# Install dumb-init for proper signal handling +RUN apt-get update && apt-get install -y --no-install-recommends \ + dumb-init \ + wget \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Enable pnpm +RUN corepack enable pnpm + +# Copy package files and .npmrc for pnpm config +COPY package.json pnpm-lock.yaml* .npmrc* ./ + +# Install dependencies - .npmrc allows better-sqlite3 to run build scripts +RUN pnpm install --frozen-lockfile + +# Copy source code and build +COPY . . +RUN pnpm build + +# Create non-root user for security +RUN groupadd --system --gid 1001 nodejs && \ + useradd --system --uid 1001 --gid nodejs appuser && \ + mkdir -p /app/data && chown -R appuser:nodejs /app/data + +# Switch to non-root user +USER appuser + +# Environment variables +ENV NODE_ENV=production +ENV PORT=3001 +ENV HOST=0.0.0.0 + +# Expose port +EXPOSE 3001 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1 + +# Use dumb-init as entrypoint +ENTRYPOINT ["dumb-init", "--"] + +CMD ["node", "dist/index.js"] diff --git a/tests/ag-ui-app/backend/data/.gitkeep b/tests/ag-ui-app/backend/data/.gitkeep new file mode 100644 index 00000000..7a423402 --- /dev/null +++ b/tests/ag-ui-app/backend/data/.gitkeep @@ -0,0 +1 @@ +# SQLite database will be created here diff --git a/tests/ag-ui-app/backend/package.json b/tests/ag-ui-app/backend/package.json new file mode 100644 index 00000000..a9b0c4e5 --- /dev/null +++ b/tests/ag-ui-app/backend/package.json @@ -0,0 +1,28 @@ +{ + "name": "smart-reply-backend", + "version": "1.0.0", + "type": "module", + "pnpm": { + "onlyBuiltDependencies": ["better-sqlite3"] + }, + "scripts": { + "dev": "tsx watch src/index.ts", + "build": "tsc", + "start": "node dist/index.js" + }, + "dependencies": { + "hono": "^4.6.0", + "@hono/node-server": "^1.13.0", + "better-sqlite3": "^11.6.0", + "@tanstack/ai": "^0.2.2", + "@tanstack/ai-openai": "^0.2.1", + "nanoid": "^5.0.9", + "dotenv": "^16.4.7" + }, + "devDependencies": { + "@types/better-sqlite3": "^7.6.12", + "@types/node": "^22.10.2", + "tsx": "^4.19.2", + "typescript": "^5.7.2" + } +} diff --git a/tests/ag-ui-app/backend/pnpm-lock.yaml b/tests/ag-ui-app/backend/pnpm-lock.yaml new file mode 100644 index 00000000..27dfae3d --- /dev/null +++ b/tests/ag-ui-app/backend/pnpm-lock.yaml @@ -0,0 +1,726 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@hono/node-server': + specifier: ^1.13.0 + version: 1.19.8(hono@4.11.3) + '@tanstack/ai': + specifier: ^0.2.2 + version: 0.2.2 + '@tanstack/ai-openai': + specifier: ^0.2.1 + version: 0.2.1(@tanstack/ai@0.2.2)(zod@4.3.5) + better-sqlite3: + specifier: ^11.6.0 + version: 11.10.0 + dotenv: + specifier: ^16.4.7 + version: 16.6.1 + hono: + specifier: ^4.6.0 + version: 4.11.3 + nanoid: + specifier: ^5.0.9 + version: 5.1.6 + devDependencies: + '@types/better-sqlite3': + specifier: ^7.6.12 + version: 7.6.13 + '@types/node': + specifier: ^22.10.2 + version: 22.19.5 + tsx: + specifier: ^4.19.2 + version: 4.21.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + +packages: + + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@hono/node-server@1.19.8': + resolution: {integrity: sha512-0/g2lIOPzX8f3vzW1ggQgvG5mjtFBDBHFAzI5SFAi2DzSqS9luJwqg9T6O/gKYLi+inS7eNxBeIFkkghIPvrMA==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + + '@tanstack/ai-openai@0.2.1': + resolution: {integrity: sha512-MCD/WgJ7qZr/oWyik/zlsXzuO++np+Ag5l5n/5ZmX4mXc3R8Ga1qDeCt+/7RSSmxDJbogeGGn/0j6JpQLrLmRA==} + peerDependencies: + '@tanstack/ai': ^0.2.1 + zod: ^4.0.0 + + '@tanstack/ai@0.2.2': + resolution: {integrity: sha512-qqnUSKYMuJnGhiL6t8BAu3Joc9QhQTJIxUIWgQlObDhdY+dCJMLyv+Z7Zw+WqzCCjDfvWmHgLNWDI8+f3KkOPw==} + engines: {node: '>=18'} + + '@tanstack/devtools-event-client@0.4.0': + resolution: {integrity: sha512-RPfGuk2bDZgcu9bAJodvO2lnZeHuz4/71HjZ0bGb/SPg8+lyTA+RLSKQvo7fSmPSi8/vcH3aKQ8EM9ywf1olaw==} + engines: {node: '>=18'} + + '@types/better-sqlite3@7.6.13': + resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} + + '@types/node@22.19.5': + resolution: {integrity: sha512-HfF8+mYcHPcPypui3w3mvzuIErlNOh2OAG+BCeBZCEwyiD5ls2SiCwEyT47OELtf7M3nHxBdu0FsmzdKxkN52Q==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-sqlite3@11.10.0: + resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + hono@4.11.3: + resolution: {integrity: sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w==} + engines: {node: '>=16.9.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + nanoid@5.1.6: + resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} + engines: {node: ^18 || >=20} + hasBin: true + + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + + node-abi@3.85.0: + resolution: {integrity: sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==} + engines: {node: '>=10'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + openai@6.16.0: + resolution: {integrity: sha512-fZ1uBqjFUjXzbGc35fFtYKEOxd20kd9fDpFeqWtsOZWiubY8CZ1NAlXHW3iathaFvqmNtCWMIsosCuyeI7Joxg==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + partial-json@0.1.7: + resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} + + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + tar-fs@2.1.4: + resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + zod@4.3.5: + resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} + +snapshots: + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@hono/node-server@1.19.8(hono@4.11.3)': + dependencies: + hono: 4.11.3 + + '@tanstack/ai-openai@0.2.1(@tanstack/ai@0.2.2)(zod@4.3.5)': + dependencies: + '@tanstack/ai': 0.2.2 + openai: 6.16.0(zod@4.3.5) + zod: 4.3.5 + transitivePeerDependencies: + - ws + + '@tanstack/ai@0.2.2': + dependencies: + '@tanstack/devtools-event-client': 0.4.0 + partial-json: 0.1.7 + + '@tanstack/devtools-event-client@0.4.0': {} + + '@types/better-sqlite3@7.6.13': + dependencies: + '@types/node': 22.19.5 + + '@types/node@22.19.5': + dependencies: + undici-types: 6.21.0 + + base64-js@1.5.1: {} + + better-sqlite3@11.10.0: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.3 + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + chownr@1.1.4: {} + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + deep-extend@0.6.0: {} + + detect-libc@2.1.2: {} + + dotenv@16.6.1: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + expand-template@2.0.3: {} + + file-uri-to-path@1.0.0: {} + + fs-constants@1.0.0: {} + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + github-from-package@0.0.0: {} + + hono@4.11.3: {} + + ieee754@1.2.1: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + mimic-response@3.1.0: {} + + minimist@1.2.8: {} + + mkdirp-classic@0.5.3: {} + + nanoid@5.1.6: {} + + napi-build-utils@2.0.0: {} + + node-abi@3.85.0: + dependencies: + semver: 7.7.3 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + openai@6.16.0(zod@4.3.5): + optionalDependencies: + zod: 4.3.5 + + partial-json@0.1.7: {} + + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.1.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.85.0 + pump: 3.0.3 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.4 + tunnel-agent: 0.6.0 + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + resolve-pkg-maps@1.0.0: {} + + safe-buffer@5.2.1: {} + + semver@7.7.3: {} + + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-json-comments@2.0.1: {} + + tar-fs@2.1.4: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.3 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tsx@4.21.0: + dependencies: + esbuild: 0.27.2 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + util-deprecate@1.0.2: {} + + wrappy@1.0.2: {} + + zod@4.3.5: {} diff --git a/tests/ag-ui-app/backend/src/db/index.ts b/tests/ag-ui-app/backend/src/db/index.ts new file mode 100644 index 00000000..3c5c8ba8 --- /dev/null +++ b/tests/ag-ui-app/backend/src/db/index.ts @@ -0,0 +1,102 @@ +import Database from 'better-sqlite3' +import { initDb, type Tone, type Conversation, type Reply, type ConversationWithReplies } from './schema.js' +import { nanoid } from 'nanoid' + +// Singleton database instance +let db: Database.Database | null = null + +export function getDb(): Database.Database { + if (!db) { + db = initDb() + } + return db +} + +// Database operations +export const dbOps = { + // Create a new conversation + createConversation(message: string, tone: Tone, context?: string, intent?: string): string { + const db = getDb() + const id = nanoid() + db.prepare('INSERT INTO conversations (id, original_message, context, intent, tone) VALUES (?, ?, ?, ?, ?)') + .run(id, message, context || null, intent || null, tone) + return id + }, + + // Save generated replies + saveReplies(conversationId: string, replies: string[]): void { + const db = getDb() + const stmt = db.prepare( + 'INSERT INTO replies (id, conversation_id, content, reply_index) VALUES (?, ?, ?, ?)' + ) + + const insertMany = db.transaction((items: string[]) => { + items.forEach((content, index) => { + stmt.run(nanoid(), conversationId, content, index) + }) + }) + + insertMany(replies) + }, + + // Get all conversations with their replies + getHistory(limit: number = 50): ConversationWithReplies[] { + const db = getDb() + + const conversations = db.prepare(` + SELECT c.*, + json_group_array( + json_object( + 'id', r.id, + 'content', r.content, + 'reply_index', r.reply_index, + 'created_at', r.created_at + ) + ) as replies_json + FROM conversations c + LEFT JOIN replies r ON c.id = r.conversation_id + GROUP BY c.id + ORDER BY c.created_at DESC + LIMIT ? + `).all(limit) as (Conversation & { replies_json: string })[] + + return conversations.map(conv => ({ + ...conv, + replies: JSON.parse(conv.replies_json) + .filter((r: Reply & { id: string | null }) => r.id !== null) + .sort((a: Reply, b: Reply) => a.reply_index - b.reply_index) + })) + }, + + // Get a single conversation with replies + getConversation(id: string): ConversationWithReplies | null { + const db = getDb() + + const conversation = db.prepare('SELECT * FROM conversations WHERE id = ?').get(id) as Conversation | undefined + if (!conversation) return null + + const replies = db.prepare( + 'SELECT * FROM replies WHERE conversation_id = ? ORDER BY reply_index' + ).all(id) as Reply[] + + return { ...conversation, replies } + }, + + // Delete a conversation (cascades to replies) + deleteConversation(id: string): boolean { + const db = getDb() + const result = db.prepare('DELETE FROM conversations WHERE id = ?').run(id) + return result.changes > 0 + }, + + // Log analytics event + logEvent(eventType: 'generate' | 'copy' | 'select' | 'delete', conversationId?: string, metadata?: object): void { + const db = getDb() + db.prepare( + 'INSERT INTO analytics (id, event_type, conversation_id, metadata) VALUES (?, ?, ?, ?)' + ).run(nanoid(), eventType, conversationId || null, metadata ? JSON.stringify(metadata) : null) + } +} + +// Re-export types +export type { Tone, Conversation, Reply, ConversationWithReplies } diff --git a/tests/ag-ui-app/backend/src/db/schema.ts b/tests/ag-ui-app/backend/src/db/schema.ts new file mode 100644 index 00000000..0df99fcd --- /dev/null +++ b/tests/ag-ui-app/backend/src/db/schema.ts @@ -0,0 +1,89 @@ +import Database from 'better-sqlite3' +import path from 'path' +import fs from 'fs' +import { fileURLToPath } from 'url' + +// Types +export type Tone = 'professional' | 'friendly' | 'apologetic' | 'assertive' | 'neutral' + +export interface Conversation { + id: string + original_message: string + context: string | null + intent: string | null + tone: Tone + created_at: string +} + +export interface Reply { + id: string + conversation_id: string + content: string + reply_index: number + created_at: string +} + +export interface ConversationWithReplies extends Conversation { + replies: Reply[] +} + +// Initialize database +export function initDb(): Database.Database { + // Use DB_PATH env var, or /app/data in production, or local ./data in development + const dbPath = process.env.DB_PATH || + (process.env.NODE_ENV === 'production' + ? '/app/data/smart-reply.db' + : path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'data', 'smart-reply.db')) + + // Ensure directory exists + const dbDir = path.dirname(dbPath) + if (!fs.existsSync(dbDir)) { + fs.mkdirSync(dbDir, { recursive: true }) + } + + const db = new Database(dbPath) + + // Enable WAL mode for better concurrent performance + db.pragma('journal_mode = WAL') + db.pragma('foreign_keys = ON') + + // Create tables + db.exec(` + CREATE TABLE IF NOT EXISTS conversations ( + id TEXT PRIMARY KEY, + original_message TEXT NOT NULL, + context TEXT, + intent TEXT, + tone TEXT NOT NULL CHECK (tone IN ('professional', 'friendly', 'apologetic', 'assertive', 'neutral')), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + + -- Add context and intent columns if they don't exist (for existing databases) + -- SQLite doesn't support IF NOT EXISTS for ALTER TABLE, so we handle this in code + + + CREATE TABLE IF NOT EXISTS replies ( + id TEXT PRIMARY KEY, + conversation_id TEXT NOT NULL, + content TEXT NOT NULL, + reply_index INTEGER NOT NULL CHECK (reply_index BETWEEN 0 AND 2), + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE + ); + + CREATE TABLE IF NOT EXISTS analytics ( + id TEXT PRIMARY KEY, + event_type TEXT NOT NULL CHECK (event_type IN ('generate', 'copy', 'select', 'delete')), + conversation_id TEXT, + metadata TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE SET NULL + ); + + CREATE INDEX IF NOT EXISTS idx_replies_conversation ON replies(conversation_id); + CREATE INDEX IF NOT EXISTS idx_conversations_created ON conversations(created_at DESC); + CREATE INDEX IF NOT EXISTS idx_analytics_event ON analytics(event_type, created_at); + `) + + return db +} diff --git a/tests/ag-ui-app/backend/src/index.ts b/tests/ag-ui-app/backend/src/index.ts new file mode 100644 index 00000000..c417d83f --- /dev/null +++ b/tests/ag-ui-app/backend/src/index.ts @@ -0,0 +1,103 @@ +import { serve } from '@hono/node-server' +import { Hono } from 'hono' +import { cors } from 'hono/cors' +import { logger } from 'hono/logger' +import { repliesRouter } from './routes/replies.js' +import { historyRouter } from './routes/history.js' +import { checkServicesHealth } from './lib/services.js' +import 'dotenv/config' + +// Create Hono app +const app = new Hono() + +// Middleware +app.use('*', logger()) +app.use('*', cors({ + origin: (origin) => { + // Allow requests with no origin (e.g., mobile apps, curl) + if (!origin) return origin + // Allow localhost for development + if (origin.startsWith('http://localhost:') || origin.startsWith('http://127.0.0.1:')) { + return origin + } + // Allow all syncable.dev subdomains (production) + if (origin.endsWith('.syncable.dev')) { + return origin + } + return null + }, + allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'], + allowHeaders: ['Content-Type', 'Authorization'], + exposeHeaders: ['Content-Length'], + maxAge: 86400, + credentials: true +})) + +// Health check endpoint +app.get('/health', (c) => { + return c.json({ + status: 'ok', + timestamp: new Date().toISOString(), + service: 'smart-reply-backend' + }) +}) + +// Services health check (includes microservices) +app.get('/health/services', async (c) => { + const services = await checkServicesHealth() + const allHealthy = services.sentiment && services.contacts && services.style + + return c.json({ + status: allHealthy ? 'ok' : 'degraded', + timestamp: new Date().toISOString(), + services: { + 'sentiment-analysis': services.sentiment ? 'up' : 'down', + 'contact-intelligence': services.contacts ? 'up' : 'down', + 'writing-style': services.style ? 'up' : 'down' + } + }) +}) + +// API routes +app.route('/api/replies', repliesRouter) +app.route('/api/history', historyRouter) + +// 404 handler +app.notFound((c) => { + return c.json({ error: 'Not found' }, 404) +}) + +// Error handler +app.onError((err, c) => { + console.error('Unhandled error:', err) + return c.json({ error: 'Internal server error' }, 500) +}) + +// Start server +const port = parseInt(process.env.PORT || '3001', 10) + +serve({ fetch: app.fetch, port }, (info) => { + console.log(` + ╔═══════════════════════════════════════════════════════╗ + ║ ║ + ║ Smart Reply Backend ║ + ║ Running at http://localhost:${info.port} ║ + ║ ║ + ║ Endpoints: ║ + ║ • GET /health - Health check ║ + ║ • GET /health/services - Services status ║ + ║ • POST /api/replies/generate - Generate replies ║ + ║ • POST /api/replies/save - Save replies ║ + ║ • POST /api/replies/learn - Learn from reply ║ + ║ • GET /api/history - List conversations ║ + ║ • GET /api/history/:id - Get conversation ║ + ║ • DELETE /api/history/:id - Delete conversation ║ + ║ ║ + ║ Microservices: ║ + ║ • Sentiment Analysis - localhost:3002 ║ + ║ • Contact Intelligence - localhost:3003 ║ + ║ • Writing Style - localhost:3004 ║ + ║ ║ + ╚═══════════════════════════════════════════════════════╝ + `) +}) diff --git a/tests/ag-ui-app/backend/src/lib/openai.ts b/tests/ag-ui-app/backend/src/lib/openai.ts new file mode 100644 index 00000000..5599ea45 --- /dev/null +++ b/tests/ag-ui-app/backend/src/lib/openai.ts @@ -0,0 +1,72 @@ +import { createOpenaiChat } from '@tanstack/ai-openai' +import type { Tone } from '../db/index.js' + +/** + * Create OpenAI adapter using TanStack AI + * Explicitly pass API key from environment + */ +export function getOpenAIAdapter() { + const apiKey = process.env.OPENAI_API_KEY + if (!apiKey) { + throw new Error('OPENAI_API_KEY environment variable is not set') + } + const adapter = createOpenaiChat('gpt-5.2', apiKey) + return adapter +} + +// Tone-specific system prompts for smart reply generation +export const TONE_SYSTEM_PROMPTS: Record = { + professional: `You craft professional, business-appropriate replies. Use formal language, be concise and respectful. +Avoid casual expressions, slang, or overly familiar language. Maintain a courteous yet efficient tone.`, + + friendly: `You craft warm, friendly replies. Use casual but appropriate language, show genuine interest and enthusiasm. +Be personable and approachable while remaining respectful. Include warm greetings when appropriate.`, + + apologetic: `You craft sincere, apologetic replies. Express genuine remorse and understanding of the issue. +Take responsibility where appropriate, offer solutions or next steps, and show empathy for any inconvenience caused.`, + + assertive: `You craft confident, assertive replies. Be direct, clear, and firm while remaining professional. +State your position clearly, set boundaries respectfully, and avoid being passive or aggressive.`, + + neutral: `You craft balanced, neutral replies. Be objective and straightforward without emotional language. +Present information clearly, avoid taking strong positions, and maintain a calm, measured tone.` +} + +/** + * Build the complete system prompt for generating smart replies + */ +export function buildSmartReplyPrompt(tone: Tone): string { + return `You are a reply assistant that helps users craft the perfect response to messages they've received. + +TONE GUIDELINES: +${TONE_SYSTEM_PROMPTS[tone]} + +YOUR TASK: +You will receive: +1. CONVERSATION CONTEXT (optional): Background information or previous messages in the conversation thread +2. MESSAGE RECEIVED: The specific message the user needs to reply to +3. WHAT I WANT TO COMMUNICATE (optional): The user's intended message or key points they want to convey + +Using ALL provided information, generate exactly 3 different reply options that: +- Match the ${tone} tone +- Address the received message directly +- Incorporate the user's intended points if provided +- Consider the conversation context for appropriate follow-up +- Are complete, polished, and ready to send + +OUTPUT FORMAT: +Return ONLY a valid JSON array containing exactly 3 strings. Each string is a complete reply. + +Example format: +["Short reply option here.", "Medium length reply with more detail here.", "Detailed reply with full context and explanation here."] + +GUIDELINES: +- Option 1: Short and concise (1-2 sentences) +- Option 2: Medium length with appropriate detail (2-3 sentences) +- Option 3: Detailed and comprehensive (3-4 sentences) +- Make each reply natural and conversational +- If context is provided, reference it appropriately +- If the user's intent is provided, make sure all replies convey that intent +- Do NOT include any explanation, markdown, or text outside the JSON array +- Output ONLY the JSON array, nothing else` +} diff --git a/tests/ag-ui-app/backend/src/lib/services.ts b/tests/ag-ui-app/backend/src/lib/services.ts new file mode 100644 index 00000000..1c893f19 --- /dev/null +++ b/tests/ag-ui-app/backend/src/lib/services.ts @@ -0,0 +1,245 @@ +/** + * Service Client Helpers + * Query the microservices for enhanced reply generation + */ + +const SENTIMENT_URL = process.env.SENTIMENT_SERVICE_URL || 'http://localhost:3002' +const CONTACTS_URL = process.env.CONTACTS_SERVICE_URL || 'http://localhost:3003' +const STYLE_URL = process.env.STYLE_SERVICE_URL || 'http://localhost:3004' + +// Timeout for service calls (don't block reply generation for too long) +const SERVICE_TIMEOUT = 3000 + +export interface SentimentResult { + sentiment: 'positive' | 'negative' | 'neutral' | 'mixed' + confidence: number + emotions: Array<{ emotion: string; score: number }> + urgency: 'low' | 'medium' | 'high' | 'critical' + keyPoints: string[] + suggestedApproach: string +} + +export interface ContactMatchResult { + matchedContact: { + id: string + name: string + relationship: string + company: string | null + formality: string + use_emojis: boolean + preferred_tone: string | null + } | null + confidence: number + relationshipContext: string +} + +export interface StyleProfile { + totalSamples: number + averageSentenceLength: number + commonGreetings: string[] + commonSignoffs: string[] + vocabularyLevel: 'professional' | 'casual' | 'mixed' + usesEmojis: boolean + commonPhrases: string[] + punctuationStyle: { + exclamationFrequency: number + questionFrequency: number + } +} + +/** + * Fetch with timeout + */ +async function fetchWithTimeout(url: string, options: RequestInit = {}): Promise { + const controller = new AbortController() + const timeout = setTimeout(() => controller.abort(), SERVICE_TIMEOUT) + + try { + const response = await fetch(url, { + ...options, + signal: controller.signal + }) + return response + } finally { + clearTimeout(timeout) + } +} + +/** + * Analyze sentiment of a message + */ +export async function analyzeSentiment(message: string): Promise { + try { + const response = await fetchWithTimeout(`${SENTIMENT_URL}/api/analyze`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ message }) + }) + + if (!response.ok) { + console.warn('[Services] Sentiment analysis failed:', response.status) + return null + } + + return await response.json() as SentimentResult + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') { + console.warn('[Services] Sentiment analysis timed out') + } else { + console.warn('[Services] Sentiment analysis error:', error) + } + return null + } +} + +/** + * Match message to a known contact + */ +export async function matchContact(message: string): Promise { + try { + const response = await fetchWithTimeout(`${CONTACTS_URL}/api/contacts/match`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ message }) + }) + + if (!response.ok) { + console.warn('[Services] Contact matching failed:', response.status) + return null + } + + return await response.json() as ContactMatchResult + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') { + console.warn('[Services] Contact matching timed out') + } else { + console.warn('[Services] Contact matching error:', error) + } + return null + } +} + +/** + * Get the user's writing style profile + */ +export async function getStyleProfile(): Promise { + try { + const response = await fetchWithTimeout(`${STYLE_URL}/api/profile`) + + if (!response.ok) { + console.warn('[Services] Style profile fetch failed:', response.status) + return null + } + + return await response.json() as StyleProfile + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') { + console.warn('[Services] Style profile timed out') + } else { + console.warn('[Services] Style profile error:', error) + } + return null + } +} + +/** + * Learn from a selected/edited reply + */ +export async function learnFromReply(content: string, type: 'selected_reply' | 'custom_edit' | 'sent_message'): Promise { + try { + const response = await fetchWithTimeout(`${STYLE_URL}/api/learn`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ content, type }) + }) + + return response.ok + } catch (error) { + console.warn('[Services] Learn from reply error:', error) + return false + } +} + +/** + * Check health of all services + */ +export async function checkServicesHealth(): Promise<{ + sentiment: boolean + contacts: boolean + style: boolean +}> { + const results = await Promise.allSettled([ + fetchWithTimeout(`${SENTIMENT_URL}/health`), + fetchWithTimeout(`${CONTACTS_URL}/health`), + fetchWithTimeout(`${STYLE_URL}/health`) + ]) + + return { + sentiment: results[0].status === 'fulfilled' && results[0].value.ok, + contacts: results[1].status === 'fulfilled' && results[1].value.ok, + style: results[2].status === 'fulfilled' && results[2].value.ok + } +} + +/** + * Build enhanced context from all services + */ +export async function buildEnhancedContext(message: string): Promise { + // Query all services in parallel + const [sentiment, contact, style] = await Promise.all([ + analyzeSentiment(message), + matchContact(message), + getStyleProfile() + ]) + + const contextParts: string[] = [] + + // Add sentiment context + if (sentiment) { + const emotionList = sentiment.emotions + .slice(0, 3) + .map(e => `${e.emotion} (${Math.round(e.score * 100)}%)`) + .join(', ') + + contextParts.push(`SENTIMENT ANALYSIS: +- Overall sentiment: ${sentiment.sentiment} (confidence: ${Math.round(sentiment.confidence * 100)}%) +- Detected emotions: ${emotionList || 'none detected'} +- Urgency level: ${sentiment.urgency} +${sentiment.keyPoints.length > 0 ? `- Key points: ${sentiment.keyPoints.join(', ')}` : ''} +- Suggested approach: ${sentiment.suggestedApproach}`) + } + + // Add contact context + if (contact && contact.matchedContact) { + contextParts.push(`RELATIONSHIP CONTEXT: +${contact.relationshipContext}`) + } + + // Add style context + if (style && style.totalSamples >= 3) { + const styleHints: string[] = [] + + if (style.commonGreetings.length > 0) { + styleHints.push(`Preferred greetings: ${style.commonGreetings.slice(0, 3).join(', ')}`) + } + if (style.commonSignoffs.length > 0) { + styleHints.push(`Preferred sign-offs: ${style.commonSignoffs.slice(0, 3).join(', ')}`) + } + if (style.vocabularyLevel !== 'mixed') { + styleHints.push(`Writing style: ${style.vocabularyLevel}`) + } + if (style.usesEmojis) { + styleHints.push('User likes to use emojis') + } + if (style.commonPhrases.length > 0) { + styleHints.push(`Common phrases: "${style.commonPhrases.slice(0, 2).join('", "')}"`) + } + + if (styleHints.length > 0) { + contextParts.push(`USER'S WRITING STYLE: +${styleHints.join('\n')}`) + } + } + + return contextParts.join('\n\n') +} diff --git a/tests/ag-ui-app/backend/src/routes/history.ts b/tests/ag-ui-app/backend/src/routes/history.ts new file mode 100644 index 00000000..1e572a81 --- /dev/null +++ b/tests/ag-ui-app/backend/src/routes/history.ts @@ -0,0 +1,69 @@ +import { Hono } from 'hono' +import { dbOps } from '../db/index.js' + +const historyRouter = new Hono() + +// GET /api/history - Get all conversations with replies +historyRouter.get('/', (c) => { + try { + const limitParam = c.req.query('limit') + const limit = limitParam ? Math.min(Math.max(parseInt(limitParam, 10), 1), 100) : 50 + + const conversations = dbOps.getHistory(limit) + + return c.json({ + success: true, + data: conversations, + count: conversations.length + }) + } catch (error) { + console.error('Get history error:', error) + return c.json({ error: 'Failed to fetch history' }, 500) + } +}) + +// GET /api/history/:id - Get single conversation with replies +historyRouter.get('/:id', (c) => { + try { + const { id } = c.req.param() + + const conversation = dbOps.getConversation(id) + + if (!conversation) { + return c.json({ error: 'Conversation not found' }, 404) + } + + return c.json({ + success: true, + data: conversation + }) + } catch (error) { + console.error('Get conversation error:', error) + return c.json({ error: 'Failed to fetch conversation' }, 500) + } +}) + +// DELETE /api/history/:id - Delete a conversation +historyRouter.delete('/:id', (c) => { + try { + const { id } = c.req.param() + + const deleted = dbOps.deleteConversation(id) + + if (!deleted) { + return c.json({ error: 'Conversation not found' }, 404) + } + + dbOps.logEvent('delete', id) + + return c.json({ + success: true, + message: 'Conversation deleted successfully' + }) + } catch (error) { + console.error('Delete conversation error:', error) + return c.json({ error: 'Failed to delete conversation' }, 500) + } +}) + +export { historyRouter } diff --git a/tests/ag-ui-app/backend/src/routes/replies.ts b/tests/ag-ui-app/backend/src/routes/replies.ts new file mode 100644 index 00000000..63714f4a --- /dev/null +++ b/tests/ag-ui-app/backend/src/routes/replies.ts @@ -0,0 +1,185 @@ +import { Hono } from 'hono' +import { chat, toServerSentEventsResponse } from '@tanstack/ai' +import { dbOps, type Tone } from '../db/index.js' +import { getOpenAIAdapter, buildSmartReplyPrompt } from '../lib/openai.js' +import { buildEnhancedContext, learnFromReply } from '../lib/services.js' + +const repliesRouter = new Hono() + +// Validate tone +function isValidTone(tone: string): tone is Tone { + return ['professional', 'friendly', 'apologetic', 'assertive', 'neutral'].includes(tone) +} + +interface GenerateRequest { + message: string + tone: string + context?: string + intent?: string +} + +/** + * POST /api/replies/generate + * Generate smart replies using TanStack AI with streaming + */ +repliesRouter.post('/generate', async (c) => { + try { + const body = await c.req.json() + const { message, tone, context, intent } = body + + // Validate input + if (!message?.trim()) { + return c.json({ error: 'Message is required' }, 400) + } + + if (!tone || !isValidTone(tone)) { + return c.json({ + error: 'Invalid tone. Must be one of: professional, friendly, apologetic, assertive, neutral' + }, 400) + } + + if (message.length > 5000) { + return c.json({ error: 'Message too long. Maximum 5000 characters.' }, 400) + } + + // Create conversation record with context and intent + const conversationId = dbOps.createConversation( + message.trim(), + tone, + context?.trim() || undefined, + intent?.trim() || undefined + ) + dbOps.logEvent('generate', conversationId, { + tone, + messageLength: message.length, + hasContext: !!context, + hasIntent: !!intent + }) + + // Get OpenAI adapter from TanStack AI + const adapter = getOpenAIAdapter() + + // Build system prompt + const systemPrompt = buildSmartReplyPrompt(tone) + + // Query microservices for enhanced context (non-blocking, with timeout) + const enhancedContext = await buildEnhancedContext(message.trim()) + + // Build user message with context, intent, and service insights + const userMessage = buildUserMessage(message.trim(), context?.trim(), intent?.trim(), enhancedContext) + + // Create streaming chat with TanStack AI + const stream = chat({ + adapter, + systemPrompts: [systemPrompt], + messages: [ + { + role: 'user', + content: userMessage + } + ], + temperature: 0.8, + maxTokens: 2000, + }) + + // Convert to Server-Sent Events response using TanStack AI utility + const response = toServerSentEventsResponse(stream) + + // Add custom header with conversation ID + const headers = new Headers(response.headers) + headers.set('X-Conversation-Id', conversationId) + headers.set('Access-Control-Expose-Headers', 'X-Conversation-Id') + + return new Response(response.body, { + status: response.status, + headers + }) + + } catch (error) { + console.error('Generate endpoint error:', error) + return c.json({ error: 'Internal server error' }, 500) + } +}) + +/** + * Build the user message with context, intent, and service insights + */ +function buildUserMessage(message: string, context?: string, intent?: string, enhancedContext?: string): string { + let parts: string[] = [] + + // Add AI-analyzed context from microservices + if (enhancedContext) { + parts.push(`AI-ANALYZED INSIGHTS:\n${enhancedContext}`) + } + + if (context) { + parts.push(`CONVERSATION CONTEXT (from user):\n${context}`) + } + + parts.push(`MESSAGE RECEIVED:\n"${message}"`) + + if (intent) { + parts.push(`WHAT I WANT TO COMMUNICATE:\n${intent}`) + } + + parts.push('Generate 3 reply options based on the above information. Consider the sentiment, relationship context, and user\'s writing style if provided:') + + return parts.join('\n\n') +} + +/** + * POST /api/replies/save + * Save generated replies to database (called after streaming completes) + */ +repliesRouter.post('/save', async (c) => { + try { + const { conversationId, replies } = await c.req.json<{ + conversationId: string + replies: string[] + }>() + + if (!conversationId || !replies || !Array.isArray(replies)) { + return c.json({ error: 'Invalid request body' }, 400) + } + + // Save replies to database + dbOps.saveReplies(conversationId, replies.slice(0, 3)) + + return c.json({ success: true }) + } catch (error) { + console.error('Save replies error:', error) + return c.json({ error: 'Failed to save replies' }, 500) + } +}) + +/** + * POST /api/replies/learn + * Learn from a selected/copied reply to improve style matching + */ +repliesRouter.post('/learn', async (c) => { + try { + const { content, type } = await c.req.json<{ + content: string + type: 'selected_reply' | 'custom_edit' | 'sent_message' + }>() + + if (!content?.trim()) { + return c.json({ error: 'Content is required' }, 400) + } + + const validTypes = ['selected_reply', 'custom_edit', 'sent_message'] + if (!type || !validTypes.includes(type)) { + return c.json({ error: 'Invalid type' }, 400) + } + + // Send to writing style service (non-blocking) + const success = await learnFromReply(content.trim(), type) + + return c.json({ success, learned: success }) + } catch (error) { + console.error('Learn endpoint error:', error) + return c.json({ error: 'Failed to learn from reply' }, 500) + } +}) + +export { repliesRouter } diff --git a/tests/ag-ui-app/backend/tsconfig.json b/tests/ag-ui-app/backend/tsconfig.json new file mode 100644 index 00000000..ef16106b --- /dev/null +++ b/tests/ag-ui-app/backend/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "resolveJsonModule": true, + "noEmit": false + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/tests/ag-ui-app/frontend/.cta.json b/tests/ag-ui-app/frontend/.cta.json new file mode 100644 index 00000000..f9ef2cda --- /dev/null +++ b/tests/ag-ui-app/frontend/.cta.json @@ -0,0 +1,15 @@ +{ + "projectName": "frontend", + "mode": "file-router", + "typescript": true, + "tailwind": true, + "packageManager": "pnpm", + "git": true, + "addOnOptions": {}, + "version": 1, + "framework": "react-cra", + "chosenAddOns": [ + "start", + "nitro" + ] +} \ No newline at end of file diff --git a/tests/ag-ui-app/frontend/.dockerignore b/tests/ag-ui-app/frontend/.dockerignore new file mode 100644 index 00000000..a682ded1 --- /dev/null +++ b/tests/ag-ui-app/frontend/.dockerignore @@ -0,0 +1,47 @@ +# Dependencies +node_modules + +# Build output +.output +dist +.nitro +.vinxi + +# TanStack generated files +.tanstack + +# Development files +.git +.gitignore +.vscode +.idea +*.md + +# Environment files (sensitive) +.env +.env.* +!.env.example + +# Test files +*.test.ts +*.test.tsx +*.spec.ts +*.spec.tsx +__tests__ +coverage + +# Docker files +Dockerfile* +docker-compose* +.dockerignore + +# Logs +logs +*.log + +# OS files +.DS_Store +Thumbs.db + +# Cache +.cache diff --git a/tests/ag-ui-app/frontend/.gitignore b/tests/ag-ui-app/frontend/.gitignore new file mode 100644 index 00000000..6221ecbd --- /dev/null +++ b/tests/ag-ui-app/frontend/.gitignore @@ -0,0 +1,13 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +count.txt +.env +.nitro +.tanstack +.wrangler +.output +.vinxi +todos.json diff --git a/tests/ag-ui-app/frontend/.vscode/settings.json b/tests/ag-ui-app/frontend/.vscode/settings.json new file mode 100644 index 00000000..00b5278e --- /dev/null +++ b/tests/ag-ui-app/frontend/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.watcherExclude": { + "**/routeTree.gen.ts": true + }, + "search.exclude": { + "**/routeTree.gen.ts": true + }, + "files.readonlyInclude": { + "**/routeTree.gen.ts": true + } +} diff --git a/tests/ag-ui-app/frontend/Dockerfile b/tests/ag-ui-app/frontend/Dockerfile new file mode 100644 index 00000000..43bf8973 --- /dev/null +++ b/tests/ag-ui-app/frontend/Dockerfile @@ -0,0 +1,59 @@ +# ============================================================================= +# Frontend Dockerfile - TanStack Start + Nitro (Node.js preset) +# Multi-stage build for production optimization +# ============================================================================= + +# ----------------------------------------------------------------------------- +# Stage 1: Builder - Install dependencies and build +# ----------------------------------------------------------------------------- +FROM node:22-alpine AS builder + +WORKDIR /app + +# Copy package files first for better layer caching +COPY package.json pnpm-lock.yaml* package-lock.json* yarn.lock* ./ + +# Install pnpm and dependencies +RUN corepack enable pnpm && \ + pnpm install --frozen-lockfile + +# Copy source files +COPY . . + +# Build the application +# TanStack Start with Nitro outputs to .output directory +RUN pnpm run build + +# ----------------------------------------------------------------------------- +# Stage 2: Runner - Minimal production image +# ----------------------------------------------------------------------------- +FROM node:22-alpine AS runner + +WORKDIR /app + +# Create non-root user for security +RUN addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 tanstack + +# Copy built output from Nitro +# Nitro generates a self-contained server in .output +COPY --from=builder --chown=tanstack:nodejs /app/.output ./.output + +# Switch to non-root user +USER tanstack + +# Expose port +EXPOSE 3000 + +# Environment variables +ENV NODE_ENV=production +ENV HOST=0.0.0.0 +ENV PORT=3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1 + +# Start the Nitro server +# Nitro outputs the server entry point at .output/server/index.mjs +CMD ["node", ".output/server/index.mjs"] diff --git a/tests/ag-ui-app/frontend/README.md b/tests/ag-ui-app/frontend/README.md new file mode 100644 index 00000000..0124b957 --- /dev/null +++ b/tests/ag-ui-app/frontend/README.md @@ -0,0 +1,290 @@ +Welcome to your new TanStack app! + +# Getting Started + +To run this application: + +```bash +pnpm install +pnpm dev +``` + +# Building For Production + +To build this application for production: + +```bash +pnpm build +``` + +## Testing + +This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with: + +```bash +pnpm test +``` + +## Styling + +This project uses [Tailwind CSS](https://tailwindcss.com/) for styling. + + + + +## Routing +This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`. + +### Adding A Route + +To add a new route to your application just add another a new file in the `./src/routes` directory. + +TanStack will automatically generate the content of the route file for you. + +Now that you have two routes you can use a `Link` component to navigate between them. + +### Adding Links + +To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`. + +```tsx +import { Link } from "@tanstack/react-router"; +``` + +Then anywhere in your JSX you can use it like so: + +```tsx +About +``` + +This will create a link that will navigate to the `/about` route. + +More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent). + +### Using A Layout + +In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `` component. + +Here is an example layout that includes a header: + +```tsx +import { Outlet, createRootRoute } from '@tanstack/react-router' +import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' + +import { Link } from "@tanstack/react-router"; + +export const Route = createRootRoute({ + component: () => ( + <> +
+ +
+ + + + ), +}) +``` + +The `` component is not required so you can remove it if you don't want it in your layout. + +More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts). + + +## Data Fetching + +There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered. + +For example: + +```tsx +const peopleRoute = createRoute({ + getParentRoute: () => rootRoute, + path: "/people", + loader: async () => { + const response = await fetch("https://swapi.dev/api/people"); + return response.json() as Promise<{ + results: { + name: string; + }[]; + }>; + }, + component: () => { + const data = peopleRoute.useLoaderData(); + return ( +
    + {data.results.map((person) => ( +
  • {person.name}
  • + ))} +
+ ); + }, +}); +``` + +Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters). + +### React-Query + +React-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze. + +First add your dependencies: + +```bash +pnpm add @tanstack/react-query @tanstack/react-query-devtools +``` + +Next we'll need to create a query client and provider. We recommend putting those in `main.tsx`. + +```tsx +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; + +// ... + +const queryClient = new QueryClient(); + +// ... + +if (!rootElement.innerHTML) { + const root = ReactDOM.createRoot(rootElement); + + root.render( + + + + ); +} +``` + +You can also add TanStack Query Devtools to the root route (optional). + +```tsx +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; + +const rootRoute = createRootRoute({ + component: () => ( + <> + + + + + ), +}); +``` + +Now you can use `useQuery` to fetch your data. + +```tsx +import { useQuery } from "@tanstack/react-query"; + +import "./App.css"; + +function App() { + const { data } = useQuery({ + queryKey: ["people"], + queryFn: () => + fetch("https://swapi.dev/api/people") + .then((res) => res.json()) + .then((data) => data.results as { name: string }[]), + initialData: [], + }); + + return ( +
+
    + {data.map((person) => ( +
  • {person.name}
  • + ))} +
+
+ ); +} + +export default App; +``` + +You can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview). + +## State Management + +Another common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project. + +First you need to add TanStack Store as a dependency: + +```bash +pnpm add @tanstack/store +``` + +Now let's create a simple counter in the `src/App.tsx` file as a demonstration. + +```tsx +import { useStore } from "@tanstack/react-store"; +import { Store } from "@tanstack/store"; +import "./App.css"; + +const countStore = new Store(0); + +function App() { + const count = useStore(countStore); + return ( +
+ +
+ ); +} + +export default App; +``` + +One of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates. + +Let's check this out by doubling the count using derived state. + +```tsx +import { useStore } from "@tanstack/react-store"; +import { Store, Derived } from "@tanstack/store"; +import "./App.css"; + +const countStore = new Store(0); + +const doubledStore = new Derived({ + fn: () => countStore.state * 2, + deps: [countStore], +}); +doubledStore.mount(); + +function App() { + const count = useStore(countStore); + const doubledCount = useStore(doubledStore); + + return ( +
+ +
Doubled - {doubledCount}
+
+ ); +} + +export default App; +``` + +We use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating. + +Once we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook. + +You can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest). + +# Demo files + +Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed. + +# Learn More + +You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com). diff --git a/tests/ag-ui-app/frontend/package.json b/tests/ag-ui-app/frontend/package.json new file mode 100644 index 00000000..75f4b480 --- /dev/null +++ b/tests/ag-ui-app/frontend/package.json @@ -0,0 +1,45 @@ +{ + "name": "frontend", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev --port 3000", + "build": "vite build", + "preview": "vite preview", + "test": "vitest run" + }, + "dependencies": { + "@copilotkit/react-core": "1.51.3-next.3", + "@copilotkit/react-ui": "1.51.3-next.3", + "@tailwindcss/vite": "^4.0.6", + "@tanstack/ai": "^0.2.2", + "@tanstack/ai-openai": "^0.2.1", + "@tanstack/ai-react": "^0.2.2", + "@tanstack/react-devtools": "^0.7.0", + "@tanstack/react-router": "^1.132.0", + "@tanstack/react-router-devtools": "^1.132.0", + "@tanstack/react-router-ssr-query": "^1.131.7", + "@tanstack/react-start": "^1.132.0", + "@tanstack/router-plugin": "^1.132.0", + "lucide-react": "^0.561.0", + "nitro": "latest", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "tailwindcss": "^4.0.6", + "vite-tsconfig-paths": "^6.0.2" + }, + "devDependencies": { + "@tanstack/devtools-vite": "^0.3.11", + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.2.0", + "@types/node": "^22.10.2", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "@vitejs/plugin-react": "^5.0.4", + "jsdom": "^27.0.0", + "typescript": "^5.7.2", + "vite": "^7.1.7", + "vitest": "^3.0.5", + "web-vitals": "^5.1.0" + } +} \ No newline at end of file diff --git a/tests/ag-ui-app/frontend/pnpm-lock.yaml b/tests/ag-ui-app/frontend/pnpm-lock.yaml new file mode 100644 index 00000000..c355f049 --- /dev/null +++ b/tests/ag-ui-app/frontend/pnpm-lock.yaml @@ -0,0 +1,10158 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@copilotkit/react-core': + specifier: 1.51.3-next.3 + version: 1.51.3-next.3(@ag-ui/core@0.0.43)(@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5))(@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(graphql@16.12.0)(micromark-util-types@2.0.2)(micromark@4.0.2)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76) + '@copilotkit/react-ui': + specifier: 1.51.3-next.3 + version: 1.51.3-next.3(@ag-ui/core@0.0.43)(@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5))(@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(graphql@16.12.0)(micromark-util-types@2.0.2)(micromark@4.0.2)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76) + '@tailwindcss/vite': + specifier: ^4.0.6 + version: 4.1.18(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@tanstack/ai': + specifier: ^0.2.2 + version: 0.2.2 + '@tanstack/ai-openai': + specifier: ^0.2.1 + version: 0.2.1(@tanstack/ai@0.2.2)(ws@8.19.0)(zod@3.25.76) + '@tanstack/ai-react': + specifier: ^0.2.2 + version: 0.2.2(@tanstack/ai@0.2.2)(@types/react@19.2.8)(react@19.2.3) + '@tanstack/react-devtools': + specifier: ^0.7.0 + version: 0.7.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.10) + '@tanstack/react-router': + specifier: ^1.132.0 + version: 1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-router-devtools': + specifier: ^1.132.0 + version: 1.147.1(@tanstack/react-router@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.147.1)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.10) + '@tanstack/react-router-ssr-query': + specifier: ^1.131.7 + version: 1.147.1(@tanstack/query-core@5.90.16)(@tanstack/react-query@5.90.16(react@19.2.3))(@tanstack/react-router@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.147.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-start': + specifier: ^1.132.0 + version: 1.147.1(crossws@0.4.1(srvx@0.9.8))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@tanstack/router-plugin': + specifier: ^1.132.0 + version: 1.147.1(@tanstack/react-router@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + lucide-react: + specifier: ^0.561.0 + version: 0.561.0(react@19.2.3) + nitro: + specifier: latest + version: 3.0.1-alpha.1(lru-cache@11.2.4)(rollup@4.55.1)(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + react: + specifier: ^19.2.0 + version: 19.2.3 + react-dom: + specifier: ^19.2.0 + version: 19.2.3(react@19.2.3) + tailwindcss: + specifier: ^4.0.6 + version: 4.1.18 + vite-tsconfig-paths: + specifier: ^6.0.2 + version: 6.0.4(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + devDependencies: + '@tanstack/devtools-vite': + specifier: ^0.3.11 + version: 0.3.12(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@testing-library/dom': + specifier: ^10.4.0 + version: 10.4.1 + '@testing-library/react': + specifier: ^16.2.0 + version: 16.3.1(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@types/node': + specifier: ^22.10.2 + version: 22.19.5 + '@types/react': + specifier: ^19.2.0 + version: 19.2.8 + '@types/react-dom': + specifier: ^19.2.0 + version: 19.2.3(@types/react@19.2.8) + '@vitejs/plugin-react': + specifier: ^5.0.4 + version: 5.1.2(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + jsdom: + specifier: ^27.0.0 + version: 27.4.0 + typescript: + specifier: ^5.7.2 + version: 5.9.3 + vite: + specifier: ^7.1.7 + version: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vitest: + specifier: ^3.0.5 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.5)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(tsx@4.21.0) + web-vitals: + specifier: ^5.1.0 + version: 5.1.0 + +packages: + + '@0no-co/graphql.web@1.2.0': + resolution: {integrity: sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + peerDependenciesMeta: + graphql: + optional: true + + '@acemir/cssom@0.9.30': + resolution: {integrity: sha512-9CnlMCI0LmCIq0olalQqdWrJHPzm0/tw3gzOA9zJSgvFX7Xau3D24mAGa4BtwxwY69nsuJW6kQqqCzf/mEcQgg==} + + '@ag-ui/client@0.0.42': + resolution: {integrity: sha512-zAbP+sZJImR5bUpR2ni7RtuuNZMuesaxviynyIgzKlr1k2VCM49mFpbDUKU4TH4Cneu+Xe7OEnO8qCOCIzBAww==} + + '@ag-ui/client@0.0.43': + resolution: {integrity: sha512-150Y3TX+k83qvtmp4bTxmPW0Xu7to2y5vlhD8s5gof5/fxMdxybM1ieZazN7OIWU0nCK/hvhA+bVC7mmyby3gw==} + + '@ag-ui/core@0.0.42': + resolution: {integrity: sha512-C2hMg4Gs5oiUDgK9cA2RsTwSSmFZdIsqPklDrFw/Ue+quH6EU3vKp5YoOq7nuaQYO4pO8Em+Z+l5/M5PpcvP1g==} + + '@ag-ui/core@0.0.43': + resolution: {integrity: sha512-/T7kKwPAhtriFFiv3YxZ0yAzMHoY8a8I5UKzhPzwW934h92c6RJ54N93yb9PAqdX3XjCPId0C5gIBHb6QbeR3Q==} + + '@ag-ui/encoder@0.0.42': + resolution: {integrity: sha512-97B5MMCSs82t/y41uk2NrLBYFhbvn4kYsKQHMCfy8tjSWubyxh3zP7N9yHo8zJeSPe3WvzTvclyXNiGxSOsorg==} + + '@ag-ui/encoder@0.0.43': + resolution: {integrity: sha512-0b7VXFW5KMctdWsGFQEkbIU2gY/tUv0eWBbMchIvjiIS1aHxdGTB6NXHXJzGvDcXEb3urkIvVIFGvpk0WOUhVw==} + + '@ag-ui/langgraph@0.0.22': + resolution: {integrity: sha512-6O9N2QHU68Jotsr/VSv99HQukqqXd74d4PI6Rm0Ks6T60p0IK6wp2EHoH/Hy5qvh7aIoOKhGjpCVm470ElB44A==} + peerDependencies: + '@ag-ui/client': '>=0.0.42' + '@ag-ui/core': '>=0.0.42' + + '@ag-ui/proto@0.0.42': + resolution: {integrity: sha512-NDUwSgMnGEqxZGkWIJ1ge5t3Q7Kiddj360x2JAWaIfv9w+7tDJ0pmgyzf3/SXp605aY2wZiDLBtJ6jKZeg1lFg==} + + '@ag-ui/proto@0.0.43': + resolution: {integrity: sha512-JcGjwMyxd5KiRTVi3158eByPXm9lKDRMIApYgSlzhXpBwyfpub8waGFnEWqjE3U+gA1LaZHiVMIfD5NCVYj4WQ==} + + '@ai-sdk/anthropic@2.0.57': + resolution: {integrity: sha512-DREpYqW2pylgaj69gZ+K8u92bo9DaMgFdictYnY+IwYeY3bawQ4zI7l/o1VkDsBDljAx8iYz5lPURwVZNu+Xpg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/gateway@2.0.29': + resolution: {integrity: sha512-1b7E9F/B5gex/1uCkhs+sGIbH0KsZOItHnNz3iY5ir+nc4ZUA6WOU5Cu2w1USlc+3UVbhf+H+iNLlxVjLe4VvQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/google@2.0.52': + resolution: {integrity: sha512-2XUnGi3f7TV4ujoAhA+Fg3idUoG/+Y2xjCRg70a1/m0DH1KSQqYaCboJ1C19y6ZHGdf5KNT20eJdswP6TvrY2g==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/mcp@0.0.8': + resolution: {integrity: sha512-9y9GuGcZ9/+pMIHfpOCJgZVp+AZMv6TkjX2NVT17SQZvTF2N8LXuCXyoUPyi1PxIxzxl0n463LxxaB2O6olC+Q==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/openai@2.0.89': + resolution: {integrity: sha512-4+qWkBCbL9HPKbgrUO/F2uXZ8GqrYxHa8SWEYIzxEJ9zvWw3ISr3t1/27O1i8MGSym+PzEyHBT48EV4LAwWaEw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider-utils@3.0.17': + resolution: {integrity: sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider-utils@3.0.20': + resolution: {integrity: sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider@2.0.0': + resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} + engines: {node: '>=18'} + + '@ai-sdk/provider@2.0.1': + resolution: {integrity: sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng==} + engines: {node: '>=18'} + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@asamuzakjp/css-color@4.1.1': + resolution: {integrity: sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==} + + '@asamuzakjp/dom-selector@6.7.6': + resolution: {integrity: sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@braintree/sanitize-url@7.1.1': + resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} + + '@bufbuild/protobuf@2.11.0': + resolution: {integrity: sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==} + + '@cfworker/json-schema@4.1.1': + resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} + + '@chevrotain/cst-dts-gen@11.0.3': + resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} + + '@chevrotain/gast@11.0.3': + resolution: {integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==} + + '@chevrotain/regexp-to-ast@11.0.3': + resolution: {integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==} + + '@chevrotain/types@11.0.3': + resolution: {integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==} + + '@chevrotain/utils@11.0.3': + resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} + + '@copilotkit/react-core@1.51.3-next.3': + resolution: {integrity: sha512-JxD35pK46rBwrUe7++mPRxtZNBTpnoNZ7GZibHCd8m4bLCbRqM9c4yc5gdi8msbYWtXqnu6tK6Fl8m9DBFN6Hg==} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + zod: '>=3.0.0' + + '@copilotkit/react-ui@1.51.3-next.3': + resolution: {integrity: sha512-wlk009Kd5EEjcJKi+qZ/Ba+FKbkoEQWdBxjqrkxbPTqzhxg64aLpO/AUemsgczhlzq7ISV8gLHOPsxQsdSIxlQ==} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + + '@copilotkit/runtime-client-gql@1.51.3-next.3': + resolution: {integrity: sha512-LK7wOlKM2Ubr0VHzz0Tvxzuff+J+8Y+L45mu5l/ZOZRK4yx3r74KTnnTqhfmBkZQ4PehB3QtRtXg/h+aB6dpkQ==} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + + '@copilotkit/runtime@1.51.3-next.3': + resolution: {integrity: sha512-vGwD0WlUHODHsrOVWQVVT4WFSYf6njA6ALBksstoHlyKmVwJY78LzTDJBHz0yrtOQI5uxoAhd5bivOrIlSPk5Q==} + peerDependencies: + '@anthropic-ai/sdk': ^0.57.0 + '@copilotkit/shared': 1.51.3-next.3 + '@copilotkitnext/agent': 1.51.3-next.3 + '@copilotkitnext/runtime': 1.51.3-next.3 + '@langchain/aws': '>=0.1.9' + '@langchain/community': '>=0.3.58' + '@langchain/core': '>=0.3.66' + '@langchain/google-gauth': '>=0.1.0' + '@langchain/langgraph-sdk': '>=0.1.2' + '@langchain/openai': '>=0.4.2' + groq-sdk: '>=0.3.0 <1.0.0' + langchain: '>=0.3.3' + openai: ^4.85.1 + peerDependenciesMeta: + '@anthropic-ai/sdk': + optional: true + '@langchain/aws': + optional: true + '@langchain/community': + optional: true + '@langchain/google-gauth': + optional: true + '@langchain/langgraph-sdk': + optional: true + '@langchain/openai': + optional: true + groq-sdk: + optional: true + langchain: + optional: true + openai: + optional: true + + '@copilotkit/shared@1.51.3-next.3': + resolution: {integrity: sha512-fYJTaxbGP12gXyDK3Y+CWRP2PAdm0kavV/2xldTw7QLJTkUyuxKI9QcW4S1k9fblrkUv6YULbBzc1x5diBC5MQ==} + peerDependencies: + '@ag-ui/core': ^0.0.43 + + '@copilotkitnext/agent@1.51.2': + resolution: {integrity: sha512-qGUFt/m+AkA9Y/mzp0Nh5HBbPdrPupzFn6vInvXR1/KCh1+Y0RLZ5QDsGc0SJvh6FMiA6XkEuM4mKp0WimGa+g==} + engines: {node: '>=18'} + + '@copilotkitnext/core@1.51.3-next.3': + resolution: {integrity: sha512-yKVPitZ+gjCG/vJ09/zyCHk1EXrOA+exVX4wfYgFkEUKFyZ/Nu683kxQaX6mn4LY7/io1mq3hbRx1KHmKwvihg==} + engines: {node: '>=18'} + + '@copilotkitnext/react@1.51.3-next.3': + resolution: {integrity: sha512-8QcjZ+FukkZo4ZEU8PBznuNSsoz4nI7RhtrCibfz2Cq2uFXxG1t+/GNGJ9vfmJCfSdIs0ZPxYLFFA6jDNIcStA==} + engines: {node: '>=18'} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@copilotkitnext/runtime@1.51.2': + resolution: {integrity: sha512-r6dmy7p/uCgBFcyRzw88MresXuD2Xuflj4hli/vPS79KJV2d0d4o6TYzb6NeQ2Jz/9jxyklJ/3Q37sr1NkeIOw==} + engines: {node: '>=18'} + peerDependencies: + openai: ^5.9.0 + + '@copilotkitnext/shared@1.51.2': + resolution: {integrity: sha512-fAYsRBNvVQkf1psixsl1a+wn/23thbFc8PAifxXQHmWfbU6Mr2XXCkKUnBbv3TtLa0MbvL1RZJ8+K+LJl8Roiw==} + engines: {node: '>=18'} + + '@copilotkitnext/shared@1.51.3-next.3': + resolution: {integrity: sha512-tyiJXD9tWYgA1j7ANDeR6gU1Oy/VKfWJ083/vegrDBv/sOhpMTZmPGMGd6ICam8Jtyo+8pzrtqeNhbPLU4tccQ==} + engines: {node: '>=18'} + + '@copilotkitnext/web-inspector@1.51.3-next.3': + resolution: {integrity: sha512-bmIjxke9M5GM1RYpYoHyhLOCQ3hjUYqmSfj0kJxP1DfrfzHa5euahsfRNpO6UH/IAXeGkedxfq99rGMRCL7dSg==} + engines: {node: '>=18'} + + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.24': + resolution: {integrity: sha512-T0pSTcd9eYEHV+llVPSkZU7URdVGu87BpSvozMwRoLJYXmLXvEHgYfv0yDsQH9+DIdLzkJCOJBABqWWnwTGPvg==} + engines: {node: '>=18'} + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@envelop/core@5.5.0': + resolution: {integrity: sha512-nsU1EyJQAStaKHR1ZkB/ug9XBm+WPTliYtdedbJ/L1ykrp7dbbn0srqBeDnZ2mbZVp4hH3d0Fy+Og9OgPWZx+g==} + engines: {node: '>=18.0.0'} + + '@envelop/instrumentation@1.0.0': + resolution: {integrity: sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw==} + engines: {node: '>=18.0.0'} + + '@envelop/types@5.2.1': + resolution: {integrity: sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg==} + engines: {node: '>=18.0.0'} + + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@exodus/bytes@1.8.0': + resolution: {integrity: sha512-8JPn18Bcp8Uo1T82gR8lh2guEOa5KKU/IEKvvdp0sgmi7coPBWf1Doi1EXsGZb2ehc8ym/StJCjffYV+ne7sXQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@exodus/crypto': ^1.0.0-rc.4 + peerDependenciesMeta: + '@exodus/crypto': + optional: true + + '@fastify/busboy@3.2.0': + resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==} + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.26.28': + resolution: {integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@graphql-tools/executor@1.5.1': + resolution: {integrity: sha512-n94Qcu875Mji9GQ52n5UbgOTxlgvFJicBPYD+FRks9HKIQpdNPjkkrKZUYNG51XKa+bf03rxNflm4+wXhoHHrA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/merge@9.1.7': + resolution: {integrity: sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/schema@10.0.31': + resolution: {integrity: sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/utils@10.11.0': + resolution: {integrity: sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/utils@11.0.0': + resolution: {integrity: sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-typed-document-node/core@3.2.0': + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-yoga/logger@2.0.1': + resolution: {integrity: sha512-Nv0BoDGLMg9QBKy9cIswQ3/6aKaKjlTh87x3GiBg2Z4RrjyrM48DvOOK0pJh1C1At+b0mUIM67cwZcFTDLN4sA==} + engines: {node: '>=18.0.0'} + + '@graphql-yoga/plugin-defer-stream@3.18.0': + resolution: {integrity: sha512-VbvQP6hzSNHXXtX+OlK0DrbzQxE4ifP/PQzXUiECCYFOI8manRz+lmEmI1cL+HS6YlppXbtO7Qeb41MNEWLegA==} + engines: {node: '>=18.0.0'} + peerDependencies: + graphql: ^15.2.0 || ^16.0.0 + graphql-yoga: ^5.18.0 + + '@graphql-yoga/subscription@5.0.5': + resolution: {integrity: sha512-oCMWOqFs6QV96/NZRt/ZhTQvzjkGB4YohBOpKM4jH/lDT4qb7Lex/aGCxpi/JD9njw3zBBtMqxbaC22+tFHVvw==} + engines: {node: '>=18.0.0'} + + '@graphql-yoga/typed-event-target@3.0.2': + resolution: {integrity: sha512-ZpJxMqB+Qfe3rp6uszCQoag4nSw42icURnBRfFYSOmTgEeOe4rD0vYlbA8spvCu2TlCesNTlEN9BLWtQqLxabA==} + engines: {node: '>=18.0.0'} + + '@headlessui/react@2.2.9': + resolution: {integrity: sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + + '@hono/node-server@1.19.9': + resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.1.0': + resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@langchain/core@0.3.80': + resolution: {integrity: sha512-vcJDV2vk1AlCwSh3aBm/urQ1ZrlXFFBocv11bz/NBUfLWD5/UDNMzwPdaAd2dKvNmTWa9FM2lirLU3+JCf4cRA==} + engines: {node: '>=18'} + + '@langchain/langgraph-sdk@0.1.10': + resolution: {integrity: sha512-9srSCb2bSvcvehMgjA2sMMwX0o1VUgPN6ghwm5Fwc9JGAKsQa6n1S4eCwy1h4abuYxwajH5n3spBw+4I2WYbgw==} + peerDependencies: + '@langchain/core': '>=0.2.31 <0.4.0 || ^1.0.0-alpha' + react: ^18 || ^19 + react-dom: ^18 || ^19 + peerDependenciesMeta: + '@langchain/core': + optional: true + react: + optional: true + react-dom: + optional: true + + '@lit-labs/react@2.1.3': + resolution: {integrity: sha512-OD9h2JynerBQUMNzb563jiVpxfvPF0HjQkKY2mx0lpVYvD7F+rtJpOGz6ek+6ufMidV3i+MPT9SX62OKWHFrQg==} + + '@lit-labs/ssr-dom-shim@1.5.1': + resolution: {integrity: sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==} + + '@lit/react@1.0.8': + resolution: {integrity: sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==} + peerDependencies: + '@types/react': 17 || 18 || 19 + + '@lit/reactive-element@2.1.2': + resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} + + '@lukeed/csprng@1.1.0': + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + + '@lukeed/uuid@2.0.1': + resolution: {integrity: sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==} + engines: {node: '>=8'} + + '@mermaid-js/parser@0.6.3': + resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==} + + '@modelcontextprotocol/sdk@1.25.3': + resolution: {integrity: sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + + '@napi-rs/wasm-runtime@1.1.1': + resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + + '@oozcitak/dom@2.0.2': + resolution: {integrity: sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==} + engines: {node: '>=20.0'} + + '@oozcitak/infra@2.0.2': + resolution: {integrity: sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==} + engines: {node: '>=20.0'} + + '@oozcitak/url@3.0.0': + resolution: {integrity: sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==} + engines: {node: '>=20.0'} + + '@oozcitak/util@10.0.0': + resolution: {integrity: sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==} + engines: {node: '>=20.0'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@oxc-minify/binding-android-arm64@0.96.0': + resolution: {integrity: sha512-lzeIEMu/v6Y+La5JSesq4hvyKtKBq84cgQpKYTYM/yGuNk2tfd5Ha31hnC+mTh48lp/5vZH+WBfjVUjjINCfug==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-minify/binding-darwin-arm64@0.96.0': + resolution: {integrity: sha512-i0LkJAUXb4BeBFrJQbMKQPoxf8+cFEffDyLSb7NEzzKuPcH8qrVsnEItoOzeAdYam8Sr6qCHVwmBNEQzl7PWpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-minify/binding-darwin-x64@0.96.0': + resolution: {integrity: sha512-C5vI0WPR+KPIFAD5LMOJk2J8iiT+Nv65vDXmemzXEXouzfEOLYNqnW+u6NSsccpuZHHWAiLyPFkYvKFduveAUQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-minify/binding-freebsd-x64@0.96.0': + resolution: {integrity: sha512-3//5DNx+xUjVBMLLk2sl6hfe4fwfENJtjVQUBXjxzwPuv8xgZUqASG4cRG3WqG5Qe8dV6SbCI4EgKQFjO4KCZA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-minify/binding-linux-arm-gnueabihf@0.96.0': + resolution: {integrity: sha512-WXChFKV7VdDk1NePDK1J31cpSvxACAVztJ7f7lJVYBTkH+iz5D0lCqPcE7a9eb7nC3xvz4yk7DM6dA9wlUQkQg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-minify/binding-linux-arm-musleabihf@0.96.0': + resolution: {integrity: sha512-7B18glYMX4Z/YoqgE3VRLs/2YhVLxlxNKSgrtsRpuR8xv58xca+hEhiFwZN1Rn+NSMZ29Z33LWD7iYWnqYFvRA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-minify/binding-linux-arm64-gnu@0.96.0': + resolution: {integrity: sha512-Yl+KcTldsEJNcaYxxonwAXZ2q3gxIzn3kXYQWgKWdaGIpNhOCWqF+KE5WLsldoh5Ro5SHtomvb8GM6cXrIBMog==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-minify/binding-linux-arm64-musl@0.96.0': + resolution: {integrity: sha512-rNqoFWOWaxwMmUY5fspd/h5HfvgUlA3sv9CUdA2MpnHFiyoJNovR7WU8tGh+Yn0qOAs0SNH0a05gIthHig14IA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-minify/binding-linux-riscv64-gnu@0.96.0': + resolution: {integrity: sha512-3paajIuzGnukHwSI3YBjYVqbd72pZd8NJxaayaNFR0AByIm8rmIT5RqFXbq8j2uhtpmNdZRXiu0em1zOmIScWA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-minify/binding-linux-s390x-gnu@0.96.0': + resolution: {integrity: sha512-9ESrpkB2XG0lQ89JlsxlZa86iQCOs+jkDZLl6O+u5wb7ynUy21bpJJ1joauCOSYIOUlSy3+LbtJLiqi7oSQt5Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxc-minify/binding-linux-x64-gnu@0.96.0': + resolution: {integrity: sha512-UMM1jkns+p+WwwmdjC5giI3SfR2BCTga18x3C0cAu6vDVf4W37uTZeTtSIGmwatTBbgiq++Te24/DE0oCdm1iQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-minify/binding-linux-x64-musl@0.96.0': + resolution: {integrity: sha512-8b1naiC7MdP7xeMi7cQ5tb9W1rZAP9Qz/jBRqp1Y5EOZ1yhSGnf1QWuZ/0pCc+XiB9vEHXEY3Aki/H+86m2eOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-minify/binding-wasm32-wasi@0.96.0': + resolution: {integrity: sha512-bjGDjkGzo3GWU9Vg2qiFUrfoo5QxojPNV/2RHTlbIB5FWkkV4ExVjsfyqihFiAuj0NXIZqd2SAiEq9htVd3RFw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-minify/binding-win32-arm64-msvc@0.96.0': + resolution: {integrity: sha512-4L4DlHUT47qMWQuTyUghpncR3NZHWtxvd0G1KgSjVgXf+cXzFdWQCWZZtCU0yrmOoVCNUf4S04IFCJyAe+Ie7A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-minify/binding-win32-x64-msvc@0.96.0': + resolution: {integrity: sha512-T2ijfqZLpV2bgGGocXV4SXTuMoouqN0asYTIm+7jVOLvT5XgDogf3ZvCmiEnSWmxl21+r5wHcs8voU2iUROXAg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-transform/binding-android-arm64@0.96.0': + resolution: {integrity: sha512-wOm+ZsqFvyZ7B9RefUMsj0zcXw77Z2pXA51nbSQyPXqr+g0/pDGxriZWP8Sdpz/e4AEaKPA9DvrwyOZxu7GRDQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-transform/binding-darwin-arm64@0.96.0': + resolution: {integrity: sha512-td1sbcvzsyuoNRiNdIRodPXRtFFwxzPpC/6/yIUtRRhKn30XQcizxupIvQQVpJWWchxkphbBDh6UN+u+2CJ8Zw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-transform/binding-darwin-x64@0.96.0': + resolution: {integrity: sha512-xgqxnqhPYH2NYkgbqtnCJfhbXvxIf/pnhF/ig5UBK8PYpCEWIP/cfLpQRQ9DcQnRfuxi7RMIF6LdmB1AiS6Fkg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-transform/binding-freebsd-x64@0.96.0': + resolution: {integrity: sha512-1i67OXdl/rvSkcTXqDlh6qGRXYseEmf0rl/R+/i88scZ/o3A+FzlX56sThuaPzSSv9eVgesnoYUjIBJELFc1oA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-transform/binding-linux-arm-gnueabihf@0.96.0': + resolution: {integrity: sha512-9MJBs0SWODsqyzO3eAnacXgJ/sZu1xqinjEwBzkcZ3tQI8nKhMADOzu2NzbVWDWujeoC8DESXaO08tujvUru+Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-transform/binding-linux-arm-musleabihf@0.96.0': + resolution: {integrity: sha512-BQom57I2ScccixljNYh2Wy+5oVZtF1LXiiUPxSLtDHbsanpEvV/+kzCagQpTjk1BVzSQzOxfEUWjvL7mY53pRQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-transform/binding-linux-arm64-gnu@0.96.0': + resolution: {integrity: sha512-kaqvUzNu8LL4aBSXqcqGVLFG13GmJEplRI2+yqzkgAItxoP/LfFMdEIErlTWLGyBwd0OLiNMHrOvkcCQRWadVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-transform/binding-linux-arm64-musl@0.96.0': + resolution: {integrity: sha512-EiG/L3wEkPgTm4p906ufptyblBgtiQWTubGg/JEw82f8uLRroayr5zhbUqx40EgH037a3SfJthIyLZi7XPRFJw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-transform/binding-linux-riscv64-gnu@0.96.0': + resolution: {integrity: sha512-r01CY6OxKGtVeYnvH4mGmtkQMlLkXdPWWNXwo5o7fE2s/fgZPMpqh8bAuXEhuMXipZRJrjxTk1+ZQ4KCHpMn3Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-transform/binding-linux-s390x-gnu@0.96.0': + resolution: {integrity: sha512-4djg2vYLGbVeS8YiA2K4RPPpZE4fxTGCX5g/bOMbCYyirDbmBAIop4eOAj8vOA9i1CcWbDtmp+PVJ1dSw7f3IQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxc-transform/binding-linux-x64-gnu@0.96.0': + resolution: {integrity: sha512-f6pcWVz57Y8jXa2OS7cz3aRNuks34Q3j61+3nQ4xTE8H1KbalcEvHNmM92OEddaJ8QLs9YcE0kUC6eDTbY34+A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-transform/binding-linux-x64-musl@0.96.0': + resolution: {integrity: sha512-NSiRtFvR7Pbhv3mWyPMkTK38czIjcnK0+K5STo3CuzZRVbX1TM17zGdHzKBUHZu7v6IQ6/XsQ3ELa1BlEHPGWQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-transform/binding-wasm32-wasi@0.96.0': + resolution: {integrity: sha512-A91ARLiuZHGN4hBds9s7bW3czUuLuHLsV+cz44iF9j8e1zX9m2hNGXf/acQRbg/zcFUXmjz5nmk8EkZyob876w==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-transform/binding-win32-arm64-msvc@0.96.0': + resolution: {integrity: sha512-IedJf40djKgDObomhYjdRAlmSYUEdfqX3A3M9KfUltl9AghTBBLkTzUMA7O09oo71vYf5TEhbFM7+Vn5vqw7AQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-transform/binding-win32-x64-msvc@0.96.0': + resolution: {integrity: sha512-0fI0P0W7bSO/GCP/N5dkmtB9vBqCA4ggo1WmXTnxNJVmFFOtcA1vYm1I9jl8fxo+sucW2WnlpnI4fjKdo3JKxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + + '@protobuf-ts/protoc@2.11.1': + resolution: {integrity: sha512-mUZJaV0daGO6HUX90o/atzQ6A7bbN2RSuHtdwo8SSF2Qoe3zHwa4IHyCN1evftTeHfLmdz+45qo47sL+5P8nyg==} + hasBin: true + + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dropdown-menu@2.1.16': + resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-menu@2.1.16': + resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tooltip@1.2.8': + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@react-aria/focus@3.21.3': + resolution: {integrity: sha512-FsquWvjSCwC2/sBk4b+OqJyONETUIXQ2vM0YdPAuC+QFQh2DT6TIBo6dOZVSezlhudDla69xFBd6JvCFq1AbUw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/interactions@3.26.0': + resolution: {integrity: sha512-AAEcHiltjfbmP1i9iaVw34Mb7kbkiHpYdqieWufldh4aplWgsF11YQZOfaCJW4QoR2ML4Zzoa9nfFwLXA52R7Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/ssr@3.9.10': + resolution: {integrity: sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==} + engines: {node: '>= 12'} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/utils@3.32.0': + resolution: {integrity: sha512-/7Rud06+HVBIlTwmwmJa2W8xVtgxgzm0+kLbuFooZRzKDON6hhozS1dOMR/YLMxyJOaYOTpImcP4vRR9gL1hEg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/flags@3.1.2': + resolution: {integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==} + + '@react-stately/utils@3.11.0': + resolution: {integrity: sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/shared@3.32.1': + resolution: {integrity: sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@repeaterjs/repeater@3.0.6': + resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} + + '@rolldown/pluginutils@1.0.0-beta.40': + resolution: {integrity: sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w==} + + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} + + '@rollup/rollup-android-arm-eabi@4.55.1': + resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.55.1': + resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.55.1': + resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.55.1': + resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.55.1': + resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.55.1': + resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.55.1': + resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.55.1': + resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.55.1': + resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.55.1': + resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.55.1': + resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.55.1': + resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.55.1': + resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.55.1': + resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.55.1': + resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.55.1': + resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.55.1': + resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.55.1': + resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.55.1': + resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.55.1': + resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.55.1': + resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==} + cpu: [x64] + os: [win32] + + '@scarf/scarf@1.4.0': + resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + + '@segment/analytics-core@1.8.2': + resolution: {integrity: sha512-5FDy6l8chpzUfJcNlIcyqYQq4+JTUynlVoCeCUuVz+l+6W0PXg+ljKp34R4yLVCcY5VVZohuW+HH0VLWdwYVAg==} + + '@segment/analytics-generic-utils@1.2.0': + resolution: {integrity: sha512-DfnW6mW3YQOLlDQQdR89k4EqfHb0g/3XvBXkovH1FstUN93eL1kfW9CsDcVQyH3bAC5ZsFyjA/o/1Q2j0QeoWw==} + + '@segment/analytics-node@2.3.0': + resolution: {integrity: sha512-fOXLL8uY0uAWw/sTLmezze80hj8YGgXXlAfvSS6TUmivk4D/SP0C0sxnbpFdkUzWg2zT64qWIZj26afEtSnxUA==} + engines: {node: '>=20'} + + '@shikijs/core@3.21.0': + resolution: {integrity: sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA==} + + '@shikijs/engine-javascript@3.21.0': + resolution: {integrity: sha512-ATwv86xlbmfD9n9gKRiwuPpWgPENAWCLwYCGz9ugTJlsO2kOzhOkvoyV/UD+tJ0uT7YRyD530x6ugNSffmvIiQ==} + + '@shikijs/engine-oniguruma@3.21.0': + resolution: {integrity: sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ==} + + '@shikijs/langs@3.21.0': + resolution: {integrity: sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA==} + + '@shikijs/themes@3.21.0': + resolution: {integrity: sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw==} + + '@shikijs/types@3.21.0': + resolution: {integrity: sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@solid-primitives/event-listener@2.4.3': + resolution: {integrity: sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/keyboard@1.3.3': + resolution: {integrity: sha512-9dQHTTgLBqyAI7aavtO+HnpTVJgWQA1ghBSrmLtMu1SMxLPDuLfuNr+Tk5udb4AL4Ojg7h9JrKOGEEDqsJXWJA==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/resize-observer@2.1.3': + resolution: {integrity: sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/rootless@1.5.2': + resolution: {integrity: sha512-9HULb0QAzL2r47CCad0M+NKFtQ+LrGGNHZfteX/ThdGvKIg2o2GYhBooZubTCd/RTu2l2+Nw4s+dEfiDGvdrrQ==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/static-store@0.1.2': + resolution: {integrity: sha512-ReK+5O38lJ7fT+L6mUFvUr6igFwHBESZF+2Ug842s7fvlVeBdIVEdTCErygff6w7uR6+jrr7J8jQo+cYrEq4Iw==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/utils@6.3.2': + resolution: {integrity: sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ==} + peerDependencies: + solid-js: ^1.6.12 + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@swc/helpers@0.5.18': + resolution: {integrity: sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==} + + '@tailwindcss/node@4.1.18': + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} + + '@tailwindcss/oxide-android-arm64@4.1.18': + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.18': + resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.18': + resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.18': + resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@tanstack/ai-client@0.2.2': + resolution: {integrity: sha512-7WVYzMas6ACtt4NMGqnduWbKwDr1syYrmgQoy+hw3Iu5lEEIqdI/tG4koJJROtOM7ogUjKQBpIgsX4Tej+fPWA==} + + '@tanstack/ai-openai@0.2.1': + resolution: {integrity: sha512-MCD/WgJ7qZr/oWyik/zlsXzuO++np+Ag5l5n/5ZmX4mXc3R8Ga1qDeCt+/7RSSmxDJbogeGGn/0j6JpQLrLmRA==} + peerDependencies: + '@tanstack/ai': ^0.2.1 + zod: ^4.0.0 + + '@tanstack/ai-react@0.2.2': + resolution: {integrity: sha512-CNSOOoAUjre5lQxbQVqsXIJEJsTEdyPfyuvAFtgBGvbAJAVn5+6AZyOJzZZz1YKO3F05yD4hMkygelvoSPTUJA==} + peerDependencies: + '@tanstack/ai': ^0.2.2 + '@types/react': '>=18.0.0' + react: '>=18.0.0' + + '@tanstack/ai@0.2.2': + resolution: {integrity: sha512-qqnUSKYMuJnGhiL6t8BAu3Joc9QhQTJIxUIWgQlObDhdY+dCJMLyv+Z7Zw+WqzCCjDfvWmHgLNWDI8+f3KkOPw==} + engines: {node: '>=18'} + + '@tanstack/devtools-client@0.0.3': + resolution: {integrity: sha512-kl0r6N5iIL3t9gGDRAv55VRM3UIyMKVH83esRGq7xBjYsRLe/BeCIN2HqrlJkObUXQMKhy7i8ejuGOn+bDqDBw==} + engines: {node: '>=18'} + + '@tanstack/devtools-client@0.0.5': + resolution: {integrity: sha512-hsNDE3iu4frt9cC2ppn1mNRnLKo2uc1/1hXAyY9z4UYb+o40M2clFAhiFoo4HngjfGJDV3x18KVVIq7W4Un+zA==} + engines: {node: '>=18'} + + '@tanstack/devtools-event-bus@0.3.3': + resolution: {integrity: sha512-lWl88uLAz7ZhwNdLH6A3tBOSEuBCrvnY9Fzr5JPdzJRFdM5ZFdyNWz1Bf5l/F3GU57VodrN0KCFi9OA26H5Kpg==} + engines: {node: '>=18'} + + '@tanstack/devtools-event-client@0.3.5': + resolution: {integrity: sha512-RL1f5ZlfZMpghrCIdzl6mLOFLTuhqmPNblZgBaeKfdtk5rfbjykurv+VfYydOFXj0vxVIoA2d/zT7xfD7Ph8fw==} + engines: {node: '>=18'} + + '@tanstack/devtools-event-client@0.4.0': + resolution: {integrity: sha512-RPfGuk2bDZgcu9bAJodvO2lnZeHuz4/71HjZ0bGb/SPg8+lyTA+RLSKQvo7fSmPSi8/vcH3aKQ8EM9ywf1olaw==} + engines: {node: '>=18'} + + '@tanstack/devtools-ui@0.4.4': + resolution: {integrity: sha512-5xHXFyX3nom0UaNfiOM92o6ziaHjGo3mcSGe2HD5Xs8dWRZNpdZ0Smd0B9ddEhy0oB+gXyMzZgUJb9DmrZV0Mg==} + engines: {node: '>=18'} + peerDependencies: + solid-js: '>=1.9.7' + + '@tanstack/devtools-vite@0.3.12': + resolution: {integrity: sha512-fGJgu4xUhKmGk+a+/aHD8l5HKVk6+ObA+6D3YC3xCXbai/YmaGhztqcZf1tKUqjZyYyQLHsjqmKzvJgVpQP1jw==} + engines: {node: '>=18'} + peerDependencies: + vite: ^6.0.0 || ^7.0.0 + + '@tanstack/devtools@0.7.0': + resolution: {integrity: sha512-AlAoCqJhWLg9GBEaoV1g/j+X/WA1aJSWOsekxeuZpYeS2hdVuKAjj04KQLUMJhtLfNl2s2E+TCj7ZRtWyY3U4w==} + engines: {node: '>=18'} + peerDependencies: + solid-js: '>=1.9.7' + + '@tanstack/history@1.145.7': + resolution: {integrity: sha512-gMo/ReTUp0a3IOcZoI3hH6PLDC2R/5ELQ7P2yu9F6aEkA0wSQh+Q4qzMrtcKvF2ut0oE+16xWCGDo/TdYd6cEQ==} + engines: {node: '>=12'} + + '@tanstack/query-core@5.90.16': + resolution: {integrity: sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww==} + + '@tanstack/react-devtools@0.7.11': + resolution: {integrity: sha512-a2Lmz8x+JoDrsU6f7uKRcyyY+k8mA/n5mb9h7XJ3Fz/y3+sPV9t7vAW1s5lyNkQyyDt6V1Oim99faLthoJSxMw==} + engines: {node: '>=18'} + peerDependencies: + '@types/react': '>=16.8' + '@types/react-dom': '>=16.8' + react: '>=16.8' + react-dom: '>=16.8' + + '@tanstack/react-query@5.90.16': + resolution: {integrity: sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ==} + peerDependencies: + react: ^18 || ^19 + + '@tanstack/react-router-devtools@1.147.1': + resolution: {integrity: sha512-0rmPzr3euq4IljgRT7bvdn5VsGN++TLdPZ4WViQBT1edoERIKqf+LAibJ8dtGOW/LRSgHtkiohoXJphqAjAIVw==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/react-router': ^1.147.1 + '@tanstack/router-core': ^1.147.1 + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + peerDependenciesMeta: + '@tanstack/router-core': + optional: true + + '@tanstack/react-router-ssr-query@1.147.1': + resolution: {integrity: sha512-uFW4sDXVDnnBOv/tvjA5pp//Sv9YsyXLahAwVlKUfSTiOMAwQChqsLmqQUGbgKLr7D5J0GG/RTqApp58goEgog==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/query-core': '>=5.90.0' + '@tanstack/react-query': '>=5.90.0' + '@tanstack/react-router': '>=1.127.0' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-router@1.147.1': + resolution: {integrity: sha512-TKoQRxCua9tuFUhZch5bn5MYKTGAtr96Gp/GB8uGKhkLoHXkCP7HA8nwkZU/JSluONiro42+3MoAsQA8HTkIPQ==} + engines: {node: '>=12'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-client@1.147.1': + resolution: {integrity: sha512-vr4hUhNYzdLVQ9N4e6FGaZaFFfwaay7UDXPOvb0JRTdw4YVFVm/hbxaeoiVzgNH+jTGal79DXZangy0/lJC0ag==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-server@1.147.1': + resolution: {integrity: sha512-cU+ppbjfeHLlDDXDRw7L1d+b6eCoFpX7+XmVopQEv5oxWBSgUG6kWYGsoGOkb6eQ/EEJs4McSHkL62gpSbEwSw==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start@1.147.1': + resolution: {integrity: sha512-CaoUZpAmy2gw4j7henaEaYhwzdHQQ0XyrXXOPlfTPE3xV8lJIDh4yHdaLiHbPnHZT1O03IEZyDu4yzJ7C7kYkg==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + vite: '>=7.0.0' + + '@tanstack/react-store@0.8.0': + resolution: {integrity: sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/react-virtual@3.13.18': + resolution: {integrity: sha512-dZkhyfahpvlaV0rIKnvQiVoWPyURppl6w4m9IwMDpuIjcJ1sD9YGWrt0wISvgU7ewACXx2Ct46WPgI6qAD4v6A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.147.1': + resolution: {integrity: sha512-yf8o3CNgJVGO5JnIqiTe0y2eChxEM0w7TrEs1VSumL/zz2bQroYGNr1mOXJ2VeN+7YfJJwjEqq71P5CzWwMzRg==} + engines: {node: '>=12'} + + '@tanstack/router-devtools-core@1.147.1': + resolution: {integrity: sha512-jviwWLRW+ro0hDCDUFl8Ezq68LUr+ZSPQ5XixNNKueHmOiSkHDqL5hUKln4DCFlv2yUtTDWAq4GNh9zun6ElUg==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/router-core': ^1.147.1 + csstype: ^3.0.10 + solid-js: '>=1.9.5' + peerDependenciesMeta: + csstype: + optional: true + + '@tanstack/router-generator@1.147.1': + resolution: {integrity: sha512-4Ypzmfuldl1RF6LP6SWyBQMVQM0/xDtHLsKGKl1+xX6aqQCNt2qBZP2gz3epcDI+JxJzyLYYfmvzQtEh4pUHng==} + engines: {node: '>=12'} + + '@tanstack/router-plugin@1.147.1': + resolution: {integrity: sha512-gVrcHYxWA1OQKw0YEbND8nb4UDJZq+rO8Xu0BQIQdbKndhRfqyUk3riCFvr9XNgeI4G0g2lsFyb46Hqgn2gDJQ==} + engines: {node: '>=12'} + peerDependencies: + '@rsbuild/core': '>=1.0.2' + '@tanstack/react-router': ^1.147.1 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' + vite-plugin-solid: ^2.11.10 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-ssr-query-core@1.147.1': + resolution: {integrity: sha512-5lQcCiRotsUe81P/dPrvirYjod2N+/jgBWgeCNu0UhvSeXWyAIJq3Cjwa7kE8TjT/l1n3kYwx6RzBn1qHV2Htw==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/query-core': '>=5.90.0' + '@tanstack/router-core': '>=1.127.0' + + '@tanstack/router-utils@1.143.11': + resolution: {integrity: sha512-N24G4LpfyK8dOlnP8BvNdkuxg1xQljkyl6PcrdiPSA301pOjatRT1y8wuCCJZKVVD8gkd0MpCZ0VEjRMGILOtA==} + engines: {node: '>=12'} + + '@tanstack/start-client-core@1.147.1': + resolution: {integrity: sha512-XRYt+cgAw9Jq269xkhHed5/7Xhpqta/76efLG4bUImV/0Jjx5gSpx5tLGib3fSSP3DYnjZHH3YhZP/A1xSQYQQ==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-fn-stubs@1.143.8': + resolution: {integrity: sha512-2IKUPh/TlxwzwHMiHNeFw95+L2sD4M03Es27SxMR0A60Qc4WclpaD6gpC8FsbuNASM2jBxk2UyeYClJxW1GOAQ==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-plugin-core@1.147.1': + resolution: {integrity: sha512-OG9qdv2NYArQlAnG/VjUtZxBP2M/Zz/gvXc5S6Rz3xkDgaFVHjzlsBc4a1nZDGxRsWAtuZzSFBnOt85fmpabLQ==} + engines: {node: '>=22.12.0'} + peerDependencies: + vite: '>=7.0.0' + + '@tanstack/start-server-core@1.147.1': + resolution: {integrity: sha512-WgNN+5eAp03YaZQgC3Wx1RkzPagbFT/H/fes2sdQStn6JcvkyBdpRPiqiu34urlQ1N4ropZbYQXXV/IxVka/EA==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-storage-context@1.147.1': + resolution: {integrity: sha512-Th4WnghQsdls+Y6ex08MFlONDvt1fpdutm+VJg/CCc1QuASQZo7fZFo6VZmTxid0flqK8aE+nVrAhu45fkkkjg==} + engines: {node: '>=22.12.0'} + + '@tanstack/store@0.8.0': + resolution: {integrity: sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ==} + + '@tanstack/virtual-core@3.13.18': + resolution: {integrity: sha512-Mx86Hqu1k39icq2Zusq+Ey2J6dDWTjDvEv43PJtRCoEYTLyfaPnxIQ6iy7YAOK0NV/qOEmZQ/uCufrppZxTgcg==} + + '@tanstack/virtual-file-routes@1.145.4': + resolution: {integrity: sha512-CI75JrfqSluhdGwLssgVeQBaCphgfkMQpi8MCY3UJX1hoGzXa8kHYJcUuIFMOLs1q7zqHy++EVVtMK03osR5wQ==} + engines: {node: '>=12'} + + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/react@16.3.1': + resolution: {integrity: sha512-gr4KtAWqIOQoucWYD/f6ki+j5chXfcPc74Col/6poTyqTmn7zRmodWahWRCp8tYd+GMqBonw6hstNzqjbs6gjw==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/katex@0.16.8': + resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@22.19.5': + resolution: {integrity: sha512-HfF8+mYcHPcPypui3w3mvzuIErlNOh2OAG+BCeBZCEwyiD5ls2SiCwEyT47OELtf7M3nHxBdu0FsmzdKxkN52Q==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.8': + resolution: {integrity: sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==} + + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/uuid@10.0.0': + resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} + + '@types/validator@13.15.10': + resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@urql/core@5.2.0': + resolution: {integrity: sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==} + + '@vercel/oidc@3.1.0': + resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} + engines: {node: '>= 20'} + + '@vitejs/plugin-react@5.1.2': + resolution: {integrity: sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@whatwg-node/disposablestack@0.0.6': + resolution: {integrity: sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/events@0.1.2': + resolution: {integrity: sha512-ApcWxkrs1WmEMS2CaLLFUEem/49erT3sxIVjpzU5f6zmVcnijtDSrhoK2zVobOIikZJdH63jdAXOrvjf6eOUNQ==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/fetch@0.10.13': + resolution: {integrity: sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/node-fetch@0.8.5': + resolution: {integrity: sha512-4xzCl/zphPqlp9tASLVeUhB5+WJHbuWGYpfoC2q1qh5dw0AqZBW7L27V5roxYWijPxj4sspRAAoOH3d2ztaHUQ==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/promise-helpers@1.3.2': + resolution: {integrity: sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==} + engines: {node: '>=16.0.0'} + + '@whatwg-node/server@0.10.18': + resolution: {integrity: sha512-kMwLlxUbduttIgaPdSkmEarFpP+mSY8FEm+QWMBRJwxOHWkri+cxd8KZHO9EMrB9vgUuz+5WEaCawaL5wGVoXg==} + engines: {node: '>=18.0.0'} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ai@5.0.123: + resolution: {integrity: sha512-V3Imb0tg0pHCa6a/VsoW/FZpT07mwUw/4Hj6nexJC1Nvf1eyKQJyaYVkl+YTLnA8cKQSUkoarKhXWbFy4CSgjw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + babel-dead-code-elimination@1.0.11: + resolution: {integrity: sha512-mwq3W3e/pKSI6TG8lXMiDWvEi1VXYlSBlJlB3l+I0bAb5u1RNUl88udos85eOPNK3m5EXK9uO7d2g08pesTySQ==} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.9.14: + resolution: {integrity: sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==} + hasBin: true + + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + body-parser@1.20.4: + resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + engines: {node: '>=18'} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001763: + resolution: {integrity: sha512-mh/dGtq56uN98LlNX9qdbKnzINhX0QzhiWBFEkFfsFO4QyCvL8YegrJAazCwXIeqkIob8BlZPGM3xdnY+sgmvQ==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.1.2: + resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==} + engines: {node: '>=20.18.1'} + + chevrotain-allstar@0.3.1: + resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==} + peerDependencies: + chevrotain: ^11.0.0 + + chevrotain@11.0.3: + resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + class-transformer@0.5.1: + resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} + + class-validator@0.14.3: + resolution: {integrity: sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + comma-separated-tokens@1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + console-table-printer@2.15.0: + resolution: {integrity: sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-disposition@1.0.1: + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@2.0.0: + resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} + + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + + cose-base@1.0.3: + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + + cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + + cross-inspect@1.0.1: + resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==} + engines: {node: '>=16.0.0'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crossws@0.4.1: + resolution: {integrity: sha512-E7WKBcHVhAVrY6JYD5kteNqVq1GSZxqGrdSiwXR9at+XHi43HJoCQKXcCczR5LBnBquFZPsB3o7HklulKoBU5w==} + peerDependencies: + srvx: '>=0.7.1' + peerDependenciesMeta: + srvx: + optional: true + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssstyle@5.3.7: + resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==} + engines: {node: '>=20'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + cytoscape-cose-bilkent@4.1.0: + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.33.1: + resolution: {integrity: sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==} + engines: {node: '>=0.10'} + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dagre-d3-es@7.0.13: + resolution: {integrity: sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==} + + data-urls@6.0.0: + resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==} + engines: {node: '>=20'} + + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + + db0@0.3.4: + resolution: {integrity: sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==} + peerDependencies: + '@electric-sql/pglite': '*' + '@libsql/client': '*' + better-sqlite3: '*' + drizzle-orm: '*' + mysql2: '*' + sqlite3: '*' + peerDependenciesMeta: + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + better-sqlite3: + optional: true + drizzle-orm: + optional: true + mysql2: + optional: true + sqlite3: + optional: true + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + delaunator@5.0.1: + resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + diff@8.0.2: + resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} + engines: {node: '>=0.3.1'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + dompurify@3.3.1: + resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + enhanced-resolve@5.18.4: + resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + eventsource-parser@3.0.6: + resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-copy@3.0.2: + resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-patch@3.1.1: + resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + goober@2.1.18: + resolution: {integrity: sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==} + peerDependencies: + csstype: ^3.0.10 + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphql-query-complexity@0.12.0: + resolution: {integrity: sha512-fWEyuSL6g/+nSiIRgIipfI6UXTI7bAxrpPlCY1c0+V3pAEUo1ybaKmSBgNr1ed2r+agm1plJww8Loig9y6s2dw==} + peerDependencies: + graphql: ^14.6.0 || ^15.0.0 || ^16.0.0 + + graphql-scalars@1.25.0: + resolution: {integrity: sha512-b0xyXZeRFkne4Eq7NAnL400gStGqG/Sx9VqX0A05nHyEbv57UJnWKsjNnrpVqv5e/8N1MUxkt0wwcRXbiyKcFg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + + graphql-yoga@5.18.0: + resolution: {integrity: sha512-xFt1DVXS1BZ3AvjnawAGc5OYieSe56WuQuyk3iEpBwJ3QDZJWQGLmU9z/L5NUZ+pUcyprsz/bOwkYIV96fXt/g==} + engines: {node: '>=18.0.0'} + peerDependencies: + graphql: ^15.2.0 || ^16.0.0 + + graphql@16.12.0: + resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + h3@2.0.1-rc.5: + resolution: {integrity: sha512-qkohAzCab0nLzXNm78tBjZDvtKMTmtygS8BJLT3VPczAQofdqlFXDPkXdLMJN4r05+xqneG8snZJ0HgkERCZTg==} + engines: {node: '>=20.11.1'} + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + crossws: + optional: true + + h3@2.0.1-rc.7: + resolution: {integrity: sha512-qbrRu1OLXmUYnysWOCVrYhtC/m8ZuXu/zCbo3U/KyphJxbPFiC76jHYwVrmEcss9uNAHO5BoUguQ46yEpgI2PA==} + engines: {node: '>=20.11.1'} + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + crossws: + optional: true + + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-util-from-dom@5.0.1: + resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==} + + hast-util-from-html-isomorphic@2.0.0: + resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==} + + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-sanitize@5.0.2: + resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.1: + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hast@1.0.0: + resolution: {integrity: sha512-vFUqlRV5C+xqP76Wwq2SrM0kipnmpxJm7OfvVXpB35Fp+Fn4MV+ozr+JZr5qFvyR1q/U+Foim2x+3P+x9S1PLA==} + deprecated: Renamed to rehype + + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + + help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + highlightjs-vue@1.0.0: + resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + + hono@4.11.5: + resolution: {integrity: sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g==} + engines: {node: '>=16.9.0'} + + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + isbot@5.1.32: + resolution: {integrity: sha512-VNfjM73zz2IBZmdShMfAUg10prm6t7HFUQmNAEOAVS4YH92ZrZcvkMcGX6cIgBJAzWDzPent/EeAtYEHNPNPBQ==} + engines: {node: '>=18'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + jose@5.10.0: + resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-tiktoken@1.0.21: + resolution: {integrity: sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsdom@27.4.0: + resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + katex@0.16.27: + resolution: {integrity: sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==} + hasBin: true + + khroma@2.1.0: + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + + langium@3.3.1: + resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} + engines: {node: '>=16.0.0'} + + langsmith@0.3.87: + resolution: {integrity: sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q==} + peerDependencies: + '@opentelemetry/api': '*' + '@opentelemetry/exporter-trace-otlp-proto': '*' + '@opentelemetry/sdk-trace-base': '*' + openai: '*' + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/exporter-trace-otlp-proto': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + openai: + optional: true + + launch-editor@2.12.0: + resolution: {integrity: sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==} + + layout-base@1.0.2: + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + + layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + + libphonenumber-js@1.12.35: + resolution: {integrity: sha512-T/Cz6iLcsZdb5jDncDcUNhSAJ0VlSC9TnsqtBNdpkaAmy24/R1RhErtNWVWBrcUZKs9hSgaVsBkc7HxYnazIfw==} + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + lit-element@4.2.2: + resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} + + lit-html@3.3.2: + resolution: {integrity: sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==} + + lit@3.3.2: + resolution: {integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash-es@4.17.23: + resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@0.525.0: + resolution: {integrity: sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lucide-react@0.542.0: + resolution: {integrity: sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lucide-react@0.561.0: + resolution: {integrity: sha512-Y59gMY38tl4/i0qewcqohPdEbieBy7SovpBL9IFebhc2mDd8x4PZSOsiFRkpPcOq6bj1r/mjH/Rk73gSlIJP2A==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lucide@0.525.0: + resolution: {integrity: sha512-sfehWlaE/7NVkcEQ4T9JD3eID8RNMIGJBBUq9wF3UFiJIrcMKRbU3g1KGfDk4svcW7yw8BtDLXaXo02scDtUYQ==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + marked@12.0.2: + resolution: {integrity: sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==} + engines: {node: '>= 18'} + hasBin: true + + marked@16.4.2: + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + engines: {node: '>= 20'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-math@3.0.0: + resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + mermaid@11.12.2: + resolution: {integrity: sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-cjk-friendly-gfm-strikethrough@1.2.3: + resolution: {integrity: sha512-gSPnxgHDDqXYOBvQRq6lerrq9mjDhdtKn+7XETuXjxWcL62yZEfUdA28Ml1I2vDIPfAOIKLa0h2XDSGkInGHFQ==} + engines: {node: '>=16'} + peerDependencies: + micromark: ^4.0.0 + micromark-util-types: ^2.0.0 + peerDependenciesMeta: + micromark-util-types: + optional: true + + micromark-extension-cjk-friendly-util@2.1.1: + resolution: {integrity: sha512-egs6+12JU2yutskHY55FyR48ZiEcFOJFyk9rsiyIhcJ6IvWB6ABBqVrBw8IobqJTDZ/wdSr9eoXDPb5S2nW1bg==} + engines: {node: '>=16'} + peerDependencies: + micromark-util-types: '*' + peerDependenciesMeta: + micromark-util-types: + optional: true + + micromark-extension-cjk-friendly@1.2.3: + resolution: {integrity: sha512-gRzVLUdjXBLX6zNPSnHGDoo+ZTp5zy+MZm0g3sv+3chPXY7l9gW+DnrcHcZh/jiPR6MjPKO4AEJNp4Aw6V9z5Q==} + engines: {node: '>=16'} + peerDependencies: + micromark: ^4.0.0 + micromark-util-types: ^2.0.0 + peerDependenciesMeta: + micromark-util-types: + optional: true + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-extension-math@3.1.0: + resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mlly@1.8.0: + resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mustache@4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + nf3@0.1.12: + resolution: {integrity: sha512-qbMXT7RTGh74MYWPeqTIED8nDW70NXOULVHpdWcdZ7IVHVnAsMV9fNugSNnvooipDc1FMOzpis7T9nXJEbJhvQ==} + + nitro@3.0.1-alpha.1: + resolution: {integrity: sha512-U4AxIsXxdkxzkFrK0XAw0e5Qbojk8jQ50MjjRBtBakC4HurTtQoiZvF+lSe382jhuQZCfAyywGWOFa9QzXLFaw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + rolldown: '*' + rollup: ^4 + vite: ^7 + xml2js: ^0.6.2 + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + vite: + optional: true + xml2js: + optional: true + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + ofetch@2.0.0-alpha.3: + resolution: {integrity: sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.4: + resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + + openai@6.16.0: + resolution: {integrity: sha512-fZ1uBqjFUjXzbGc35fFtYKEOxd20kd9fDpFeqWtsOZWiubY8CZ1NAlXHW3iathaFvqmNtCWMIsosCuyeI7Joxg==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + oxc-minify@0.96.0: + resolution: {integrity: sha512-dXeeGrfPJJ4rMdw+NrqiCRtbzVX2ogq//R0Xns08zql2HjV3Zi2SBJ65saqfDaJzd2bcHqvGWH+M44EQCHPAcA==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-transform@0.96.0: + resolution: {integrity: sha512-dQPNIF+gHpSkmC0+Vg9IktNyhcn28Y8R3eTLyzn52UNymkasLicl3sFAtz7oEVuFmCpgGjaUTKkwk+jW2cHpDQ==} + engines: {node: ^20.19.0 || >=22.12.0} + + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + + p-queue@6.6.2: + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + engines: {node: '>=8'} + + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + + package-manager-detector@1.6.0: + resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + + parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parse5@8.0.0: + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + partial-json@0.1.7: + resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} + + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-pretty@11.3.0: + resolution: {integrity: sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==} + hasBin: true + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@9.14.0: + resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} + hasBin: true + + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + prismjs@1.27.0: + resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} + engines: {node: '>=6'} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + property-information@5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.14.1: + resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} + engines: {node: '>=0.6'} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} + peerDependencies: + react: ^19.2.3 + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-syntax-highlighter@15.6.6: + resolution: {integrity: sha512-DgXrc+AZF47+HvAPEmn7Ua/1p10jNoVZVI/LoPiYdtY+OM+/nG5yefLHKJwdKqY1adMuHFbeyBaG9j64ML7vTw==} + peerDependencies: + react: '>= 0.14.0' + + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + engines: {node: '>=0.10.0'} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + + refractor@3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + rehype-harden@1.1.7: + resolution: {integrity: sha512-j5DY0YSK2YavvNGV+qBHma15J9m0WZmRe8posT5AtKDS6TNWtMVTo6RiqF8SidfcASYz8f3k2J/1RWmq5zTXUw==} + + rehype-katex@7.0.1: + resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-sanitize@6.0.0: + resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} + + remark-cjk-friendly-gfm-strikethrough@1.2.3: + resolution: {integrity: sha512-bXfMZtsaomK6ysNN/UGRIcasQAYkC10NtPmP0oOHOV8YOhA2TXmwRXCku4qOzjIFxAPfish5+XS0eIug2PzNZA==} + engines: {node: '>=16'} + peerDependencies: + '@types/mdast': ^4.0.0 + unified: ^11.0.0 + peerDependenciesMeta: + '@types/mdast': + optional: true + + remark-cjk-friendly@1.2.3: + resolution: {integrity: sha512-UvAgxwlNk+l9Oqgl/9MWK2eWRS7zgBW/nXX9AthV7nd/3lNejF138E7Xbmk9Zs4WjTJGs721r7fAEc7tNFoH7g==} + engines: {node: '>=16'} + peerDependencies: + '@types/mdast': ^4.0.0 + unified: ^11.0.0 + peerDependenciesMeta: + '@types/mdast': + optional: true + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-math@6.0.0: + resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + remend@1.0.1: + resolution: {integrity: sha512-152puVH0qMoRJQFnaMG+rVDdf01Jq/CaED+MBuXExurJgdbkLp0c3TIe4R12o28Klx8uyGsjvFNG05aFG69G9w==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + + rollup@4.55.1: + resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rou3@0.7.12: + resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==} + + roughjs@4.6.6: + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + seroval-plugins@1.3.3: + resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval-plugins@1.4.2: + resolution: {integrity: sha512-X7p4MEDTi+60o2sXZ4bnDBhgsUYDSkQEvzYZuJyFqWg9jcoPsHts5nrg5O956py2wyt28lUrBxk0M0/wU8URpA==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.3.2: + resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} + engines: {node: '>=10'} + + seroval@1.4.2: + resolution: {integrity: sha512-N3HEHRCZYn3cQbsC4B5ldj9j+tHdf4JZoYPlcI4rRYu0Xy4qN8MQf1Z08EibzB0WpgRG5BGK08FTrmM66eSzKQ==} + engines: {node: '>=10'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + shiki@3.21.0: + resolution: {integrity: sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w==} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + simple-wcswidth@1.1.2: + resolution: {integrity: sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==} + + solid-js@1.9.10: + resolution: {integrity: sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==} + + sonic-boom@4.2.0: + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + space-separated-tokens@1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + srvx@0.10.0: + resolution: {integrity: sha512-NqIsR+wQCfkvvwczBh8J8uM4wTZx41K2lLSEp/3oMp917ODVVMtW5Me4epCmQ3gH8D+0b+/t4xxkUKutyhimTA==} + engines: {node: '>=20.16.0'} + hasBin: true + + srvx@0.9.8: + resolution: {integrity: sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ==} + engines: {node: '>=20.16.0'} + hasBin: true + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + streamdown@1.6.11: + resolution: {integrity: sha512-Y38fwRx5kCKTluwM+Gf27jbbi9q6Qy+WC9YrC1YbCpMkktT3PsRBJHMWiqYeF8y/JzLpB1IzDoeaB6qkQEDnAA==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tabbable@6.4.0: + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + + tailwind-merge@3.4.0: + resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} + + tailwindcss@4.1.18: + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + tldts-core@7.0.19: + resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} + + tldts@7.0.19: + resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} + hasBin: true + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + engines: {node: '>=16'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + ts-dedent@2.2.0: + resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} + engines: {node: '>=6.10'} + + ts-deepmerge@7.0.3: + resolution: {integrity: sha512-Du/ZW2RfwV/D4cmA5rXafYjBQVuvu4qGiEEla4EmEHVHgRdx68Gftx7i66jn2bzHPwSVZY36Ae6OuDn9el4ZKA==} + engines: {node: '>=14.13.1'} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + + type-graphql@2.0.0-rc.1: + resolution: {integrity: sha512-HCu4j3jR0tZvAAoO7DMBT3MRmah0DFRe5APymm9lXUghXA0sbhiMf6SLRafRYfk0R0KiUQYRduuGP3ap1RnF1Q==} + engines: {node: '>= 18.12.0'} + peerDependencies: + class-validator: '>=0.14.0' + graphql: ^16.8.1 + graphql-scalars: ^1.22.4 + peerDependenciesMeta: + class-validator: + optional: true + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.2: + resolution: {integrity: sha512-heMioaxBcG9+Znsda5Q8sQbWnLJSl98AFDXTO80wELWEzX3hordXsTdxrIfMQoO9IY1MEnoGoPjpoKpMj+Yx0Q==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici@7.18.2: + resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==} + engines: {node: '>=20.18.1'} + + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + + unstorage@2.0.0-alpha.5: + resolution: {integrity: sha512-Sj8btci21Twnd6M+N+MHhjg3fVn6lAPElPmvFTe0Y/wR0WImErUdA1PzlAaUavHylJ7uDiFwlZDQKm0elG4b7g==} + peerDependencies: + '@azure/app-configuration': ^1.9.0 + '@azure/cosmos': ^4.7.0 + '@azure/data-tables': ^13.3.1 + '@azure/identity': ^4.13.0 + '@azure/keyvault-secrets': ^4.10.0 + '@azure/storage-blob': ^12.29.1 + '@capacitor/preferences': ^6.0.3 || ^7.0.0 + '@deno/kv': '>=0.12.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.35.6 + '@vercel/blob': '>=0.27.3' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1.0.1 + aws4fetch: ^1.0.20 + chokidar: ^4 || ^5 + db0: '>=0.3.4' + idb-keyval: ^6.2.2 + ioredis: ^5.8.2 + lru-cache: ^11.2.2 + mongodb: ^6 || ^7 + ofetch: '*' + uploadthing: ^7.7.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + chokidar: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + lru-cache: + optional: true + mongodb: + optional: true + ofetch: + optional: true + uploadthing: + optional: true + + untruncate-json@0.0.1: + resolution: {integrity: sha512-4W9enDK4X1y1s2S/Rz7ysw6kDuMS3VmRjMFg7GZrNO+98OSe+x5Lh7PKYoVjy3lW/1wmhs6HW0lusnQRHgMarA==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + urlpattern-polyfill@10.1.0: + resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} + + urql@4.2.2: + resolution: {integrity: sha512-3GgqNa6iF7bC4hY/ImJKN4REQILcSU9VKcKL8gfELZM8mM5BnLH1BsCc8kBdnVGD1LIFOs4W3O2idNHhON1r0w==} + peerDependencies: + '@urql/core': ^5.0.0 + react: '>= 16.8.0' + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-stick-to-bottom@1.1.2: + resolution: {integrity: sha512-ssUfMNvfH8a8hGLoAt5kcOsjbsVORknon2tbkECuf3EsVucFFBbyXl+Xnv3b58P8ZRuZelzO81fgb6M0eRo8cg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + validator@13.15.26: + resolution: {integrity: sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==} + engines: {node: '>= 0.10'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite-tsconfig-paths@6.0.4: + resolution: {integrity: sha512-iIsEJ+ek5KqRTK17pmxtgIxXtqr3qDdE6OxrP9mVeGhVDNXRJTKN/l9oMbujTQNzMLe6XZ8qmpztfbkPu2TiFQ==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + + vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + + vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + + vscode-uri@3.0.8: + resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + web-vitals@5.1.0: + resolution: {integrity: sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@15.1.0: + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + engines: {node: '>=20'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wonka@6.3.5: + resolution: {integrity: sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlbuilder2@4.0.3: + resolution: {integrity: sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==} + engines: {node: '>=20.0'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + zod-to-json-schema@3.25.1: + resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} + peerDependencies: + zod: ^3.25 || ^4 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@0no-co/graphql.web@1.2.0(graphql@16.12.0)': + optionalDependencies: + graphql: 16.12.0 + + '@acemir/cssom@0.9.30': {} + + '@ag-ui/client@0.0.42': + dependencies: + '@ag-ui/core': 0.0.42 + '@ag-ui/encoder': 0.0.42 + '@ag-ui/proto': 0.0.42 + '@types/uuid': 10.0.0 + compare-versions: 6.1.1 + fast-json-patch: 3.1.1 + rxjs: 7.8.1 + untruncate-json: 0.0.1 + uuid: 11.1.0 + zod: 3.25.76 + + '@ag-ui/client@0.0.43': + dependencies: + '@ag-ui/core': 0.0.43 + '@ag-ui/encoder': 0.0.43 + '@ag-ui/proto': 0.0.43 + '@types/uuid': 10.0.0 + compare-versions: 6.1.1 + fast-json-patch: 3.1.1 + rxjs: 7.8.1 + untruncate-json: 0.0.1 + uuid: 11.1.0 + zod: 3.25.76 + + '@ag-ui/core@0.0.42': + dependencies: + rxjs: 7.8.1 + zod: 3.25.76 + + '@ag-ui/core@0.0.43': + dependencies: + rxjs: 7.8.1 + zod: 3.25.76 + + '@ag-ui/encoder@0.0.42': + dependencies: + '@ag-ui/core': 0.0.42 + '@ag-ui/proto': 0.0.42 + + '@ag-ui/encoder@0.0.43': + dependencies: + '@ag-ui/core': 0.0.43 + '@ag-ui/proto': 0.0.43 + + '@ag-ui/langgraph@0.0.22(@ag-ui/client@0.0.43)(@ag-ui/core@0.0.43)(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@ag-ui/client': 0.0.43 + '@ag-ui/core': 0.0.43 + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)) + '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + partial-json: 0.1.7 + rxjs: 7.8.1 + transitivePeerDependencies: + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' + - openai + - react + - react-dom + + '@ag-ui/proto@0.0.42': + dependencies: + '@ag-ui/core': 0.0.42 + '@bufbuild/protobuf': 2.11.0 + '@protobuf-ts/protoc': 2.11.1 + + '@ag-ui/proto@0.0.43': + dependencies: + '@ag-ui/core': 0.0.43 + '@bufbuild/protobuf': 2.11.0 + '@protobuf-ts/protoc': 2.11.1 + + '@ai-sdk/anthropic@2.0.57(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.1 + '@ai-sdk/provider-utils': 3.0.20(zod@3.25.76) + zod: 3.25.76 + + '@ai-sdk/gateway@2.0.29(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.1 + '@ai-sdk/provider-utils': 3.0.20(zod@3.25.76) + '@vercel/oidc': 3.1.0 + zod: 3.25.76 + + '@ai-sdk/google@2.0.52(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.1 + '@ai-sdk/provider-utils': 3.0.20(zod@3.25.76) + zod: 3.25.76 + + '@ai-sdk/mcp@0.0.8(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.17(zod@3.25.76) + pkce-challenge: 5.0.1 + zod: 3.25.76 + + '@ai-sdk/openai@2.0.89(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.1 + '@ai-sdk/provider-utils': 3.0.20(zod@3.25.76) + zod: 3.25.76 + + '@ai-sdk/provider-utils@3.0.17(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.0.6 + zod: 3.25.76 + + '@ai-sdk/provider-utils@3.0.20(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.1 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.0.6 + zod: 3.25.76 + + '@ai-sdk/provider@2.0.0': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/provider@2.0.1': + dependencies: + json-schema: 0.4.0 + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.6.0 + tinyexec: 1.0.2 + + '@asamuzakjp/css-color@4.1.1': + dependencies: + '@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) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 11.2.4 + + '@asamuzakjp/dom-selector@6.7.6': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.1.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.4 + + '@asamuzakjp/nwsapi@2.3.9': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@braintree/sanitize-url@7.1.1': {} + + '@bufbuild/protobuf@2.11.0': {} + + '@cfworker/json-schema@4.1.1': {} + + '@chevrotain/cst-dts-gen@11.0.3': + dependencies: + '@chevrotain/gast': 11.0.3 + '@chevrotain/types': 11.0.3 + lodash-es: 4.17.21 + + '@chevrotain/gast@11.0.3': + dependencies: + '@chevrotain/types': 11.0.3 + lodash-es: 4.17.21 + + '@chevrotain/regexp-to-ast@11.0.3': {} + + '@chevrotain/types@11.0.3': {} + + '@chevrotain/utils@11.0.3': {} + + '@copilotkit/react-core@1.51.3-next.3(@ag-ui/core@0.0.43)(@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5))(@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(graphql@16.12.0)(micromark-util-types@2.0.2)(micromark@4.0.2)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76)': + dependencies: + '@ag-ui/client': 0.0.43 + '@copilotkit/runtime-client-gql': 1.51.3-next.3(@ag-ui/core@0.0.43)(@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5))(@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(graphql@16.12.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@copilotkit/shared': 1.51.3-next.3(@ag-ui/core@0.0.43) + '@copilotkitnext/core': 1.51.3-next.3 + '@copilotkitnext/react': 1.51.3-next.3(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@scarf/scarf': 1.4.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + streamdown: 1.6.11(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(react@19.2.3) + untruncate-json: 0.0.1 + zod: 3.25.76 + transitivePeerDependencies: + - '@ag-ui/core' + - '@anthropic-ai/sdk' + - '@copilotkitnext/agent' + - '@copilotkitnext/runtime' + - '@langchain/aws' + - '@langchain/community' + - '@langchain/core' + - '@langchain/google-gauth' + - '@langchain/langgraph-sdk' + - '@langchain/openai' + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' + - '@types/mdast' + - '@types/react' + - '@types/react-dom' + - encoding + - graphql + - groq-sdk + - langchain + - micromark + - micromark-util-types + - openai + - supports-color + + '@copilotkit/react-ui@1.51.3-next.3(@ag-ui/core@0.0.43)(@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5))(@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(graphql@16.12.0)(micromark-util-types@2.0.2)(micromark@4.0.2)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76)': + dependencies: + '@copilotkit/react-core': 1.51.3-next.3(@ag-ui/core@0.0.43)(@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5))(@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(graphql@16.12.0)(micromark-util-types@2.0.2)(micromark@4.0.2)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76) + '@copilotkit/runtime-client-gql': 1.51.3-next.3(@ag-ui/core@0.0.43)(@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5))(@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(graphql@16.12.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@copilotkit/shared': 1.51.3-next.3(@ag-ui/core@0.0.43) + '@headlessui/react': 2.2.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-syntax-highlighter: 15.6.6(react@19.2.3) + streamdown: 1.6.11(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(react@19.2.3) + transitivePeerDependencies: + - '@ag-ui/core' + - '@anthropic-ai/sdk' + - '@copilotkitnext/agent' + - '@copilotkitnext/runtime' + - '@langchain/aws' + - '@langchain/community' + - '@langchain/core' + - '@langchain/google-gauth' + - '@langchain/langgraph-sdk' + - '@langchain/openai' + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' + - '@types/mdast' + - '@types/react' + - '@types/react-dom' + - encoding + - graphql + - groq-sdk + - langchain + - micromark + - micromark-util-types + - openai + - react-dom + - supports-color + - zod + + '@copilotkit/runtime-client-gql@1.51.3-next.3(@ag-ui/core@0.0.43)(@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5))(@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(graphql@16.12.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@copilotkit/runtime': 1.51.3-next.3(@copilotkit/shared@1.51.3-next.3(@ag-ui/core@0.0.43))(@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5))(@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@copilotkit/shared': 1.51.3-next.3(@ag-ui/core@0.0.43) + '@urql/core': 5.2.0(graphql@16.12.0) + react: 19.2.3 + untruncate-json: 0.0.1 + urql: 4.2.2(@urql/core@5.2.0(graphql@16.12.0))(react@19.2.3) + transitivePeerDependencies: + - '@ag-ui/core' + - '@anthropic-ai/sdk' + - '@copilotkitnext/agent' + - '@copilotkitnext/runtime' + - '@langchain/aws' + - '@langchain/community' + - '@langchain/core' + - '@langchain/google-gauth' + - '@langchain/langgraph-sdk' + - '@langchain/openai' + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' + - encoding + - graphql + - groq-sdk + - langchain + - openai + - react-dom + + '@copilotkit/runtime@1.51.3-next.3(@copilotkit/shared@1.51.3-next.3(@ag-ui/core@0.0.43))(@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5))(@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@ag-ui/client': 0.0.43 + '@ag-ui/core': 0.0.43 + '@ag-ui/langgraph': 0.0.22(@ag-ui/client@0.0.43)(@ag-ui/core@0.0.43)(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@copilotkit/shared': 1.51.3-next.3(@ag-ui/core@0.0.43) + '@copilotkitnext/agent': 1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5) + '@copilotkitnext/runtime': 1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76)) + '@graphql-yoga/plugin-defer-stream': 3.18.0(graphql-yoga@5.18.0(graphql@16.12.0))(graphql@16.12.0) + '@hono/node-server': 1.19.9(hono@4.11.5) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)) + '@scarf/scarf': 1.4.0 + class-transformer: 0.5.1 + class-validator: 0.14.3 + graphql: 16.12.0 + graphql-scalars: 1.25.0(graphql@16.12.0) + graphql-yoga: 5.18.0(graphql@16.12.0) + hono: 4.11.5 + partial-json: 0.1.7 + pino: 9.14.0 + pino-pretty: 11.3.0 + reflect-metadata: 0.2.2 + rxjs: 7.8.1 + type-graphql: 2.0.0-rc.1(class-validator@0.14.3)(graphql-scalars@1.25.0(graphql@16.12.0))(graphql@16.12.0) + zod: 3.25.76 + optionalDependencies: + '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + openai: 6.16.0(ws@8.19.0)(zod@3.25.76) + transitivePeerDependencies: + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' + - react + - react-dom + + '@copilotkit/shared@1.51.3-next.3(@ag-ui/core@0.0.43)': + dependencies: + '@ag-ui/core': 0.0.43 + '@segment/analytics-node': 2.3.0 + chalk: 4.1.2 + graphql: 16.12.0 + uuid: 10.0.0 + zod: 3.25.76 + transitivePeerDependencies: + - encoding + + '@copilotkitnext/agent@1.51.2(@cfworker/json-schema@4.1.1)(hono@4.11.5)': + dependencies: + '@ag-ui/client': 0.0.42 + '@ai-sdk/anthropic': 2.0.57(zod@3.25.76) + '@ai-sdk/google': 2.0.52(zod@3.25.76) + '@ai-sdk/mcp': 0.0.8(zod@3.25.76) + '@ai-sdk/openai': 2.0.89(zod@3.25.76) + '@modelcontextprotocol/sdk': 1.25.3(@cfworker/json-schema@4.1.1)(hono@4.11.5)(zod@3.25.76) + ai: 5.0.123(zod@3.25.76) + rxjs: 7.8.1 + zod: 3.25.76 + transitivePeerDependencies: + - '@cfworker/json-schema' + - hono + - supports-color + + '@copilotkitnext/core@1.51.3-next.3': + dependencies: + '@ag-ui/client': 0.0.42 + '@copilotkitnext/shared': 1.51.3-next.3 + rxjs: 7.8.1 + zod: 3.25.76 + zod-to-json-schema: 3.25.1(zod@3.25.76) + + '@copilotkitnext/react@1.51.3-next.3(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@ag-ui/client': 0.0.42 + '@ag-ui/core': 0.0.42 + '@copilotkitnext/core': 1.51.3-next.3 + '@copilotkitnext/shared': 1.51.3-next.3 + '@copilotkitnext/web-inspector': 1.51.3-next.3 + '@lit-labs/react': 2.1.3(@types/react@19.2.8) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + class-variance-authority: 0.7.1 + clsx: 2.1.1 + katex: 0.16.27 + lucide-react: 0.525.0(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + streamdown: 1.6.11(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(react@19.2.3) + tailwind-merge: 3.4.0 + ts-deepmerge: 7.0.3 + tw-animate-css: 1.4.0 + use-stick-to-bottom: 1.1.2(react@19.2.3) + zod: 3.25.76 + transitivePeerDependencies: + - '@types/mdast' + - '@types/react' + - '@types/react-dom' + - micromark + - micromark-util-types + - supports-color + + '@copilotkitnext/runtime@1.51.2(openai@6.16.0(ws@8.19.0)(zod@3.25.76))': + dependencies: + '@ag-ui/client': 0.0.42 + '@ag-ui/core': 0.0.42 + '@ag-ui/encoder': 0.0.42 + '@copilotkitnext/shared': 1.51.2 + cors: 2.8.6 + express: 4.22.1 + hono: 4.11.5 + openai: 6.16.0(ws@8.19.0)(zod@3.25.76) + rxjs: 7.8.1 + transitivePeerDependencies: + - supports-color + + '@copilotkitnext/shared@1.51.2': + dependencies: + '@ag-ui/client': 0.0.42 + partial-json: 0.1.7 + uuid: 11.1.0 + + '@copilotkitnext/shared@1.51.3-next.3': + dependencies: + '@ag-ui/client': 0.0.42 + partial-json: 0.1.7 + uuid: 11.1.0 + + '@copilotkitnext/web-inspector@1.51.3-next.3': + dependencies: + '@ag-ui/client': 0.0.42 + '@copilotkitnext/core': 1.51.3-next.3 + lit: 3.3.2 + lucide: 0.525.0 + marked: 12.0.2 + + '@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)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@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) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.24': {} + + '@csstools/css-tokenizer@3.0.4': {} + + '@emnapi/core@1.8.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.8.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@envelop/core@5.5.0': + dependencies: + '@envelop/instrumentation': 1.0.0 + '@envelop/types': 5.2.1 + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + '@envelop/instrumentation@1.0.0': + dependencies: + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + '@envelop/types@5.2.1': + dependencies: + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@exodus/bytes@1.8.0': {} + + '@fastify/busboy@3.2.0': {} + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@floating-ui/react@0.26.28(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@floating-ui/utils': 0.2.10 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + tabbable: 6.4.0 + + '@floating-ui/utils@0.2.10': {} + + '@graphql-tools/executor@1.5.1(graphql@16.12.0)': + dependencies: + '@graphql-tools/utils': 11.0.0(graphql@16.12.0) + '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) + '@repeaterjs/repeater': 3.0.6 + '@whatwg-node/disposablestack': 0.0.6 + '@whatwg-node/promise-helpers': 1.3.2 + graphql: 16.12.0 + tslib: 2.8.1 + + '@graphql-tools/merge@9.1.7(graphql@16.12.0)': + dependencies: + '@graphql-tools/utils': 11.0.0(graphql@16.12.0) + graphql: 16.12.0 + tslib: 2.8.1 + + '@graphql-tools/schema@10.0.31(graphql@16.12.0)': + dependencies: + '@graphql-tools/merge': 9.1.7(graphql@16.12.0) + '@graphql-tools/utils': 11.0.0(graphql@16.12.0) + graphql: 16.12.0 + tslib: 2.8.1 + + '@graphql-tools/utils@10.11.0(graphql@16.12.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) + '@whatwg-node/promise-helpers': 1.3.2 + cross-inspect: 1.0.1 + graphql: 16.12.0 + tslib: 2.8.1 + + '@graphql-tools/utils@11.0.0(graphql@16.12.0)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) + '@whatwg-node/promise-helpers': 1.3.2 + cross-inspect: 1.0.1 + graphql: 16.12.0 + tslib: 2.8.1 + + '@graphql-typed-document-node/core@3.2.0(graphql@16.12.0)': + dependencies: + graphql: 16.12.0 + + '@graphql-yoga/logger@2.0.1': + dependencies: + tslib: 2.8.1 + + '@graphql-yoga/plugin-defer-stream@3.18.0(graphql-yoga@5.18.0(graphql@16.12.0))(graphql@16.12.0)': + dependencies: + '@graphql-tools/utils': 10.11.0(graphql@16.12.0) + graphql: 16.12.0 + graphql-yoga: 5.18.0(graphql@16.12.0) + + '@graphql-yoga/subscription@5.0.5': + dependencies: + '@graphql-yoga/typed-event-target': 3.0.2 + '@repeaterjs/repeater': 3.0.6 + '@whatwg-node/events': 0.1.2 + tslib: 2.8.1 + + '@graphql-yoga/typed-event-target@3.0.2': + dependencies: + '@repeaterjs/repeater': 3.0.6 + tslib: 2.8.1 + + '@headlessui/react@2.2.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@floating-ui/react': 0.26.28(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/focus': 3.21.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/interactions': 3.26.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-virtual': 3.13.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) + + '@hono/node-server@1.19.9(hono@4.11.5)': + dependencies: + hono: 4.11.5 + + '@iconify/types@2.0.0': {} + + '@iconify/utils@3.1.0': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@iconify/types': 2.0.0 + mlly: 1.8.0 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76))': + dependencies: + '@cfworker/json-schema': 4.1.1 + ansi-styles: 5.2.0 + camelcase: 6.3.0 + decamelize: 1.2.0 + js-tiktoken: 1.0.21 + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)) + mustache: 4.2.0 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 10.0.0 + zod: 3.25.76 + zod-to-json-schema: 3.25.1(zod@3.25.76) + transitivePeerDependencies: + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' + - openai + + '@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@types/json-schema': 7.0.15 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 9.0.1 + optionalDependencies: + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@lit-labs/react@2.1.3(@types/react@19.2.8)': + dependencies: + '@lit/react': 1.0.8(@types/react@19.2.8) + transitivePeerDependencies: + - '@types/react' + + '@lit-labs/ssr-dom-shim@1.5.1': {} + + '@lit/react@1.0.8(@types/react@19.2.8)': + dependencies: + '@types/react': 19.2.8 + + '@lit/reactive-element@2.1.2': + dependencies: + '@lit-labs/ssr-dom-shim': 1.5.1 + + '@lukeed/csprng@1.1.0': {} + + '@lukeed/uuid@2.0.1': + dependencies: + '@lukeed/csprng': 1.1.0 + + '@mermaid-js/parser@0.6.3': + dependencies: + langium: 3.3.1 + + '@modelcontextprotocol/sdk@1.25.3(@cfworker/json-schema@4.1.1)(hono@4.11.5)(zod@3.25.76)': + dependencies: + '@hono/node-server': 1.19.9(hono@4.11.5) + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 7.5.1(express@5.2.1) + jose: 6.1.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.1(zod@3.25.76) + optionalDependencies: + '@cfworker/json-schema': 4.1.1 + transitivePeerDependencies: + - hono + - supports-color + + '@napi-rs/wasm-runtime@1.1.1': + dependencies: + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@oozcitak/dom@2.0.2': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/url': 3.0.0 + '@oozcitak/util': 10.0.0 + + '@oozcitak/infra@2.0.2': + dependencies: + '@oozcitak/util': 10.0.0 + + '@oozcitak/url@3.0.0': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + + '@oozcitak/util@10.0.0': {} + + '@opentelemetry/api@1.9.0': {} + + '@oxc-minify/binding-android-arm64@0.96.0': + optional: true + + '@oxc-minify/binding-darwin-arm64@0.96.0': + optional: true + + '@oxc-minify/binding-darwin-x64@0.96.0': + optional: true + + '@oxc-minify/binding-freebsd-x64@0.96.0': + optional: true + + '@oxc-minify/binding-linux-arm-gnueabihf@0.96.0': + optional: true + + '@oxc-minify/binding-linux-arm-musleabihf@0.96.0': + optional: true + + '@oxc-minify/binding-linux-arm64-gnu@0.96.0': + optional: true + + '@oxc-minify/binding-linux-arm64-musl@0.96.0': + optional: true + + '@oxc-minify/binding-linux-riscv64-gnu@0.96.0': + optional: true + + '@oxc-minify/binding-linux-s390x-gnu@0.96.0': + optional: true + + '@oxc-minify/binding-linux-x64-gnu@0.96.0': + optional: true + + '@oxc-minify/binding-linux-x64-musl@0.96.0': + optional: true + + '@oxc-minify/binding-wasm32-wasi@0.96.0': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + optional: true + + '@oxc-minify/binding-win32-arm64-msvc@0.96.0': + optional: true + + '@oxc-minify/binding-win32-x64-msvc@0.96.0': + optional: true + + '@oxc-transform/binding-android-arm64@0.96.0': + optional: true + + '@oxc-transform/binding-darwin-arm64@0.96.0': + optional: true + + '@oxc-transform/binding-darwin-x64@0.96.0': + optional: true + + '@oxc-transform/binding-freebsd-x64@0.96.0': + optional: true + + '@oxc-transform/binding-linux-arm-gnueabihf@0.96.0': + optional: true + + '@oxc-transform/binding-linux-arm-musleabihf@0.96.0': + optional: true + + '@oxc-transform/binding-linux-arm64-gnu@0.96.0': + optional: true + + '@oxc-transform/binding-linux-arm64-musl@0.96.0': + optional: true + + '@oxc-transform/binding-linux-riscv64-gnu@0.96.0': + optional: true + + '@oxc-transform/binding-linux-s390x-gnu@0.96.0': + optional: true + + '@oxc-transform/binding-linux-x64-gnu@0.96.0': + optional: true + + '@oxc-transform/binding-linux-x64-musl@0.96.0': + optional: true + + '@oxc-transform/binding-wasm32-wasi@0.96.0': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + optional: true + + '@oxc-transform/binding-win32-arm64-msvc@0.96.0': + optional: true + + '@oxc-transform/binding-win32-x64-msvc@0.96.0': + optional: true + + '@pinojs/redact@0.4.0': {} + + '@protobuf-ts/protoc@2.11.1': {} + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-context@1.1.2(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-direction@1.1.1(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-id@1.1.1(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + aria-hidden: 1.2.6 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.8)(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/rect': 1.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-slot@1.2.3(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-slot@1.2.4(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.8)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.8)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.8)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.8 + + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@radix-ui/rect@1.1.1': {} + + '@react-aria/focus@3.21.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/interactions': 3.26.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-aria/utils': 3.32.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-types/shared': 3.32.1(react@19.2.3) + '@swc/helpers': 0.5.18 + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/interactions@3.26.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-aria/utils': 3.32.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@react-stately/flags': 3.1.2 + '@react-types/shared': 3.32.1(react@19.2.3) + '@swc/helpers': 0.5.18 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-aria/ssr@3.9.10(react@19.2.3)': + dependencies: + '@swc/helpers': 0.5.18 + react: 19.2.3 + + '@react-aria/utils@3.32.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@react-stately/flags': 3.1.2 + '@react-stately/utils': 3.11.0(react@19.2.3) + '@react-types/shared': 3.32.1(react@19.2.3) + '@swc/helpers': 0.5.18 + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@react-stately/flags@3.1.2': + dependencies: + '@swc/helpers': 0.5.18 + + '@react-stately/utils@3.11.0(react@19.2.3)': + dependencies: + '@swc/helpers': 0.5.18 + react: 19.2.3 + + '@react-types/shared@3.32.1(react@19.2.3)': + dependencies: + react: 19.2.3 + + '@repeaterjs/repeater@3.0.6': {} + + '@rolldown/pluginutils@1.0.0-beta.40': {} + + '@rolldown/pluginutils@1.0.0-beta.53': {} + + '@rollup/rollup-android-arm-eabi@4.55.1': + optional: true + + '@rollup/rollup-android-arm64@4.55.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.55.1': + optional: true + + '@rollup/rollup-darwin-x64@4.55.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.55.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.55.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.55.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.55.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.55.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.55.1': + optional: true + + '@scarf/scarf@1.4.0': {} + + '@segment/analytics-core@1.8.2': + dependencies: + '@lukeed/uuid': 2.0.1 + '@segment/analytics-generic-utils': 1.2.0 + dset: 3.1.4 + tslib: 2.8.1 + + '@segment/analytics-generic-utils@1.2.0': + dependencies: + tslib: 2.8.1 + + '@segment/analytics-node@2.3.0': + dependencies: + '@lukeed/uuid': 2.0.1 + '@segment/analytics-core': 1.8.2 + '@segment/analytics-generic-utils': 1.2.0 + buffer: 6.0.3 + jose: 5.10.0 + node-fetch: 2.7.0 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@shikijs/core@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.4 + + '@shikijs/engine-oniguruma@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + + '@shikijs/themes@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + + '@shikijs/types@3.21.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@solid-primitives/event-listener@2.4.3(solid-js@1.9.10)': + dependencies: + '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) + solid-js: 1.9.10 + + '@solid-primitives/keyboard@1.3.3(solid-js@1.9.10)': + dependencies: + '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10) + '@solid-primitives/rootless': 1.5.2(solid-js@1.9.10) + '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) + solid-js: 1.9.10 + + '@solid-primitives/resize-observer@2.1.3(solid-js@1.9.10)': + dependencies: + '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10) + '@solid-primitives/rootless': 1.5.2(solid-js@1.9.10) + '@solid-primitives/static-store': 0.1.2(solid-js@1.9.10) + '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) + solid-js: 1.9.10 + + '@solid-primitives/rootless@1.5.2(solid-js@1.9.10)': + dependencies: + '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) + solid-js: 1.9.10 + + '@solid-primitives/static-store@0.1.2(solid-js@1.9.10)': + dependencies: + '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) + solid-js: 1.9.10 + + '@solid-primitives/utils@6.3.2(solid-js@1.9.10)': + dependencies: + solid-js: 1.9.10 + + '@standard-schema/spec@1.1.0': {} + + '@swc/helpers@0.5.18': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.1.18': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.4 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.18 + + '@tailwindcss/oxide-android-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide@4.1.18': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-x64': 4.1.18 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 + + '@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@tailwindcss/node': 4.1.18 + '@tailwindcss/oxide': 4.1.18 + tailwindcss: 4.1.18 + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + + '@tanstack/ai-client@0.2.2': + dependencies: + '@tanstack/ai': 0.2.2 + + '@tanstack/ai-openai@0.2.1(@tanstack/ai@0.2.2)(ws@8.19.0)(zod@3.25.76)': + dependencies: + '@tanstack/ai': 0.2.2 + openai: 6.16.0(ws@8.19.0)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - ws + + '@tanstack/ai-react@0.2.2(@tanstack/ai@0.2.2)(@types/react@19.2.8)(react@19.2.3)': + dependencies: + '@tanstack/ai': 0.2.2 + '@tanstack/ai-client': 0.2.2 + '@types/react': 19.2.8 + react: 19.2.3 + + '@tanstack/ai@0.2.2': + dependencies: + '@tanstack/devtools-event-client': 0.4.0 + partial-json: 0.1.7 + + '@tanstack/devtools-client@0.0.3': + dependencies: + '@tanstack/devtools-event-client': 0.3.5 + + '@tanstack/devtools-client@0.0.5': + dependencies: + '@tanstack/devtools-event-client': 0.4.0 + + '@tanstack/devtools-event-bus@0.3.3': + dependencies: + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@tanstack/devtools-event-client@0.3.5': {} + + '@tanstack/devtools-event-client@0.4.0': {} + + '@tanstack/devtools-ui@0.4.4(csstype@3.2.3)(solid-js@1.9.10)': + dependencies: + clsx: 2.1.1 + goober: 2.1.18(csstype@3.2.3) + solid-js: 1.9.10 + transitivePeerDependencies: + - csstype + + '@tanstack/devtools-vite@0.3.12(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@tanstack/devtools-client': 0.0.5 + '@tanstack/devtools-event-bus': 0.3.3 + chalk: 5.6.2 + launch-editor: 2.12.0 + picomatch: 4.0.3 + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@tanstack/devtools@0.7.0(csstype@3.2.3)(solid-js@1.9.10)': + dependencies: + '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10) + '@solid-primitives/keyboard': 1.3.3(solid-js@1.9.10) + '@solid-primitives/resize-observer': 2.1.3(solid-js@1.9.10) + '@tanstack/devtools-client': 0.0.3 + '@tanstack/devtools-event-bus': 0.3.3 + '@tanstack/devtools-ui': 0.4.4(csstype@3.2.3)(solid-js@1.9.10) + clsx: 2.1.1 + goober: 2.1.18(csstype@3.2.3) + solid-js: 1.9.10 + transitivePeerDependencies: + - bufferutil + - csstype + - utf-8-validate + + '@tanstack/history@1.145.7': {} + + '@tanstack/query-core@5.90.16': {} + + '@tanstack/react-devtools@0.7.11(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.10)': + dependencies: + '@tanstack/devtools': 0.7.0(csstype@3.2.3)(solid-js@1.9.10) + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + transitivePeerDependencies: + - bufferutil + - csstype + - solid-js + - utf-8-validate + + '@tanstack/react-query@5.90.16(react@19.2.3)': + dependencies: + '@tanstack/query-core': 5.90.16 + react: 19.2.3 + + '@tanstack/react-router-devtools@1.147.1(@tanstack/react-router@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.147.1)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.10)': + dependencies: + '@tanstack/react-router': 1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-devtools-core': 1.147.1(@tanstack/router-core@1.147.1)(csstype@3.2.3)(solid-js@1.9.10) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@tanstack/router-core': 1.147.1 + transitivePeerDependencies: + - csstype + - solid-js + + '@tanstack/react-router-ssr-query@1.147.1(@tanstack/query-core@5.90.16)(@tanstack/react-query@5.90.16(react@19.2.3))(@tanstack/react-router@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.147.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tanstack/query-core': 5.90.16 + '@tanstack/react-query': 5.90.16(react@19.2.3) + '@tanstack/react-router': 1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-ssr-query-core': 1.147.1(@tanstack/query-core@5.90.16)(@tanstack/router-core@1.147.1) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + transitivePeerDependencies: + - '@tanstack/router-core' + + '@tanstack/react-router@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tanstack/history': 1.145.7 + '@tanstack/react-store': 0.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-core': 1.147.1 + isbot: 5.1.32 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + '@tanstack/react-start-client@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tanstack/react-router': 1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-core': 1.147.1 + '@tanstack/start-client-core': 1.147.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + '@tanstack/react-start-server@1.147.1(crossws@0.4.1(srvx@0.9.8))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tanstack/history': 1.145.7 + '@tanstack/react-router': 1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-core': 1.147.1 + '@tanstack/start-client-core': 1.147.1 + '@tanstack/start-server-core': 1.147.1(crossws@0.4.1(srvx@0.9.8)) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + transitivePeerDependencies: + - crossws + + '@tanstack/react-start@1.147.1(crossws@0.4.1(srvx@0.9.8))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@tanstack/react-router': 1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-start-client': 1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/react-start-server': 1.147.1(crossws@0.4.1(srvx@0.9.8))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@tanstack/router-utils': 1.143.11 + '@tanstack/start-client-core': 1.147.1 + '@tanstack/start-plugin-core': 1.147.1(@tanstack/react-router@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(crossws@0.4.1(srvx@0.9.8))(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@tanstack/start-server-core': 1.147.1(crossws@0.4.1(srvx@0.9.8)) + pathe: 2.0.3 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + transitivePeerDependencies: + - '@rsbuild/core' + - crossws + - supports-color + - vite-plugin-solid + - webpack + + '@tanstack/react-store@0.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tanstack/store': 0.8.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + use-sync-external-store: 1.6.0(react@19.2.3) + + '@tanstack/react-virtual@3.13.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@tanstack/virtual-core': 3.13.18 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@tanstack/router-core@1.147.1': + dependencies: + '@tanstack/history': 1.145.7 + '@tanstack/store': 0.8.0 + cookie-es: 2.0.0 + seroval: 1.4.2 + seroval-plugins: 1.4.2(seroval@1.4.2) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + '@tanstack/router-devtools-core@1.147.1(@tanstack/router-core@1.147.1)(csstype@3.2.3)(solid-js@1.9.10)': + dependencies: + '@tanstack/router-core': 1.147.1 + clsx: 2.1.1 + goober: 2.1.18(csstype@3.2.3) + solid-js: 1.9.10 + tiny-invariant: 1.3.3 + optionalDependencies: + csstype: 3.2.3 + + '@tanstack/router-generator@1.147.1': + dependencies: + '@tanstack/router-core': 1.147.1 + '@tanstack/router-utils': 1.143.11 + '@tanstack/virtual-file-routes': 1.145.4 + prettier: 3.7.4 + recast: 0.23.11 + source-map: 0.7.6 + tsx: 4.21.0 + zod: 3.25.76 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.147.1(@tanstack/react-router@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@tanstack/router-core': 1.147.1 + '@tanstack/router-generator': 1.147.1 + '@tanstack/router-utils': 1.143.11 + '@tanstack/virtual-file-routes': 1.145.4 + babel-dead-code-elimination: 1.0.11 + chokidar: 3.6.0 + unplugin: 2.3.11 + zod: 3.25.76 + optionalDependencies: + '@tanstack/react-router': 1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + transitivePeerDependencies: + - supports-color + + '@tanstack/router-ssr-query-core@1.147.1(@tanstack/query-core@5.90.16)(@tanstack/router-core@1.147.1)': + dependencies: + '@tanstack/query-core': 5.90.16 + '@tanstack/router-core': 1.147.1 + + '@tanstack/router-utils@1.143.11': + dependencies: + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + ansis: 4.2.0 + diff: 8.0.2 + pathe: 2.0.3 + tinyglobby: 0.2.15 + transitivePeerDependencies: + - supports-color + + '@tanstack/start-client-core@1.147.1': + dependencies: + '@tanstack/router-core': 1.147.1 + '@tanstack/start-fn-stubs': 1.143.8 + '@tanstack/start-storage-context': 1.147.1 + seroval: 1.4.2 + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + '@tanstack/start-fn-stubs@1.143.8': {} + + '@tanstack/start-plugin-core@1.147.1(@tanstack/react-router@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(crossws@0.4.1(srvx@0.9.8))(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.28.5 + '@babel/types': 7.28.5 + '@rolldown/pluginutils': 1.0.0-beta.40 + '@tanstack/router-core': 1.147.1 + '@tanstack/router-generator': 1.147.1 + '@tanstack/router-plugin': 1.147.1(@tanstack/react-router@1.147.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@tanstack/router-utils': 1.143.11 + '@tanstack/start-client-core': 1.147.1 + '@tanstack/start-server-core': 1.147.1(crossws@0.4.1(srvx@0.9.8)) + babel-dead-code-elimination: 1.0.11 + cheerio: 1.1.2 + exsolve: 1.0.8 + pathe: 2.0.3 + srvx: 0.10.0 + tinyglobby: 0.2.15 + ufo: 1.6.2 + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vitefu: 1.1.1(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + xmlbuilder2: 4.0.3 + zod: 3.25.76 + transitivePeerDependencies: + - '@rsbuild/core' + - '@tanstack/react-router' + - crossws + - supports-color + - vite-plugin-solid + - webpack + + '@tanstack/start-server-core@1.147.1(crossws@0.4.1(srvx@0.9.8))': + dependencies: + '@tanstack/history': 1.145.7 + '@tanstack/router-core': 1.147.1 + '@tanstack/start-client-core': 1.147.1 + '@tanstack/start-storage-context': 1.147.1 + h3-v2: h3@2.0.1-rc.7(crossws@0.4.1(srvx@0.9.8)) + seroval: 1.4.2 + tiny-invariant: 1.3.3 + transitivePeerDependencies: + - crossws + + '@tanstack/start-storage-context@1.147.1': + dependencies: + '@tanstack/router-core': 1.147.1 + + '@tanstack/store@0.8.0': {} + + '@tanstack/virtual-core@3.13.18': {} + + '@tanstack/virtual-file-routes@1.145.4': {} + + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/runtime': 7.28.4 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/react@16.3.1(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.4 + '@testing-library/dom': 10.4.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.5 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.5 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/deep-eql@4.0.2': {} + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + + '@types/estree@1.0.8': {} + + '@types/geojson@7946.0.16': {} + + '@types/hast@2.3.10': + dependencies: + '@types/unist': 2.0.11 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/json-schema@7.0.15': {} + + '@types/katex@0.16.8': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + + '@types/node@22.19.5': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.3(@types/react@19.2.8)': + dependencies: + '@types/react': 19.2.8 + + '@types/react@19.2.8': + dependencies: + csstype: 3.2.3 + + '@types/retry@0.12.0': {} + + '@types/semver@7.7.1': {} + + '@types/trusted-types@2.0.7': {} + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/uuid@10.0.0': {} + + '@types/validator@13.15.10': {} + + '@ungap/structured-clone@1.3.0': {} + + '@urql/core@5.2.0(graphql@16.12.0)': + dependencies: + '@0no-co/graphql.web': 1.2.0(graphql@16.12.0) + wonka: 6.3.5 + transitivePeerDependencies: + - graphql + + '@vercel/oidc@3.1.0': {} + + '@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.53 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@whatwg-node/disposablestack@0.0.6': + dependencies: + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + '@whatwg-node/events@0.1.2': + dependencies: + tslib: 2.8.1 + + '@whatwg-node/fetch@0.10.13': + dependencies: + '@whatwg-node/node-fetch': 0.8.5 + urlpattern-polyfill: 10.1.0 + + '@whatwg-node/node-fetch@0.8.5': + dependencies: + '@fastify/busboy': 3.2.0 + '@whatwg-node/disposablestack': 0.0.6 + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + '@whatwg-node/promise-helpers@1.3.2': + dependencies: + tslib: 2.8.1 + + '@whatwg-node/server@0.10.18': + dependencies: + '@envelop/instrumentation': 1.0.0 + '@whatwg-node/disposablestack': 0.0.6 + '@whatwg-node/fetch': 0.10.13 + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + acorn@8.15.0: {} + + agent-base@7.1.4: {} + + ai@5.0.123(zod@3.25.76): + dependencies: + '@ai-sdk/gateway': 2.0.29(zod@3.25.76) + '@ai-sdk/provider': 2.0.1 + '@ai-sdk/provider-utils': 3.0.20(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + zod: 3.25.76 + + ajv-formats@3.0.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansis@4.2.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + array-flatten@1.1.1: {} + + assertion-error@2.0.1: {} + + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + + atomic-sleep@1.0.0: {} + + babel-dead-code-elimination@1.0.11: + dependencies: + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + bail@2.0.2: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.9.14: {} + + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + + binary-extensions@2.3.0: {} + + body-parser@1.20.4: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.14.1 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.14.1 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + boolbase@1.0.0: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.14 + caniuse-lite: 1.0.30001763 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bytes@3.1.2: {} + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001763: {} + + ccount@2.0.1: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@1.1.4: {} + + character-entities-legacy@3.0.0: {} + + character-entities@1.2.4: {} + + character-entities@2.0.2: {} + + character-reference-invalid@1.1.4: {} + + character-reference-invalid@2.0.1: {} + + check-error@2.1.3: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.1.2: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.1 + htmlparser2: 10.0.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 7.18.2 + whatwg-mimetype: 4.0.0 + + chevrotain-allstar@0.3.1(chevrotain@11.0.3): + dependencies: + chevrotain: 11.0.3 + lodash-es: 4.17.23 + + chevrotain@11.0.3: + dependencies: + '@chevrotain/cst-dts-gen': 11.0.3 + '@chevrotain/gast': 11.0.3 + '@chevrotain/regexp-to-ast': 11.0.3 + '@chevrotain/types': 11.0.3 + '@chevrotain/utils': 11.0.3 + lodash-es: 4.17.21 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + class-transformer@0.5.1: {} + + class-validator@0.14.3: + dependencies: + '@types/validator': 13.15.10 + libphonenumber-js: 1.12.35 + validator: 13.15.26 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + comma-separated-tokens@1.0.8: {} + + comma-separated-tokens@2.0.3: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + compare-versions@6.1.1: {} + + confbox@0.1.8: {} + + consola@3.4.2: {} + + console-table-printer@2.15.0: + dependencies: + simple-wcswidth: 1.1.2 + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-disposition@1.0.1: {} + + content-type@1.0.5: {} + + convert-source-map@2.0.0: {} + + cookie-es@2.0.0: {} + + cookie-signature@1.0.7: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + + cross-inspect@1.0.1: + dependencies: + tslib: 2.8.1 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crossws@0.4.1(srvx@0.9.8): + optionalDependencies: + srvx: 0.9.8 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + css-what@6.2.2: {} + + cssstyle@5.3.7: + dependencies: + '@asamuzakjp/css-color': 4.1.1 + '@csstools/css-syntax-patches-for-csstree': 1.0.24 + css-tree: 3.1.0 + lru-cache: 11.2.4 + + csstype@3.2.3: {} + + cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.1): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.33.1 + + cytoscape-fcose@2.2.0(cytoscape@3.33.1): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.33.1 + + cytoscape@3.33.1: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.0.1 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.13: + dependencies: + d3: 7.9.0 + lodash-es: 4.17.23 + + data-urls@6.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + + dateformat@4.6.3: {} + + dayjs@1.11.19: {} + + db0@0.3.4: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize@1.2.0: {} + + decimal.js@10.6.0: {} + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + deep-eql@5.0.2: {} + + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + + depd@2.0.0: {} + + dequal@2.0.3: {} + + destroy@1.2.0: {} + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diff@8.0.2: {} + + dom-accessibility-api@0.5.16: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + dompurify@3.3.1: + optionalDependencies: + '@types/trusted-types': 2.0.7 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dset@3.1.4: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.267: {} + + encodeurl@2.0.0: {} + + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + enhanced-resolve@5.18.4: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + entities@4.5.0: {} + + entities@6.0.1: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@5.0.0: {} + + esprima@4.0.1: {} + + estree-util-is-identifier-name@3.0.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + etag@1.8.1: {} + + event-target-shim@5.0.1: {} + + eventemitter3@4.0.7: {} + + events@3.3.0: {} + + eventsource-parser@3.0.6: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + + expect-type@1.3.0: {} + + express-rate-limit@7.5.1(express@5.2.1): + dependencies: + express: 5.2.1 + + express@4.22.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.4 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.14.1 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.0.1 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.1 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + exsolve@1.0.8: {} + + extend@3.0.2: {} + + fast-copy@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fast-json-patch@3.1.1: {} + + fast-safe-stringify@2.1.1: {} + + fast-uri@3.1.0: {} + + fault@1.0.4: + dependencies: + format: 0.2.2 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.3.2: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + format@0.2.2: {} + + forwarded@0.2.0: {} + + fresh@0.5.2: {} + + fresh@2.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-east-asian-width@1.4.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + globrex@0.1.2: {} + + goober@2.1.18(csstype@3.2.3): + dependencies: + csstype: 3.2.3 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphql-query-complexity@0.12.0(graphql@16.12.0): + dependencies: + graphql: 16.12.0 + lodash.get: 4.4.2 + + graphql-scalars@1.25.0(graphql@16.12.0): + dependencies: + graphql: 16.12.0 + tslib: 2.8.1 + + graphql-yoga@5.18.0(graphql@16.12.0): + dependencies: + '@envelop/core': 5.5.0 + '@envelop/instrumentation': 1.0.0 + '@graphql-tools/executor': 1.5.1(graphql@16.12.0) + '@graphql-tools/schema': 10.0.31(graphql@16.12.0) + '@graphql-tools/utils': 10.11.0(graphql@16.12.0) + '@graphql-yoga/logger': 2.0.1 + '@graphql-yoga/subscription': 5.0.5 + '@whatwg-node/fetch': 0.10.13 + '@whatwg-node/promise-helpers': 1.3.2 + '@whatwg-node/server': 0.10.18 + graphql: 16.12.0 + lru-cache: 10.4.3 + tslib: 2.8.1 + + graphql@16.12.0: {} + + h3@2.0.1-rc.5(crossws@0.4.1(srvx@0.9.8)): + dependencies: + rou3: 0.7.12 + srvx: 0.9.8 + optionalDependencies: + crossws: 0.4.1(srvx@0.9.8) + + h3@2.0.1-rc.7(crossws@0.4.1(srvx@0.9.8)): + dependencies: + rou3: 0.7.12 + srvx: 0.10.0 + optionalDependencies: + crossws: 0.4.1(srvx@0.9.8) + + hachure-fill@0.5.2: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-from-dom@5.0.1: + dependencies: + '@types/hast': 3.0.4 + hastscript: 9.0.1 + web-namespaces: 2.0.1 + + hast-util-from-html-isomorphic@2.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-dom: 5.0.1 + hast-util-from-html: 2.0.3 + unist-util-remove-position: 5.0.0 + + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.3 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-parse-selector@2.2.5: {} + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.1 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-sanitize@5.0.2: + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.3.0 + unist-util-position: 5.0.0 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast@1.0.0: {} + + hastscript@6.0.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + + help-me@5.0.0: {} + + highlight.js@10.7.3: {} + + highlightjs-vue@1.0.0: {} + + hono@4.11.5: {} + + html-encoding-sniffer@6.0.0: + dependencies: + '@exodus/bytes': 1.8.0 + transitivePeerDependencies: + - '@exodus/crypto' + + html-url-attributes@3.0.1: {} + + html-void-elements@3.0.0: {} + + htmlparser2@10.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 6.0.1 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + inherits@2.0.4: {} + + inline-style-parser@0.2.7: {} + + internmap@1.0.1: {} + + internmap@2.0.3: {} + + ipaddr.js@1.9.1: {} + + is-alphabetical@1.0.4: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@1.0.4: + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-decimal@1.0.4: {} + + is-decimal@2.0.1: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@1.0.4: {} + + is-hexadecimal@2.0.1: {} + + is-number@7.0.0: {} + + is-plain-obj@4.1.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-promise@4.0.0: {} + + isbot@5.1.32: {} + + isexe@2.0.0: {} + + jiti@2.6.1: {} + + jose@5.10.0: {} + + jose@6.1.3: {} + + joycon@3.1.1: {} + + js-tiktoken@1.0.21: + dependencies: + base64-js: 1.5.1 + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsdom@27.4.0: + dependencies: + '@acemir/cssom': 0.9.30 + '@asamuzakjp/dom-selector': 6.7.6 + '@exodus/bytes': 1.8.0 + cssstyle: 5.3.7 + data-urls: 6.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + parse5: 8.0.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + ws: 8.19.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@exodus/crypto' + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-schema-traverse@1.0.0: {} + + json-schema-typed@8.0.2: {} + + json-schema@0.4.0: {} + + json5@2.2.3: {} + + katex@0.16.27: + dependencies: + commander: 8.3.0 + + khroma@2.1.0: {} + + langium@3.3.1: + dependencies: + chevrotain: 11.0.3 + chevrotain-allstar: 0.3.1(chevrotain@11.0.3) + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.0.8 + + langsmith@0.3.87(@opentelemetry/api@1.9.0)(openai@6.16.0(ws@8.19.0)(zod@3.25.76)): + dependencies: + '@types/uuid': 10.0.0 + chalk: 4.1.2 + console-table-printer: 2.15.0 + p-queue: 6.6.2 + semver: 7.7.3 + uuid: 10.0.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + openai: 6.16.0(ws@8.19.0)(zod@3.25.76) + + launch-editor@2.12.0: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.8.3 + + layout-base@1.0.2: {} + + layout-base@2.0.1: {} + + libphonenumber-js@1.12.35: {} + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + lit-element@4.2.2: + dependencies: + '@lit-labs/ssr-dom-shim': 1.5.1 + '@lit/reactive-element': 2.1.2 + lit-html: 3.3.2 + + lit-html@3.3.2: + dependencies: + '@types/trusted-types': 2.0.7 + + lit@3.3.2: + dependencies: + '@lit/reactive-element': 2.1.2 + lit-element: 4.2.2 + lit-html: 3.3.2 + + lodash-es@4.17.21: {} + + lodash-es@4.17.23: {} + + lodash.get@4.4.2: {} + + longest-streak@3.1.0: {} + + loupe@3.2.1: {} + + lowlight@1.20.0: + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + + lru-cache@10.4.3: {} + + lru-cache@11.2.4: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@0.525.0(react@19.2.3): + dependencies: + react: 19.2.3 + + lucide-react@0.542.0(react@19.2.3): + dependencies: + react: 19.2.3 + + lucide-react@0.561.0(react@19.2.3): + dependencies: + react: 19.2.3 + + lucide@0.525.0: {} + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + markdown-table@3.0.4: {} + + marked@12.0.2: {} + + marked@16.4.2: {} + + math-intrinsics@1.1.0: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-math@3.0.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + longest-streak: 3.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + unist-util-remove-position: 5.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.12.2: {} + + media-typer@0.3.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@1.0.3: {} + + merge-descriptors@2.0.0: {} + + mermaid@11.12.2: + dependencies: + '@braintree/sanitize-url': 7.1.1 + '@iconify/utils': 3.1.0 + '@mermaid-js/parser': 0.6.3 + '@types/d3': 7.4.3 + cytoscape: 3.33.1 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.1) + cytoscape-fcose: 2.2.0(cytoscape@3.33.1) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.13 + dayjs: 1.11.19 + dompurify: 3.3.1 + katex: 0.16.27 + khroma: 2.1.0 + lodash-es: 4.17.23 + marked: 16.4.2 + roughjs: 4.6.6 + stylis: 4.3.6 + ts-dedent: 2.2.0 + uuid: 11.1.0 + + methods@1.1.2: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-cjk-friendly-gfm-strikethrough@1.2.3(micromark-util-types@2.0.2)(micromark@4.0.2): + dependencies: + devlop: 1.1.0 + get-east-asian-width: 1.4.0 + micromark: 4.0.2 + micromark-extension-cjk-friendly-util: 2.1.1(micromark-util-types@2.0.2) + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + optionalDependencies: + micromark-util-types: 2.0.2 + + micromark-extension-cjk-friendly-util@2.1.1(micromark-util-types@2.0.2): + dependencies: + get-east-asian-width: 1.4.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + optionalDependencies: + micromark-util-types: 2.0.2 + + micromark-extension-cjk-friendly@1.2.3(micromark-util-types@2.0.2)(micromark@4.0.2): + dependencies: + devlop: 1.1.0 + micromark: 4.0.2 + micromark-extension-cjk-friendly-util: 2.1.1(micromark-util-types@2.0.2) + micromark-util-chunked: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + optionalDependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-math@3.1.0: + dependencies: + '@types/katex': 0.16.8 + devlop: 1.1.0 + katex: 0.16.27 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mime@1.6.0: {} + + minimist@1.2.8: {} + + mlly@1.8.0: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.2 + + ms@2.0.0: {} + + ms@2.1.3: {} + + mustache@4.2.0: {} + + nanoid@3.3.11: {} + + negotiator@0.6.3: {} + + negotiator@1.0.0: {} + + nf3@0.1.12: {} + + nitro@3.0.1-alpha.1(lru-cache@11.2.4)(rollup@4.55.1)(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)): + dependencies: + consola: 3.4.2 + crossws: 0.4.1(srvx@0.9.8) + db0: 0.3.4 + h3: 2.0.1-rc.5(crossws@0.4.1(srvx@0.9.8)) + jiti: 2.6.1 + nf3: 0.1.12 + ofetch: 2.0.0-alpha.3 + ohash: 2.0.11 + oxc-minify: 0.96.0 + oxc-transform: 0.96.0 + srvx: 0.9.8 + undici: 7.18.2 + unenv: 2.0.0-rc.24 + unstorage: 2.0.0-alpha.5(db0@0.3.4)(lru-cache@11.2.4)(ofetch@2.0.0-alpha.3) + optionalDependencies: + rollup: 4.55.1 + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - better-sqlite3 + - chokidar + - drizzle-orm + - idb-keyval + - ioredis + - lru-cache + - mongodb + - mysql2 + - sqlite3 + - uploadthing + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-releases@2.0.27: {} + + normalize-path@3.0.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + ofetch@2.0.0-alpha.3: {} + + ohash@2.0.11: {} + + on-exit-leak-free@2.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.4: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.1.0 + regex-recursion: 6.0.2 + + openai@6.16.0(ws@8.19.0)(zod@3.25.76): + optionalDependencies: + ws: 8.19.0 + zod: 3.25.76 + + oxc-minify@0.96.0: + optionalDependencies: + '@oxc-minify/binding-android-arm64': 0.96.0 + '@oxc-minify/binding-darwin-arm64': 0.96.0 + '@oxc-minify/binding-darwin-x64': 0.96.0 + '@oxc-minify/binding-freebsd-x64': 0.96.0 + '@oxc-minify/binding-linux-arm-gnueabihf': 0.96.0 + '@oxc-minify/binding-linux-arm-musleabihf': 0.96.0 + '@oxc-minify/binding-linux-arm64-gnu': 0.96.0 + '@oxc-minify/binding-linux-arm64-musl': 0.96.0 + '@oxc-minify/binding-linux-riscv64-gnu': 0.96.0 + '@oxc-minify/binding-linux-s390x-gnu': 0.96.0 + '@oxc-minify/binding-linux-x64-gnu': 0.96.0 + '@oxc-minify/binding-linux-x64-musl': 0.96.0 + '@oxc-minify/binding-wasm32-wasi': 0.96.0 + '@oxc-minify/binding-win32-arm64-msvc': 0.96.0 + '@oxc-minify/binding-win32-x64-msvc': 0.96.0 + + oxc-transform@0.96.0: + optionalDependencies: + '@oxc-transform/binding-android-arm64': 0.96.0 + '@oxc-transform/binding-darwin-arm64': 0.96.0 + '@oxc-transform/binding-darwin-x64': 0.96.0 + '@oxc-transform/binding-freebsd-x64': 0.96.0 + '@oxc-transform/binding-linux-arm-gnueabihf': 0.96.0 + '@oxc-transform/binding-linux-arm-musleabihf': 0.96.0 + '@oxc-transform/binding-linux-arm64-gnu': 0.96.0 + '@oxc-transform/binding-linux-arm64-musl': 0.96.0 + '@oxc-transform/binding-linux-riscv64-gnu': 0.96.0 + '@oxc-transform/binding-linux-s390x-gnu': 0.96.0 + '@oxc-transform/binding-linux-x64-gnu': 0.96.0 + '@oxc-transform/binding-linux-x64-musl': 0.96.0 + '@oxc-transform/binding-wasm32-wasi': 0.96.0 + '@oxc-transform/binding-win32-arm64-msvc': 0.96.0 + '@oxc-transform/binding-win32-x64-msvc': 0.96.0 + + p-finally@1.0.0: {} + + p-queue@6.6.2: + dependencies: + eventemitter3: 4.0.7 + p-timeout: 3.2.0 + + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + p-timeout@3.2.0: + dependencies: + p-finally: 1.0.0 + + package-manager-detector@1.6.0: {} + + parse-entities@2.0.0: + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parse5@8.0.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + partial-json@0.1.7: {} + + path-data-parser@0.1.0: {} + + path-key@3.1.1: {} + + path-to-regexp@0.1.12: {} + + path-to-regexp@8.3.0: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-pretty@11.3.0: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 3.0.2 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pump: 3.0.3 + readable-stream: 4.7.0 + secure-json-parse: 2.7.0 + sonic-boom: 4.2.0 + strip-json-comments: 3.1.1 + + pino-std-serializers@7.1.0: {} + + pino@9.14.0: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.0 + thread-stream: 3.1.0 + + pkce-challenge@5.0.1: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.0 + pathe: 2.0.3 + + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prettier@3.7.4: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + prismjs@1.27.0: {} + + prismjs@1.30.0: {} + + process-warning@5.0.0: {} + + process@0.11.10: {} + + property-information@5.6.0: + dependencies: + xtend: 4.0.2 + + property-information@7.1.0: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@2.3.1: {} + + qs@6.14.1: + dependencies: + side-channel: 1.1.0 + + quick-format-unescaped@4.0.4: {} + + range-parser@1.2.1: {} + + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + + react-dom@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + scheduler: 0.27.0 + + react-is@17.0.2: {} + + react-refresh@0.18.0: {} + + react-remove-scroll-bar@2.3.8(@types/react@19.2.8)(react@19.2.3): + dependencies: + react: 19.2.3 + react-style-singleton: 2.2.3(@types/react@19.2.8)(react@19.2.3) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.8 + + react-remove-scroll@2.7.2(@types/react@19.2.8)(react@19.2.3): + dependencies: + react: 19.2.3 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.8)(react@19.2.3) + react-style-singleton: 2.2.3(@types/react@19.2.8)(react@19.2.3) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.8)(react@19.2.3) + use-sidecar: 1.1.3(@types/react@19.2.8)(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.8 + + react-style-singleton@2.2.3(@types/react@19.2.8)(react@19.2.3): + dependencies: + get-nonce: 1.0.1 + react: 19.2.3 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.8 + + react-syntax-highlighter@15.6.6(react@19.2.3): + dependencies: + '@babel/runtime': 7.28.4 + highlight.js: 10.7.3 + highlightjs-vue: 1.0.0 + lowlight: 1.20.0 + prismjs: 1.30.0 + react: 19.2.3 + refractor: 3.6.0 + + react@19.2.3: {} + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + real-require@0.2.0: {} + + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + + reflect-metadata@0.2.2: {} + + refractor@3.6.0: + dependencies: + hastscript: 6.0.0 + parse-entities: 2.0.0 + prismjs: 1.27.0 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + rehype-harden@1.1.7: + dependencies: + unist-util-visit: 5.1.0 + + rehype-katex@7.0.1: + dependencies: + '@types/hast': 3.0.4 + '@types/katex': 0.16.8 + hast-util-from-html-isomorphic: 2.0.0 + hast-util-to-text: 4.0.2 + katex: 0.16.27 + unist-util-visit-parents: 6.0.2 + vfile: 6.0.3 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-sanitize@6.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-sanitize: 5.0.2 + + remark-cjk-friendly-gfm-strikethrough@1.2.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5): + dependencies: + micromark-extension-cjk-friendly-gfm-strikethrough: 1.2.3(micromark-util-types@2.0.2)(micromark@4.0.2) + unified: 11.0.5 + optionalDependencies: + '@types/mdast': 4.0.4 + transitivePeerDependencies: + - micromark + - micromark-util-types + + remark-cjk-friendly@1.2.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5): + dependencies: + micromark-extension-cjk-friendly: 1.2.3(micromark-util-types@2.0.2)(micromark@4.0.2) + unified: 11.0.5 + optionalDependencies: + '@types/mdast': 4.0.4 + transitivePeerDependencies: + - micromark + - micromark-util-types + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-math@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-math: 3.0.0 + micromark-extension-math: 3.1.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + remend@1.0.1: {} + + require-from-string@2.0.2: {} + + resolve-pkg-maps@1.0.0: {} + + retry@0.13.1: {} + + robust-predicates@3.0.2: {} + + rollup@4.55.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.55.1 + '@rollup/rollup-android-arm64': 4.55.1 + '@rollup/rollup-darwin-arm64': 4.55.1 + '@rollup/rollup-darwin-x64': 4.55.1 + '@rollup/rollup-freebsd-arm64': 4.55.1 + '@rollup/rollup-freebsd-x64': 4.55.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.55.1 + '@rollup/rollup-linux-arm-musleabihf': 4.55.1 + '@rollup/rollup-linux-arm64-gnu': 4.55.1 + '@rollup/rollup-linux-arm64-musl': 4.55.1 + '@rollup/rollup-linux-loong64-gnu': 4.55.1 + '@rollup/rollup-linux-loong64-musl': 4.55.1 + '@rollup/rollup-linux-ppc64-gnu': 4.55.1 + '@rollup/rollup-linux-ppc64-musl': 4.55.1 + '@rollup/rollup-linux-riscv64-gnu': 4.55.1 + '@rollup/rollup-linux-riscv64-musl': 4.55.1 + '@rollup/rollup-linux-s390x-gnu': 4.55.1 + '@rollup/rollup-linux-x64-gnu': 4.55.1 + '@rollup/rollup-linux-x64-musl': 4.55.1 + '@rollup/rollup-openbsd-x64': 4.55.1 + '@rollup/rollup-openharmony-arm64': 4.55.1 + '@rollup/rollup-win32-arm64-msvc': 4.55.1 + '@rollup/rollup-win32-ia32-msvc': 4.55.1 + '@rollup/rollup-win32-x64-gnu': 4.55.1 + '@rollup/rollup-win32-x64-msvc': 4.55.1 + fsevents: 2.3.3 + + rou3@0.7.12: {} + + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.3.0 + transitivePeerDependencies: + - supports-color + + rw@1.3.3: {} + + rxjs@7.8.1: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.2.1: {} + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.27.0: {} + + secure-json-parse@2.7.0: {} + + semver@6.3.1: {} + + semver@7.7.3: {} + + send@0.19.2: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + seroval-plugins@1.3.3(seroval@1.3.2): + dependencies: + seroval: 1.3.2 + + seroval-plugins@1.4.2(seroval@1.4.2): + dependencies: + seroval: 1.4.2 + + seroval@1.3.2: {} + + seroval@1.4.2: {} + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + shiki@3.21.0: + dependencies: + '@shikijs/core': 3.21.0 + '@shikijs/engine-javascript': 3.21.0 + '@shikijs/engine-oniguruma': 3.21.0 + '@shikijs/langs': 3.21.0 + '@shikijs/themes': 3.21.0 + '@shikijs/types': 3.21.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + simple-wcswidth@1.1.2: {} + + solid-js@1.9.10: + dependencies: + csstype: 3.2.3 + seroval: 1.3.2 + seroval-plugins: 1.3.3(seroval@1.3.2) + + sonic-boom@4.2.0: + dependencies: + atomic-sleep: 1.0.0 + + source-map-js@1.2.1: {} + + source-map@0.6.1: {} + + source-map@0.7.6: {} + + space-separated-tokens@1.1.5: {} + + space-separated-tokens@2.0.2: {} + + split2@4.2.0: {} + + srvx@0.10.0: {} + + srvx@0.9.8: {} + + stackback@0.0.2: {} + + statuses@2.0.2: {} + + std-env@3.10.0: {} + + streamdown@1.6.11(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(react@19.2.3): + dependencies: + clsx: 2.1.1 + hast: 1.0.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + katex: 0.16.27 + lucide-react: 0.542.0(react@19.2.3) + marked: 16.4.2 + mermaid: 11.12.2 + react: 19.2.3 + rehype-harden: 1.1.7 + rehype-katex: 7.0.1 + rehype-raw: 7.0.0 + rehype-sanitize: 6.0.0 + remark-cjk-friendly: 1.2.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5) + remark-cjk-friendly-gfm-strikethrough: 1.2.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5) + remark-gfm: 4.0.1 + remark-math: 6.0.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remend: 1.0.1 + shiki: 3.21.0 + tailwind-merge: 3.4.0 + unified: 11.0.5 + unist-util-visit: 5.1.0 + transitivePeerDependencies: + - '@types/mdast' + - micromark + - micromark-util-types + - supports-color + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-json-comments@3.1.1: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + stylis@4.3.6: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + symbol-tree@3.2.4: {} + + tabbable@6.4.0: {} + + tailwind-merge@3.4.0: {} + + tailwindcss@4.1.18: {} + + tapable@2.3.0: {} + + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + + tiny-invariant@1.3.3: {} + + tiny-warning@1.0.3: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyexec@1.0.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + tldts-core@7.0.19: {} + + tldts@7.0.19: + dependencies: + tldts-core: 7.0.19 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + tough-cookie@6.0.0: + dependencies: + tldts: 7.0.19 + + tr46@0.0.3: {} + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + ts-dedent@2.2.0: {} + + ts-deepmerge@7.0.3: {} + + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + tslib@2.8.1: {} + + tsx@4.21.0: + dependencies: + esbuild: 0.27.2 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + tw-animate-css@1.4.0: {} + + type-graphql@2.0.0-rc.1(class-validator@0.14.3)(graphql-scalars@1.25.0(graphql@16.12.0))(graphql@16.12.0): + dependencies: + '@graphql-yoga/subscription': 5.0.5 + '@types/node': 22.19.5 + '@types/semver': 7.7.1 + graphql: 16.12.0 + graphql-query-complexity: 0.12.0(graphql@16.12.0) + graphql-scalars: 1.25.0(graphql@16.12.0) + semver: 7.7.3 + tslib: 2.8.1 + optionalDependencies: + class-validator: 0.14.3 + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + + typescript@5.9.3: {} + + ufo@1.6.2: {} + + undici-types@6.21.0: {} + + undici@7.18.2: {} + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.1.0 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + unpipe@1.0.0: {} + + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.15.0 + picomatch: 4.0.3 + webpack-virtual-modules: 0.6.2 + + unstorage@2.0.0-alpha.5(db0@0.3.4)(lru-cache@11.2.4)(ofetch@2.0.0-alpha.3): + optionalDependencies: + db0: 0.3.4 + lru-cache: 11.2.4 + ofetch: 2.0.0-alpha.3 + + untruncate-json@0.0.1: {} + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + urlpattern-polyfill@10.1.0: {} + + urql@4.2.2(@urql/core@5.2.0(graphql@16.12.0))(react@19.2.3): + dependencies: + '@urql/core': 5.2.0(graphql@16.12.0) + react: 19.2.3 + wonka: 6.3.5 + + use-callback-ref@1.3.3(@types/react@19.2.8)(react@19.2.3): + dependencies: + react: 19.2.3 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.8 + + use-sidecar@1.1.3(@types/react@19.2.8)(react@19.2.3): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.3 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.8 + + use-stick-to-bottom@1.1.2(react@19.2.3): + dependencies: + react: 19.2.3 + + use-sync-external-store@1.6.0(react@19.2.3): + dependencies: + react: 19.2.3 + + utils-merge@1.0.1: {} + + uuid@10.0.0: {} + + uuid@11.1.0: {} + + uuid@9.0.1: {} + + validator@13.15.26: {} + + vary@1.1.2: {} + + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite-node@3.2.4(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-tsconfig-paths@6.0.4(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.3) + optionalDependencies: + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + transitivePeerDependencies: + - supports-color + - typescript + + vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): + dependencies: + esbuild: 0.27.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.55.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.19.5 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + tsx: 4.21.0 + + vitefu@1.1.1(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)): + optionalDependencies: + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.19.5)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(tsx@4.21.0): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vite-node: 3.2.4(@types/node@22.19.5)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.19.5 + jsdom: 27.4.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vscode-jsonrpc@8.2.0: {} + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-uri@3.0.8: {} + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + web-namespaces@2.0.1: {} + + web-vitals@5.1.0: {} + + webidl-conversions@3.0.1: {} + + webidl-conversions@8.0.1: {} + + webpack-virtual-modules@0.6.2: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@15.1.0: + dependencies: + tr46: 6.0.0 + webidl-conversions: 8.0.1 + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wonka@6.3.5: {} + + wrappy@1.0.2: {} + + ws@8.19.0: {} + + xml-name-validator@5.0.0: {} + + xmlbuilder2@4.0.3: + dependencies: + '@oozcitak/dom': 2.0.2 + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + js-yaml: 4.1.1 + + xmlchars@2.2.0: {} + + xtend@4.0.2: {} + + yallist@3.1.1: {} + + zod-to-json-schema@3.25.1(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} + + zwitch@2.0.4: {} diff --git a/tests/ag-ui-app/frontend/public/favicon.ico b/tests/ag-ui-app/frontend/public/favicon.ico new file mode 100644 index 00000000..a11777cc Binary files /dev/null and b/tests/ag-ui-app/frontend/public/favicon.ico differ diff --git a/tests/ag-ui-app/frontend/public/logo192.png b/tests/ag-ui-app/frontend/public/logo192.png new file mode 100644 index 00000000..fc44b0a3 Binary files /dev/null and b/tests/ag-ui-app/frontend/public/logo192.png differ diff --git a/tests/ag-ui-app/frontend/public/logo512.png b/tests/ag-ui-app/frontend/public/logo512.png new file mode 100644 index 00000000..a4e47a65 Binary files /dev/null and b/tests/ag-ui-app/frontend/public/logo512.png differ diff --git a/tests/ag-ui-app/frontend/public/manifest.json b/tests/ag-ui-app/frontend/public/manifest.json new file mode 100644 index 00000000..078ef501 --- /dev/null +++ b/tests/ag-ui-app/frontend/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "TanStack App", + "name": "Create TanStack App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/tests/ag-ui-app/frontend/public/robots.txt b/tests/ag-ui-app/frontend/public/robots.txt new file mode 100644 index 00000000..e9e57dc4 --- /dev/null +++ b/tests/ag-ui-app/frontend/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/tests/ag-ui-app/frontend/public/tanstack-circle-logo.png b/tests/ag-ui-app/frontend/public/tanstack-circle-logo.png new file mode 100644 index 00000000..9db3e67b Binary files /dev/null and b/tests/ag-ui-app/frontend/public/tanstack-circle-logo.png differ diff --git a/tests/ag-ui-app/frontend/public/tanstack-word-logo-white.svg b/tests/ag-ui-app/frontend/public/tanstack-word-logo-white.svg new file mode 100644 index 00000000..b6ec5086 --- /dev/null +++ b/tests/ag-ui-app/frontend/public/tanstack-word-logo-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/ag-ui-app/frontend/src/Screenshot 2026-01-25 at 13.14.31.png b/tests/ag-ui-app/frontend/src/Screenshot 2026-01-25 at 13.14.31.png new file mode 100644 index 00000000..0ef5e5b8 Binary files /dev/null and b/tests/ag-ui-app/frontend/src/Screenshot 2026-01-25 at 13.14.31.png differ diff --git a/tests/ag-ui-app/frontend/src/components/smart-reply/BackgroundEffects.tsx b/tests/ag-ui-app/frontend/src/components/smart-reply/BackgroundEffects.tsx new file mode 100644 index 00000000..a97d3262 --- /dev/null +++ b/tests/ag-ui-app/frontend/src/components/smart-reply/BackgroundEffects.tsx @@ -0,0 +1,55 @@ +/** + * BackgroundEffects Component + * Creates the ambient glassmorphic background with animated orbs and grid pattern + */ +export function BackgroundEffects() { + return ( +
+ {/* Ambient Orb - Cyan (top left) */} +