diff --git a/.gitignore b/.gitignore index acaf045..bc5106e 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,9 @@ docs/RECURRING_FAILURE_PATTERN.md docs/REFLECT_*.md docs/REORGANIZATION_COMPLETE_*.md docs/TESTING_FAILURE_ANALYSIS.md + +# Phase 0 — captured goldens are environment-specific +goldens/ + +# Live state written by work_absorber daemon — not source +WORK_PROGRESS_REPORT.md diff --git a/BETA_ONBOARDING.md b/BETA_ONBOARDING.md index b2fb601..6628cf9 100644 --- a/BETA_ONBOARDING.md +++ b/BETA_ONBOARDING.md @@ -55,17 +55,11 @@ Cortex talks to Claude Code through MCP. Update `.mcp.json` at your Dev root: Replace `YOUR_USER` with your actual username. Claude Code will pick this up on next restart. -## Start the Bridge (For Intelligence Queries) +## No Bridge Needed -Basic commands (`status`, `onboard`, `doctor`) work immediately. For intelligence queries and MCP tools, start the bridge server: - -```bash -# Start bridge in background (runs on :8765) -python api/bridge_endpoint.py & - -# Verify -curl -s http://127.0.0.1:8765/health | python3 -m json.tool -``` +The MCP server runs fully in-process — all 18 tools work without starting any +background server. (The optional HTTP bridge at `:8765` exists only for local +agents like Hermes; MCP users can ignore it.) ## First Session (Verify It Works) @@ -73,14 +67,14 @@ curl -s http://127.0.0.1:8765/health | python3 -m json.tool # 1. Status — shows your context cortex status -# 2. Query intelligence (requires bridge running) +# 2. Query intelligence cortex intelligence "What are the key gotchas in this codebase?" # 3. Daily briefing cortex briefing ``` -In Claude Code, you'll now have 18 MCP tools: `cortex_intelligence`, `cortex_recommendations`, `cortex_anomalies`, `cortex_doctor`, and more. +In Claude Code, you'll now have 18 MCP tools: `cortex_intelligence`, `cortex_recommendations`, `cortex_anomalies`, `cortex_doctor`, and more — all served in-process. ## What to Expect diff --git a/INSTALL.md b/INSTALL.md index fa8984c..207d5fa 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -50,22 +50,10 @@ cortex status cortex doctor ``` -## Starting the Bridge Server +## Intelligence Queries — No Server Needed -The bridge server (`api/bridge_endpoint.py`) powers intelligence queries and MCP integration. Basic commands (`status`, `onboard`, `doctor`) work without it. - -```bash -# Start the bridge (runs on :8765) -python api/bridge_endpoint.py - -# Or with uvicorn directly -uvicorn api.bridge_endpoint:app --host 127.0.0.1 --port 8765 - -# Verify it's running -curl http://127.0.0.1:8765/health -``` - -Once the bridge is running, you can use: +Intelligence runs **in-process**. There is no bridge daemon to start for normal +use: ```bash # Intelligence query — ask Cortex anything about your project @@ -75,6 +63,10 @@ cortex intelligence "What patterns should I watch out for?" cortex briefing ``` +The optional HTTP bridge (`api/bridge_endpoint.py`, installed via +`pip install -e ".[server]"`) exists only for local agents that consume Cortex +over HTTP. MCP clients and the CLI never require it. + ## Claude Code / MCP Integration If you use Claude Code (or any MCP-compatible client), add Cortex as a tool server: @@ -110,15 +102,6 @@ result = bridge.query_intelligence("implement caching", project="my-api") session = bridge.get_session_context() ``` -## Gateway — Telegram Bot + Web Chat (Coming Soon) - -Cortex includes a Gateway module for Telegram (`@KempionBot`) and web chat (`:8765/chat`). **This feature is not yet active by default.** To enable it, you'll need: - -- A Telegram bot token (`CORTEX_TELEGRAM_TOKEN`) from [@BotFather](https://t.me/BotFather) -- The bridge server running (`cortex serve`) - -See `cortex/gateway/` for configuration details. - ## What to Expect - **First session**: Cortex starts with an empty memory. It learns from your git history, commits, and interaction patterns. diff --git a/README.md b/README.md index 4d623bb..4492cce 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,10 @@ print(f"Branch: {session['git']['branch']}") print(f"Active goals: {session['goals']}") ``` -Performance: bridge initialization under 10ms, context retrieval under 100ms, intelligence queries under 1s. +Performance: context retrieval under 100ms, intelligence queries under 1s. First +`CortexBridge()` construction loads ML/embedding modules and can take a few +seconds — the MCP server defers this with a lazy singleton so tool calls stay +responsive. --- @@ -266,6 +269,10 @@ Cortex exposes a Model Context Protocol server so Claude Desktop and compatible Once registered, Claude can call `cortex_intelligence`, `cortex_recommendations`, and `cortex_anomalies` without prompt engineering on your end. +All 18 MCP tools run **in-process** — no background server, no HTTP daemon. The +optional FastAPI bridge (`pip install -e ".[server]"`) is only needed by local +agents that consume Cortex over HTTP; MCP clients never require it. + --- ## Comparison with Alternatives @@ -307,8 +314,8 @@ All data is local by default. Nothing leaves your machine unless you configure a ```bash git clone https://github.com/jessekemp1/cortex cd cortex -pip install -e . # core only -pip install -e ".[server]" # + FastAPI server (uvicorn, apscheduler) +pip install -e . # core only — MCP server + CLI, all in-process +pip install -e ".[server]" # + optional HTTP bridge (uvicorn) for local agents pip install -e ".[all]" # + analytics (xgboost, shap, openai) ``` diff --git a/ROADMAP.md b/ROADMAP.md index c73b759..ca4e6ed 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -298,7 +298,14 @@ Beyond just discovering papers, Cortex needs to evolve its own capabilities: ### 6-Month Roadmap (Phased) -#### Phase 1: Ship + Validate (Mar 12 — Mar 28) — CURRENT +> **Status (2026-05-21):** Phase dates below are the original plan. Phases 1-3 +> ran roughly to schedule (see per-item status columns). A parallel +> engineering-stabilization track also landed in May — gap analysis, a +> contract-test safety net, repair of the broken MCP tools, the bridge +> HTTP collapse (MCP now runs fully in-process), and the install-process +> fixes for Linux. Phase 6 (batch redesign) was added from that track. + +#### Phase 1: Ship + Validate (Mar 12 — Mar 28) | Item | Priority | Status | Dependency | |------|----------|--------|------------| @@ -522,6 +529,74 @@ class CortexMemoryBackend: | **Cross-repo transfer** (memory sharing across repos) | P2 | 2 weeks | Portfolio value | | **CRA self-improvement** (research agent learns what to scan) | P3 | 1 week | Meta-learning | +#### Phase 6: Batch Subsystem Redesign — Local-First Tiered Routing + +**Context.** The `batch/` subsystem was 17,994 LOC built around one strategy: +keep the Anthropic Batch API queue permanently full ("the flywheel"). For a +solo dev / small-portfolio user this is over-built — 5 competing orchestrators, +a daemon that *invents* work to fill the queue, and a large fraction of "batch +intelligence" that is actually mechanical data-processing wrongly routed +through a paid async API. A first gut removed 3,227 LOC of verified-dead code +(deprecated/, 2 orphan orchestrators, weather cruft) — batch/ is now 14,764 +LOC. The remaining redesign replaces "always-full cloud queue" with a +**local-first, 4-tier router** that escalates to Claude only when the task +genuinely needs it. + +| Item | Priority | Effort | Impact | +|------|----------|--------|--------| +| **Step 1 — Tier 0 reclassification** (pull mechanical work off the API) | P1 | 3-4 days | High — removes 40-60% of batch volume; free, instant, robust | +| **Step 2 — Unify 3 orchestrators → one `BatchOrchestrator`** | P1 | 1 week | High — migrate ~8 callers; deletes ~5-6K LOC + optimizer trio | +| **Step 3 — Delete the flywheel daemon** (after `frontier_scout` migrated) | P2 | 1 day | Medium — kills the make-work loop + a macOS-only daemon | +| **Step 4 — Build the 4-tier router** (extend `routing_framework.py`) | P1 | 3-4 days | High — the new decision brain; ~300-500 LOC | +| **Step 5 — Add Ollama Tier 1** (optional, degrades to Tier 2) | P2 | 3-4 days | Medium — free local LLM for bulk summarization | + +**The 4-tier router (routing by task stakes, local-first default):** + +| Tier | Engine | Work | Cost | +|------|--------|------|------| +| 0 | Local, no LLM | Pattern extraction, dedup, scoring, scans | Free, instant | +| 1 | Local LLM (Ollama) | Bulk summarization, triage, draft briefings | Free, minutes | +| 2 | Claude Batch API | Real reasoning, non-urgent | 50% off, async | +| 3 | Claude real-time | Interactive / in-session (MCP tools) | Full price | + +```python +# Extend batch/routing_framework.py from 2-way (interactive|batch) to 4-tier. +# Default to the lowest tier that can do the job; escalate, never default high. +class TieredRouter: + def route(self, task: BatchTask) -> Tier: + if task.is_mechanical: # no LLM needed at all + return Tier.LOCAL_COMPUTE # Tier 0 + if task.quality_tolerant and self.ollama_available: + return Tier.LOCAL_LLM # Tier 1 + if not task.time_sensitive: + return Tier.CLAUDE_BATCH # Tier 2 — 50% off + return Tier.CLAUDE_REALTIME # Tier 3 +``` + +**Sequencing rules:** +- Step 1 first — highest ROI, lowest risk, no caller migration (just stop + routing data-processing through Claude). +- Step 2 is gated by the contract suite — migrate one caller at a time + (`briefing.py`, `cli/v2_ops.py`, `cli/system.py`, `cli/batch.py`, + `engines/frontier_scout.py`, `orchestration/models.py`, `health/monitor.py`, + `batch/overnight_queue.py`), run `pytest tests/contract/` + `smoke_mcp.py` + between each. +- Step 5 (Ollama) must be **optional** — if no local model is reachable, the + router degrades Tier 1 → Tier 2. Never make local a hard dependency + (a CPU-only Hetzner VM runs 8B models slowly). + +**Why this is more robust:** removes the hard dependency on Batch-API uptime / +network / API key for the bulk path; overnight work runs offline on the box; +no "queue stuck for 24h" failure mode; Claude becomes an escalation, not a +single point of failure. Cost becomes bounded — local is free, you pay Claude +only for the escalated high-stakes slice. + +**Success criteria:** `batch/` ≤ 4,000 LOC (from 17,994). One orchestrator, +not five. Zero "invent work to fill the queue" code. Tier 0 work makes zero +API calls. The router degrades cleanly with no Ollama and no Batch API +(everything still completes, just at a higher tier or on-demand). + + --- ### Research Papers to Track (Priority Queue) diff --git a/STRUCTURE.md b/STRUCTURE.md index b66720a..5a7d5ad 100644 --- a/STRUCTURE.md +++ b/STRUCTURE.md @@ -2,70 +2,79 @@ ## Entry Points -| File | Purpose | +| Path | Purpose | |------|---------| -| `cli.py` | CLI entry point (`cortex` command). 4,300 lines — use `def cmd_` to navigate command handlers. Decomposition planned for v1.1. | -| `mcp_server.py` | MCP server for Claude Desktop and compatible clients. | -| `bridge.py` | Python SDK. `CortexBridge` is the public class. Start here for programmatic use. | +| `mcp_server.py` | **Primary interface.** MCP server for Claude Code / Claude Desktop. Since the Phase 5 bridge collapse, all 18 tools run in-process — no HTTP, no daemon. | +| `cli/` | CLI package (`cortex` command). `cli/__init__.py:main()` dispatches to handlers in `cli/commands/`. | +| `bridge.py` | `CortexBridge` — the backing intelligence class. Used directly (in-process) by the MCP server and as a Python SDK. | +| `api/bridge_endpoint.py` | Optional FastAPI HTTP shim. Only needed by local agents (e.g. Hermes) — not by MCP clients. | ## Package Layout ``` cortex/ -├── bridge.py # SDK entry point — CortexBridge class -├── bridge_intelligence.py # Intelligence mixin for CortexBridge (query, retrieval) -├── bridge_system.py # System/ops mixin for CortexBridge (git, deps, portfolio) -├── cli.py # CLI (monolith, refactor in progress) -├── mcp_server.py # MCP server -├── config.py # CortexConfig dataclass + load_config() -├── briefing.py # Daily briefing generation +├── mcp_server.py # MCP server — 18 tools, in-process +├── mcp_handlers.py # Stdlib-only handlers backing MCP tools (no HTTP) +├── health_probe.py # Stdlib-only service-health probes +├── bridge.py # CortexBridge class — core init/storage + composition +├── bridge_intelligence.py # IntelligenceMixin (query, retrieval, recommendations) +├── bridge_system.py # SystemMixin (git, deps, portfolio, graph, batch ops) +├── briefing.py # Daily briefing generation (incl. resilient tiered path) ├── orchestrator.py # Task orchestration +├── recommendation_engine.py# Task-level recommendations +├── recommendations.py # PortfolioRecommender — portfolio-level reports ├── scheduler.py # Background job scheduler ├── learning.py # Feedback and learning loop +├── config.py # CortexConfig dataclass + load_config() +│ +├── cli/ # CLI package +│ ├── __init__.py # main() entry point + dispatcher +│ └── commands/ # command handlers (one module per area) +│ +├── api/ +│ └── bridge_endpoint.py # Optional HTTP shim (FastAPI) for non-MCP consumers │ ├── intelligence/ # Core algorithms (importable subpackage) -│ ├── memory/ # Three-tier memory (tiered_memory.py, hybrid_retriever.py) -│ ├── monitoring/ # Trend analysis, anomaly detection, alert generation -│ ├── signals.py # Signal detection -│ ├── unified_intelligence.py # Aggregates all intelligence sources +│ ├── memory/ # tiered_memory.py, hybrid_retriever.py +│ ├── monitoring/ # trend analysis, anomaly detection, alerts +│ ├── unified_intelligence.py │ └── ... │ -├── tests/ # 600+ tests -├── examples/ # Runnable demos (demo_tiered_memory.py, etc.) -├── batch/ # Async batch job infrastructure -├── scripts/ # Utilities -│ └── internal/ # Internal tooling (not for external use) -├── docs/ -│ ├── API.md # Full API reference -│ ├── user_guide/ # Getting started guides -│ └── archive/internal/ # Session logs and implementation notes (historical) -└── agents/ # Data agents for portfolio analysis +├── batch/ # Async batch infrastructure (Phase 6 redesign planned) +├── engines/, supervisor/ # Orchestration + research agent +│ +├── tests/ +│ ├── contract/ # MCP-tool + bridge-endpoint contract tests +│ └── ... # ~93 test files +└── docs/ ``` ## Bridge Architecture (Mixin Pattern) -`CortexBridge` uses Python mixins to keep the class navigable: +`CortexBridge` composes two mixins to keep the class navigable: ```python # bridge.py — defines CortexBridge + core init/storage -class CortexBridge(BridgeIntelligenceMixin, BridgeSystemMixin): +class CortexBridge(IntelligenceMixin, SystemMixin): def __init__(self, root_dir=None): ... - def get_context(self, task, project): ... # Core retrieval - def inject_recommendation(self, title, ...): ... # Store to memory + def get_context(self, task, project): ... # core retrieval + def query_graph(self, node_type, filters): ... # context graph -# bridge_intelligence.py — intelligence queries -class BridgeIntelligenceMixin: +# bridge_intelligence.py +class IntelligenceMixin: def query_intelligence(self, request, project, ...): ... def get_recommendations(self): ... - def get_anomalies(self, project): ... -# bridge_system.py — system/portfolio operations -class BridgeSystemMixin: - def get_session_context(self): ... - def get_portfolio_stats(self): ... - def get_dependency_graph(self, project): ... +# bridge_system.py +class SystemMixin: + def get_portfolio_health_summary(self): ... + def get_batch_status(self, batch_id): ... ``` +The MCP server reaches `CortexBridge` through a lazy singleton (`mcp_server._get_bridge`) +— construction loads ML/embedding modules and is deferred until the first +tool call that needs it. + ## Configuration Cortex stores user data in `~/.cortex/` (never in the repo). @@ -73,19 +82,26 @@ Cortex stores user data in `~/.cortex/` (never in the repo). Key env vars: - `CORTEX_ROOT_DIR` — path to your projects root (default: cwd) - `ANTHROPIC_API_KEY` — required for embedding and intelligence features -- `CORTEX_ANTI_PATTERNS_SCRIPT` — path to custom anti-pattern mining script (optional) +- `CORTEX_STATE_DIR` / `CORTEX_HOME` — override the `~/.cortex/` state location Config file: `~/.cortex/config.yaml` (created by `cortex init`) ## Known Technical Debt -- `cli.py` is a monolith (4,311 lines). Decomposition into `commands/` is planned for v1.1. -- Internal imports use bare module names (`from formatter import ...`) rather than `from cortex.formatter import ...`. This works with the current `package_dir` setup but will be migrated to proper package imports in v1.1. +- Internal imports use bare module names (`from formatter import ...`) rather + than `from cortex.formatter import ...`. This works with the current + `package_dir` setup but is a real hazard: importing the same file via both + `bridge` and `cortex.bridge` produces two distinct module objects. New code + should pick one canonical path. - `sys.path.insert` calls in several files are a legacy workaround for the above. +- `batch/` (~14.7K LOC) is over-built around an always-full cloud queue — + ROADMAP Phase 6 tracks the local-first redesign. ## Contributing 1. Run `pytest tests/ -v` — all tests must pass before submitting a PR. -2. Run `ruff check .` — no lint errors. -3. New memory or retrieval logic requires tests with **specific value assertions** (not `assert result is not None`). -4. See `tests/KNOWN_ISSUES.md` for the current state of test quality. +2. Run `pytest tests/contract/` — the MCP/bridge contract suite must stay green. +3. Run `ruff check .` — no lint errors. +4. New memory or retrieval logic requires tests with **specific value + assertions** (not `assert result is not None`). +5. See `tests/KNOWN_ISSUES.md` for the test-quality policy. diff --git a/WORK_PROGRESS_REPORT.md b/WORK_PROGRESS_REPORT.md deleted file mode 100644 index 6c4505a..0000000 --- a/WORK_PROGRESS_REPORT.md +++ /dev/null @@ -1,21 +0,0 @@ -# Cortex Work Progress Report -**Auto-updated:** 2026-04-12 10:17 - -- **Work items tracked:** 50 -- **Plan drifts:** 174 -- **Unplanned work:** 89 - ---- - -## Recent Work - -- ✅ [cortex] Task 6 Completion Summary: Layout, StatusBar, CompletedPanel & Final Integration (2026-04-09) -- 🔄 [lhy-site] feat: LHY Admin — Next.js scaffold, Supabase schema, components, migration script (2026-03-22) -- 🔄 [Vortex/frontend] ConfidenceBands Component — Screenshot Summary (2026-03-16) -- 🔄 [Vortex/backend] VortexV2: Executive Summary (2026-03-13) -- 🔄 [kempion-research-site] fix(deploy): move excess api functions to _disabled for Vercel Hobby limit (2026-03-11) -- 🔄 [pupil] Pupil: Canadian FinServ Population Engine (2026-02-23) -- 🔄 [cortex] .claude/BATCH_CLEANUP_RESULTS.md (2026-02-16) -- ✅ [Vortex/frontend] VortexV3 Week 2: Complete Implementation Summary (2026-02-14) -- 🔄 [Vortex/backend] UI Transparency Implementation Guide (2026-02-14) -- ✅ [cortex] Converx Integration Complete - ACTION_PLAN.md Integration (2026-02-14) diff --git a/_contrib/cortexdbx/README.md b/_contrib/cortexdbx/README.md deleted file mode 100644 index eb4e589..0000000 --- a/_contrib/cortexdbx/README.md +++ /dev/null @@ -1,109 +0,0 @@ -# CortexDBx - -Intelligence Layer for Databricks: outcome-based learning with calibrated recommendations. - -## Overview - -CortexDBx tracks context, strategy, and outcome; calibrates confidence via Beta-Binomial; and surfaces recommendations. It runs locally (in-memory) for dev and tests, or on Databricks (Delta/Unity Catalog) for production. - -## Structure - -``` -cortexdbx/ -├── __init__.py # Package exports -├── synthetic/ # Synthetic data generator (6 domains) -├── calibration/ # Bayesian calibration -├── sdk/ # Python SDK (local + Spark-ready) -├── agents/ # Agent team architecture -├── scripts/ # Local verification -│ ├── run_e2e_local.sh # Run unit + e2e tests -│ └── run_e2e_local.py -├── notebooks/ # Databricks notebooks -│ ├── 01_generate_synthetic_data.py -│ ├── 02_run_calibration.py -│ └── 03_run_recommendations.py -├── databricks/ # Databricks deployment -│ ├── jobs/ # Job JSON definitions -│ └── README.md # How to run on Databricks -└── tests/ - ├── test_e2e_learning_loop.py # Full learning loop e2e - ├── test_generator.py - ├── test_calibration.py - ├── test_sdk.py - └── test_orchestrator.py -``` - -## Quick Start - -### Local (no Databricks) - -```python -from cortexdbx import SyntheticDataGenerator, CalibrationEngine -from cortexdbx.sdk import CortexDBxClient - -# Generate synthetic data -gen = SyntheticDataGenerator("fraud_investigation", seed=42) -data = list(gen.generate_dataset(100)) - -# Calibrate from outcomes -engine = CalibrationEngine() -for r in data: - engine.update( - r["context"]["context_id"], - r["strategy"]["strategy_id"], - r["outcome"]["result"], - ) - -# Or use SDK (in-memory backend) -client = CortexDBxClient() -client.log_outcome({"alert_type": "fraud"}, "escalate_to_analyst", "SUCCESS") -recs = client.recommend({"alert_type": "fraud"}, min_confidence=0.5) -``` - -### Run Tests - -```bash -cd /path/to/cortex -python -m pytest cortexdbx/tests/ -v -``` - -## E2E: Run locally first - -Validate the full learning loop (generate, ingest, calibrate, recommend) before Databricks: - -```bash -cd /path/to/cortex -./cortexdbx/scripts/run_e2e_local.sh -# or -python cortexdbx/scripts/run_e2e_local.py -``` - -This runs all unit tests and e2e tests (`test_e2e_learning_loop.py`). Success criteria: Brier < 0.25, at least one recommendation for a seen context. - -## Databricks: Build and run - -Same flow (generate synthetic data, run calibration, write recommendations) on Databricks: - -1. Clone the cortex repo into Databricks Repos; set `PYTHONPATH` to the repo root so `import cortexdbx` works. -2. Run notebooks in order: `01_generate_synthetic_data` -> `02_run_calibration` -> `03_run_recommendations` (or create jobs from `cortexdbx/databricks/jobs/*.json`). -3. Verify: query `outcomes`, `context_strategy_edges`, and `recommendations` (see verification queries in `cortexdbx/databricks/README.md`). - -Full steps, job definitions, and verification: [cortexdbx/databricks/README.md](databricks/README.md). - -## Domains (6 use cases) - -- fraud_investigation -- clinical_trial -- maintenance -- marketing_campaign -- security_incident -- supply_chain - -## Documentation - -- [CortexDBx MVP Implementation](../../docs/v1/CORTEXDBX_MVP.md) - Full spec, Delta schema, Databricks jobs -- [Cortex Core](../../docs/v1/CORTEX_CORE.md) - Vision and architecture - -## Version - -0.1.0 diff --git a/_contrib/cortexdbx/__init__.py b/_contrib/cortexdbx/__init__.py deleted file mode 100644 index 9054674..0000000 --- a/_contrib/cortexdbx/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -CortexDBx: Intelligence Layer for Databricks. - -Outcome-based learning: track context + strategy + result, calibrate confidence, -surface recommendations. Runs on Databricks (Delta/Unity Catalog) or locally for dev. -""" - -__version__ = "0.1.0" - -from cortexdbx.calibration.engine import CalibrationEngine, CalibrationState -from cortexdbx.synthetic.generator import DOMAIN_CONFIGS, SyntheticDataGenerator - -__all__ = [ - "__version__", - "SyntheticDataGenerator", - "DOMAIN_CONFIGS", - "CalibrationEngine", - "CalibrationState", -] diff --git a/_contrib/cortexdbx/agents/__init__.py b/_contrib/cortexdbx/agents/__init__.py deleted file mode 100644 index 1cb073a..0000000 --- a/_contrib/cortexdbx/agents/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Agent team architecture for CortexDBx scaling.""" - -from cortexdbx.agents.definitions import AGENT_CONFIGS, AgentConfig, AgentRole -from cortexdbx.agents.orchestrator import AgentOrchestrator - -__all__ = [ - "AgentConfig", - "AgentRole", - "AGENT_CONFIGS", - "AgentOrchestrator", -] diff --git a/_contrib/cortexdbx/agents/definitions.py b/_contrib/cortexdbx/agents/definitions.py deleted file mode 100644 index 5028792..0000000 --- a/_contrib/cortexdbx/agents/definitions.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Agent definitions for CortexDBx domain experts.""" - -from dataclasses import dataclass -from enum import Enum -from typing import List - - -class AgentRole(Enum): - ORCHESTRATOR = "orchestrator" - DOMAIN_EXPERT = "domain_expert" - CALIBRATOR = "calibrator" - RECOMMENDER = "recommender" - EVALUATOR = "evaluator" - - -@dataclass -class AgentConfig: - """Configuration for a CortexDBx agent.""" - - role: AgentRole - domain: str - capabilities: List[str] - prompt_template: str - tools: List[str] - - -AGENT_CONFIGS = { - "orchestrator": AgentConfig( - role=AgentRole.ORCHESTRATOR, - domain="all", - capabilities=[ - "coordinate_domain_agents", - "aggregate_results", - "manage_parallelism", - "handle_failures", - ], - prompt_template=( - "You are the CortexDBx Orchestrator Agent. " - "Current task: {task}. Available domain agents: {agents}" - ), - tools=["spawn_agent", "wait_for_agents", "aggregate_results"], - ), - "fraud_domain": AgentConfig( - role=AgentRole.DOMAIN_EXPERT, - domain="fraud_investigation", - capabilities=[ - "analyze_fraud_patterns", - "recommend_investigation_priority", - "learn_from_resolution_outcomes", - ], - prompt_template=( - "You are the Fraud Investigation Domain Agent. Context: {context}. Task: {task}" - ), - tools=["query_outcomes", "update_calibration", "generate_recommendation"], - ), - "healthcare_domain": AgentConfig( - role=AgentRole.DOMAIN_EXPERT, - domain="clinical_trial", - capabilities=[ - "analyze_enrollment_patterns", - "recommend_criteria_adjustments", - "learn_from_trial_outcomes", - ], - prompt_template=( - "You are the Clinical Trial Domain Agent. Context: {context}. Task: {task}" - ), - tools=["query_outcomes", "update_calibration", "generate_recommendation"], - ), - "maintenance_domain": AgentConfig( - role=AgentRole.DOMAIN_EXPERT, - domain="maintenance", - capabilities=[ - "analyze_maintenance_patterns", - "recommend_repair_strategy", - "learn_from_repair_outcomes", - ], - prompt_template=("You are the Maintenance Domain Agent. Context: {context}. Task: {task}"), - tools=["query_outcomes", "update_calibration", "generate_recommendation"], - ), - "marketing_domain": AgentConfig( - role=AgentRole.DOMAIN_EXPERT, - domain="marketing_campaign", - capabilities=[ - "analyze_campaign_patterns", - "recommend_creative_strategy", - "learn_from_campaign_outcomes", - ], - prompt_template=( - "You are the Marketing Campaign Domain Agent. Context: {context}. Task: {task}" - ), - tools=["query_outcomes", "update_calibration", "generate_recommendation"], - ), - "security_domain": AgentConfig( - role=AgentRole.DOMAIN_EXPERT, - domain="security_incident", - capabilities=[ - "analyze_incident_patterns", - "recommend_playbook", - "learn_from_response_outcomes", - ], - prompt_template=( - "You are the Security Incident Domain Agent. Context: {context}. Task: {task}" - ), - tools=["query_outcomes", "update_calibration", "generate_recommendation"], - ), - "supply_chain_domain": AgentConfig( - role=AgentRole.DOMAIN_EXPERT, - domain="supply_chain", - capabilities=[ - "analyze_routing_patterns", - "recommend_supplier_strategy", - "learn_from_delivery_outcomes", - ], - prompt_template=("You are the Supply Chain Domain Agent. Context: {context}. Task: {task}"), - tools=["query_outcomes", "update_calibration", "generate_recommendation"], - ), -} diff --git a/_contrib/cortexdbx/agents/orchestrator.py b/_contrib/cortexdbx/agents/orchestrator.py deleted file mode 100644 index ee401e7..0000000 --- a/_contrib/cortexdbx/agents/orchestrator.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -Agent orchestrator for CortexDBx. - -Coordinates domain agents for parallel processing of outcomes. -""" - -import logging -from concurrent.futures import ThreadPoolExecutor, as_completed -from typing import Any, Dict, List - -from cortexdbx.agents.definitions import AGENT_CONFIGS -from cortexdbx.calibration.engine import CalibrationEngine - -logger = logging.getLogger(__name__) - - -class AgentOrchestrator: - """ - Coordinates domain agents for parallel processing. - - Ingests outcomes by domain and updates a shared calibration engine. - """ - - def __init__(self, max_workers: int = 6): - self.max_workers = max_workers - self.executor = ThreadPoolExecutor(max_workers=max_workers) - self.calibration = CalibrationEngine() - self._domain_agents: Dict[str, bool] = { - d: True for d in AGENT_CONFIGS if d != "orchestrator" - } - - def process_outcomes(self, outcomes: List[Dict[str, Any]]) -> Dict[str, Any]: - """ - Process a list of outcomes: group by domain and update calibration. - - outcomes: list of dicts with context_id, strategy_id, result - Returns: aggregated stats (processed, by_domain) - """ - by_domain: Dict[str, List[Dict[str, Any]]] = {} - for o in outcomes: - domain = o.get("domain", "unknown") - if domain not in by_domain: - by_domain[domain] = [] - by_domain[domain].append(o) - - total_ingested = 0 - by_domain_stats: Dict[str, int] = {} - - for domain, domain_outcomes in by_domain.items(): - count = self.calibration.ingest_outcomes(domain_outcomes) - by_domain_stats[domain] = count - total_ingested += count - - return { - "total_processed": len(outcomes), - "total_ingested": total_ingested, - "by_domain": by_domain_stats, - } - - def process_outcomes_parallel( - self, outcomes: List[Dict[str, Any]], chunk_size: int = 500 - ) -> Dict[str, Any]: - """ - Process outcomes in parallel chunks. - - Splits outcomes into chunks and processes each chunk in a worker. - """ - chunks: List[List[Dict[str, Any]]] = [] - for i in range(0, len(outcomes), chunk_size): - chunks.append(outcomes[i : i + chunk_size]) - - all_results: List[Dict[str, Any]] = [] - futures = {self.executor.submit(self.process_outcomes, chunk): chunk for chunk in chunks} - - for future in as_completed(futures): - try: - result = future.result() - all_results.append(result) - except Exception as e: - logger.error("Chunk processing failed: %s", e) - - total_processed = sum(r["total_processed"] for r in all_results) - total_ingested = sum(r["total_ingested"] for r in all_results) - by_domain: Dict[str, int] = {} - for r in all_results: - for d, c in r.get("by_domain", {}).items(): - by_domain[d] = by_domain.get(d, 0) + c - - return { - "total_processed": total_processed, - "total_ingested": total_ingested, - "by_domain": by_domain, - "chunks_processed": len(all_results), - } - - def get_recommendations( - self, context_id: str, min_confidence: float = 0.7, limit: int = 10 - ) -> List[tuple]: - """Get top strategy recommendations for a context from calibration.""" - return self.calibration.get_top_strategies( - context_id, min_confidence=min_confidence, limit=limit - ) - - def evaluate_calibration(self, ground_truth: Dict[str, float]) -> Dict[str, Any]: - """Evaluate calibration quality against ground truth.""" - return self.calibration.evaluate_calibration(ground_truth) - - def shutdown(self) -> None: - """Shutdown the executor.""" - self.executor.shutdown(wait=True) diff --git a/_contrib/cortexdbx/calibration/__init__.py b/_contrib/cortexdbx/calibration/__init__.py deleted file mode 100644 index 635c235..0000000 --- a/_contrib/cortexdbx/calibration/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Bayesian calibration for CortexDBx outcome graph.""" - -from cortexdbx.calibration.engine import CalibrationEngine, CalibrationState - -__all__ = ["CalibrationEngine", "CalibrationState"] diff --git a/_contrib/cortexdbx/calibration/engine.py b/_contrib/cortexdbx/calibration/engine.py deleted file mode 100644 index 1393629..0000000 --- a/_contrib/cortexdbx/calibration/engine.py +++ /dev/null @@ -1,163 +0,0 @@ -""" -Beta-Binomial calibration engine for CortexDBx. - -Updates confidence per (context, strategy) based on outcomes. -""" - -from dataclasses import dataclass -from typing import Any, Dict, List, Tuple - - -@dataclass -class CalibrationState: - """State for a single (context, strategy) edge.""" - - alpha: float - beta: float - success_count: int - failure_count: int - partial_count: int - - @property - def confidence(self) -> float: - """Current P(success) estimate.""" - return self.alpha / (self.alpha + self.beta) - - @property - def evidence_count(self) -> int: - return self.success_count + self.failure_count + self.partial_count - - @property - def uncertainty(self) -> float: - """Variance of Beta distribution - higher = more uncertain.""" - a, b = self.alpha, self.beta - return (a * b) / ((a + b) ** 2 * (a + b + 1)) - - -class CalibrationEngine: - """ - Bayesian calibration using Beta-Binomial model. - - Updates confidence per (context, strategy) pair based on outcomes. - """ - - def __init__(self, prior_alpha: float = 1.0, prior_beta: float = 1.0): - self.prior_alpha = prior_alpha - self.prior_beta = prior_beta - self.states: Dict[Tuple[str, str], CalibrationState] = {} - - def get_state(self, context_id: str, strategy_id: str) -> CalibrationState: - """Get or create calibration state for (context, strategy).""" - key = (context_id, strategy_id) - if key not in self.states: - self.states[key] = CalibrationState( - alpha=self.prior_alpha, - beta=self.prior_beta, - success_count=0, - failure_count=0, - partial_count=0, - ) - return self.states[key] - - def update(self, context_id: str, strategy_id: str, result: str) -> CalibrationState: - """ - Update calibration based on new outcome. - - result: 'SUCCESS' (counts as 1), 'PARTIAL' (counts as 0.5), 'FAILURE' (counts as 0) - """ - state = self.get_state(context_id, strategy_id) - result_upper = result.upper() - - if result_upper == "SUCCESS": - state.alpha += 1.0 - state.success_count += 1 - elif result_upper == "PARTIAL": - state.alpha += 0.5 - state.beta += 0.5 - state.partial_count += 1 - else: - state.beta += 1.0 - state.failure_count += 1 - - return state - - def get_confidence(self, context_id: str, strategy_id: str) -> Tuple[float, str]: - """ - Get current confidence with explanation. - - Returns: (confidence, explanation) - """ - state = self.get_state(context_id, strategy_id) - - if state.evidence_count == 0: - return 0.5, "No historical data" - - confidence = state.confidence - explanation = ( - f"{confidence:.0%} confidence based on {state.evidence_count} outcomes " - f"({state.success_count} success, {state.failure_count} failure, " - f"{state.partial_count} partial)" - ) - return confidence, explanation - - def ingest_outcomes(self, outcomes: List[Dict[str, Any]]) -> int: - """ - Ingest a list of outcome records and update calibration. - - Each outcome must have context_id, strategy_id, result. - Returns count of outcomes ingested. - """ - count = 0 - for o in outcomes: - ctx = o.get("context_id") - strat = o.get("strategy_id") - result = o.get("result") - if ctx and strat and result: - self.update(ctx, strat, result) - count += 1 - return count - - def evaluate_calibration(self, ground_truth: Dict[str, float]) -> Dict[str, Any]: - """ - Evaluate calibration quality against ground truth. - - ground_truth: strategy_id -> actual success rate - Returns metrics including Brier score and MAE. - """ - predictions: List[float] = [] - actuals: List[float] = [] - - for (ctx, strat), state in self.states.items(): - if strat in ground_truth and state.evidence_count >= 5: - predictions.append(state.confidence) - actuals.append(ground_truth[strat]) - - if not predictions: - return {"error": "Insufficient data for evaluation", "n": 0} - - brier = sum((p - a) ** 2 for p, a in zip(predictions, actuals)) / len(predictions) - mae = sum(abs(p - a) for p, a in zip(predictions, actuals)) / len(predictions) - - high_conf = [(p, a) for p, a in zip(predictions, actuals) if p > 0.8] - precision_high = ( - sum(1 for p, a in high_conf if a > 0.6) / len(high_conf) if high_conf else None - ) - - return { - "brier_score": brier, - "mean_absolute_error": mae, - "precision_at_high_confidence": precision_high, - "num_evaluated": len(predictions), - } - - def get_top_strategies( - self, context_id: str, min_confidence: float = 0.0, limit: int = 10 - ) -> List[Tuple[str, float]]: - """Return (strategy_id, confidence) for a context, sorted by confidence.""" - candidates = [ - (strat_id, state.confidence) - for (ctx_id, strat_id), state in self.states.items() - if ctx_id == context_id and state.confidence >= min_confidence - ] - candidates.sort(key=lambda x: x[1], reverse=True) - return candidates[:limit] diff --git a/_contrib/cortexdbx/databricks/README.md b/_contrib/cortexdbx/databricks/README.md deleted file mode 100644 index 520b0a5..0000000 --- a/_contrib/cortexdbx/databricks/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# CortexDBx on Databricks - -How to build and run CortexDBx in a Databricks workspace (generate synthetic data, run calibration, write recommendations). - -## Prerequisites - -- Databricks workspace with Python 3.8+ -- Cluster or job cluster with access to Unity Catalog (or use `hive_metastore`) - -## Option A: Repos (recommended) - -1. **Clone the cortex repo into Databricks Repos** - - In the workspace: Repos -> Add Repo -> clone your cortex repo (e.g. `https://github.com//cortex.git`). - - Repo will be at e.g. `/Repos//cortex` (or whatever name you gave the repo). - -2. **Set PYTHONPATH so `cortexdbx` is importable** - - On the cluster: Environment variables -> add `PYTHONPATH` = `/Repos//cortex` (replace with your repo path). - - Or in the job cluster config: same env var. - -3. **Update job notebook paths** - - Job JSON files in `cortexdbx/databricks/jobs/` use paths like `/Repos/cortex/cortexdbx/notebooks/01_generate_synthetic_data`. - - If your repo is under a different path (e.g. `/Repos//cortex`), update the `notebook_path` in each job JSON to match (e.g. `/Repos//cortex/cortexdbx/notebooks/01_generate_synthetic_data`). - -4. **Run notebooks in order** - - **01_generate_synthetic_data**: Creates catalog/schema if needed, generates synthetic outcomes, writes `contexts`, `strategies`, `outcomes`. - - **02_run_calibration**: Reads `outcomes`, computes per (context_id, strategy_id) Beta posterior, writes `context_strategy_edges`. - - **03_run_recommendations**: Reads `context_strategy_edges`, filters by min_confidence, writes `recommendations`. - - You can run them manually from the Repo notebook UI, or create jobs that run these notebooks (see Job definitions below). - -## Option B: Job + wheel - -1. Build a wheel that includes `cortexdbx` (e.g. from the cortex repo root: ensure `cortexdbx` is a package, then `pip wheel .` or build via setup.py). -2. Install the wheel on the job cluster (e.g. pip install from a volume or from DBFS). -3. Run the same notebooks; they will import `cortexdbx` from the installed package. - -## Job definitions - -Job JSON files are in `cortexdbx/databricks/jobs/`: - -- **generate_synthetic_data_job.json**: Runs 01 once (generate data). -- **calibration_job.json**: Runs 02 on a schedule (e.g. hourly). -- **full_run_job.json**: Runs 01 -> 02 -> 03 in sequence (generate, calibrate, recommendations). - -To create a job from JSON (Databricks CLI or API): - -```bash -databricks jobs create --json-file cortexdbx/databricks/jobs/full_run_job.json -``` - -Or in the workspace: Workflows -> Create Job -> paste/import the JSON (and fix notebook paths if needed). - -## Widgets / parameters - -Notebooks use Databricks widgets for: - -- **catalog**: e.g. `cortex_catalog` (use `hive_metastore` if not using Unity Catalog). -- **schema**: e.g. `cortex_mvp`. -- **outcomes_per_domain** (01 only): e.g. `1000`. -- **min_confidence** (03 only): e.g. `0.7`. - -Defaults are set in the notebooks; jobs pass them via `base_parameters`. - -## Verification in Databricks - -After running the notebooks: - -1. **After 01** - - `SELECT COUNT(*) FROM ..outcomes` - - Expect hundreds/thousands per domain (e.g. 6000 total for 6 domains x 1000). - -2. **After 02** - - `SELECT COUNT(*) FROM ..context_strategy_edges` - - Expect hundreds/thousands of edges. - - `SELECT * FROM ..context_strategy_edges ORDER BY confidence DESC LIMIT 20` - - Spot-check: confidence in [0, 1], alpha/beta positive. - -3. **After 03** - - `SELECT COUNT(*) FROM ..recommendations` - - Expect recommendations with confidence >= min_confidence. - - Optionally, in a Python cell, use `CortexDBxClient(use_local_backend=False)` with Spark to call `recommend(context)` for a known context and assert non-empty result (requires SDK wired to Delta; see docs/v1/CORTEXDBX_MVP.md). - -## Success criteria - -- Generate and calibration jobs complete without error. -- Tables `outcomes`, `context_strategy_edges`, and (if run) `recommendations` are populated. -- Spot-check: confidence values in [0, 1], at least one recommendation for a context that had outcomes. diff --git a/_contrib/cortexdbx/databricks/jobs/calibration_job.json b/_contrib/cortexdbx/databricks/jobs/calibration_job.json deleted file mode 100644 index f796fca..0000000 --- a/_contrib/cortexdbx/databricks/jobs/calibration_job.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "cortexdbx-calibration", - "schedule": { - "quartz_cron_expression": "0 0 * * * ?", - "timezone_id": "UTC" - }, - "tasks": [ - { - "task_key": "run_calibration", - "notebook_task": { - "notebook_path": "/Repos/cortex/cortexdbx/notebooks/02_run_calibration", - "base_parameters": { - "catalog": "cortex_catalog", - "schema": "cortex_mvp" - } - }, - "new_cluster": { - "spark_version": "14.3.x-scala2.12", - "node_type_id": "i3.xlarge", - "num_workers": 2 - } - } - ] -} diff --git a/_contrib/cortexdbx/databricks/jobs/full_run_job.json b/_contrib/cortexdbx/databricks/jobs/full_run_job.json deleted file mode 100644 index 6b37498..0000000 --- a/_contrib/cortexdbx/databricks/jobs/full_run_job.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "cortexdbx-full-run", - "tasks": [ - { - "task_key": "generate_data", - "notebook_task": { - "notebook_path": "/Repos/cortex/cortexdbx/notebooks/01_generate_synthetic_data", - "base_parameters": { - "catalog": "cortex_catalog", - "schema": "cortex_mvp", - "outcomes_per_domain": "1000" - } - }, - "new_cluster": { - "spark_version": "14.3.x-scala2.12", - "node_type_id": "i3.xlarge", - "num_workers": 2 - } - }, - { - "task_key": "run_calibration", - "depends_on": [{"task_key": "generate_data"}], - "notebook_task": { - "notebook_path": "/Repos/cortex/cortexdbx/notebooks/02_run_calibration", - "base_parameters": { - "catalog": "cortex_catalog", - "schema": "cortex_mvp" - } - }, - "new_cluster": { - "spark_version": "14.3.x-scala2.12", - "node_type_id": "i3.xlarge", - "num_workers": 2 - } - }, - { - "task_key": "run_recommendations", - "depends_on": [{"task_key": "run_calibration"}], - "notebook_task": { - "notebook_path": "/Repos/cortex/cortexdbx/notebooks/03_run_recommendations", - "base_parameters": { - "catalog": "cortex_catalog", - "schema": "cortex_mvp", - "min_confidence": "0.7" - } - }, - "new_cluster": { - "spark_version": "14.3.x-scala2.12", - "node_type_id": "i3.xlarge", - "num_workers": 2 - } - } - ] -} diff --git a/_contrib/cortexdbx/databricks/jobs/generate_synthetic_data_job.json b/_contrib/cortexdbx/databricks/jobs/generate_synthetic_data_job.json deleted file mode 100644 index bee4697..0000000 --- a/_contrib/cortexdbx/databricks/jobs/generate_synthetic_data_job.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "cortexdbx-generate-synthetic-data", - "tasks": [ - { - "task_key": "generate_data", - "notebook_task": { - "notebook_path": "/Repos/cortex/cortexdbx/notebooks/01_generate_synthetic_data", - "base_parameters": { - "catalog": "cortex_catalog", - "schema": "cortex_mvp", - "outcomes_per_domain": "1000" - } - }, - "new_cluster": { - "spark_version": "14.3.x-scala2.12", - "node_type_id": "i3.xlarge", - "num_workers": 2 - } - } - ] -} diff --git a/_contrib/cortexdbx/notebooks/01_generate_synthetic_data.py b/_contrib/cortexdbx/notebooks/01_generate_synthetic_data.py deleted file mode 100644 index 078105b..0000000 --- a/_contrib/cortexdbx/notebooks/01_generate_synthetic_data.py +++ /dev/null @@ -1,99 +0,0 @@ -# Databricks notebook source -# MAGIC %md -# MAGIC # 01 Generate Synthetic Data -# MAGIC Creates catalog/schema if needed, generates outcomes for CortexDBx domains, writes to Delta (contexts, strategies, outcomes). -# MAGIC Requires: cortexdbx on PYTHONPATH (e.g. repo root) or installed via pip. - -# COMMAND ---------- - -# Widgets for catalog and schema (defaults work with Unity Catalog or hive_metastore) -dbutils.widgets.text("catalog", "cortex_catalog", "Catalog") -dbutils.widgets.text("schema", "cortex_mvp", "Schema") -dbutils.widgets.text("outcomes_per_domain", "1000", "Outcomes per domain") - -# COMMAND ---------- - -CATALOG = dbutils.widgets.get("catalog") -SCHEMA = dbutils.widgets.get("schema") -OUTCOMES_PER_DOMAIN = int(dbutils.widgets.get("outcomes_per_domain")) - -# COMMAND ---------- - -# Create catalog and schema if using Unity Catalog (skip if hive_metastore) -if CATALOG != "hive_metastore": - spark.sql(f"CREATE CATALOG IF NOT EXISTS {CATALOG}") -spark.sql(f"CREATE SCHEMA IF NOT EXISTS {CATALOG}.{SCHEMA}") -spark.sql(f"USE {CATALOG}.{SCHEMA}") - -# COMMAND ---------- - -import json - -# Import cortexdbx (must be on PYTHONPATH or installed) -from cortexdbx.synthetic.generator import DOMAIN_CONFIGS, SyntheticDataGenerator - -# COMMAND ---------- - -# Generate data for all 6 domains -all_contexts = {} -all_strategies = {} -all_outcomes = [] - -for domain in DOMAIN_CONFIGS: - gen = SyntheticDataGenerator(domain, seed=42) - for record in gen.generate_dataset(OUTCOMES_PER_DOMAIN): - ctx = record["context"] - strat = record["strategy"] - out = record["outcome"] - all_contexts[ctx["context_id"]] = { - "context_id": ctx["context_id"], - "context_hash": ctx["context_hash"], - "domain": ctx["domain"], - "factors": json.dumps(ctx["factors"]), - "first_seen": out["created_at"], - "last_seen": out["created_at"], - } - all_strategies[strat["strategy_id"]] = { - "strategy_id": strat["strategy_id"], - "name": strat["name"], - "domain": strat["domain"], - "category": strat.get("category", "primary"), - "created_at": out["created_at"], - } - all_outcomes.append( - { - "outcome_id": out["outcome_id"], - "context_id": out["context_id"], - "strategy_id": out["strategy_id"], - "result": out["result"], - "evidence": out["evidence"], - "actor": out["actor"], - "created_at": out["created_at"], - } - ) - -# COMMAND ---------- - -# Write contexts (overwrite for demo; use append for incremental) -contexts_df = spark.createDataFrame(list(all_contexts.values())) -contexts_df.write.mode("overwrite").saveAsTable(f"{CATALOG}.{SCHEMA}.contexts") - -# COMMAND ---------- - -# Write strategies -strategies_df = spark.createDataFrame(list(all_strategies.values())) -strategies_df.write.mode("overwrite").saveAsTable(f"{CATALOG}.{SCHEMA}.strategies") - -# COMMAND ---------- - -# Write outcomes -outcomes_df = spark.createDataFrame(all_outcomes) -outcomes_df.write.mode("overwrite").saveAsTable(f"{CATALOG}.{SCHEMA}.outcomes") - -# COMMAND ---------- - -# Verify -display( - spark.sql(f"SELECT domain, COUNT(*) as cnt FROM {CATALOG}.{SCHEMA}.outcomes GROUP BY domain") -) -display(spark.sql(f"SELECT COUNT(*) as total_outcomes FROM {CATALOG}.{SCHEMA}.outcomes")) diff --git a/_contrib/cortexdbx/notebooks/02_run_calibration.py b/_contrib/cortexdbx/notebooks/02_run_calibration.py deleted file mode 100644 index 8637997..0000000 --- a/_contrib/cortexdbx/notebooks/02_run_calibration.py +++ /dev/null @@ -1,69 +0,0 @@ -# Databricks notebook source -# MAGIC %md -# MAGIC # 02 Run Calibration -# MAGIC Reads outcomes, computes per (context_id, strategy_id) success/failure/partial counts and Beta posterior, writes context_strategy_edges. - -# COMMAND ---------- - -dbutils.widgets.text("catalog", "cortex_catalog", "Catalog") -dbutils.widgets.text("schema", "cortex_mvp", "Schema") - -# COMMAND ---------- - -CATALOG = dbutils.widgets.get("catalog") -SCHEMA = dbutils.widgets.get("schema") -PRIOR_ALPHA = 1.0 -PRIOR_BETA = 1.0 - -# COMMAND ---------- - -from pyspark.sql import functions as F - -# COMMAND ---------- - -# Read outcomes -outcomes_df = spark.table(f"{CATALOG}.{SCHEMA}.outcomes") - -# COMMAND ---------- - -# Aggregate per (context_id, strategy_id): success_count, failure_count, partial_count -aggregated = outcomes_df.groupBy("context_id", "strategy_id").agg( - F.sum(F.when(F.col("result") == "SUCCESS", 1).otherwise(0)).alias("success_count"), - F.sum(F.when(F.col("result") == "FAILURE", 1).otherwise(0)).alias("failure_count"), - F.sum(F.when(F.col("result") == "PARTIAL", 1).otherwise(0)).alias("partial_count"), - F.max("created_at").alias("last_updated"), -) - -# COMMAND ---------- - -# Beta-Binomial: alpha = prior_alpha + success + 0.5*partial, beta = prior_beta + failure + 0.5*partial -# confidence = alpha / (alpha + beta) -edges_df = ( - aggregated.withColumn( - "alpha", - F.lit(PRIOR_ALPHA) + F.col("success_count") + 0.5 * F.col("partial_count"), - ) - .withColumn( - "beta", - F.lit(PRIOR_BETA) + F.col("failure_count") + 0.5 * F.col("partial_count"), - ) - .withColumn( - "confidence", - F.col("alpha") / (F.col("alpha") + F.col("beta")), - ) -) - -# COMMAND ---------- - -# Write context_strategy_edges -edges_df.write.mode("overwrite").saveAsTable(f"{CATALOG}.{SCHEMA}.context_strategy_edges") - -# COMMAND ---------- - -# Verify -display( - spark.sql( - f"SELECT * FROM {CATALOG}.{SCHEMA}.context_strategy_edges ORDER BY confidence DESC LIMIT 20" - ) -) -display(spark.sql(f"SELECT COUNT(*) as total_edges FROM {CATALOG}.{SCHEMA}.context_strategy_edges")) diff --git a/_contrib/cortexdbx/notebooks/03_run_recommendations.py b/_contrib/cortexdbx/notebooks/03_run_recommendations.py deleted file mode 100644 index 639912f..0000000 --- a/_contrib/cortexdbx/notebooks/03_run_recommendations.py +++ /dev/null @@ -1,60 +0,0 @@ -# Databricks notebook source -# MAGIC %md -# MAGIC # 03 Run Recommendations -# MAGIC Reads context_strategy_edges, filters by min_confidence, writes to recommendations table for dashboard/API. - -# COMMAND ---------- - -dbutils.widgets.text("catalog", "cortex_catalog", "Catalog") -dbutils.widgets.text("schema", "cortex_mvp", "Schema") -dbutils.widgets.text("min_confidence", "0.7", "Min confidence") - -# COMMAND ---------- - -CATALOG = dbutils.widgets.get("catalog") -SCHEMA = dbutils.widgets.get("schema") -MIN_CONFIDENCE = float(dbutils.widgets.get("min_confidence")) - -# COMMAND ---------- - -from pyspark.sql import functions as F - -# COMMAND ---------- - -# Read edges above threshold -edges_df = spark.table(f"{CATALOG}.{SCHEMA}.context_strategy_edges").filter( - F.col("confidence") >= MIN_CONFIDENCE -) - -# COMMAND ---------- - -# Build recommendations: recommendation_id (unique per context_id, strategy_id), context_id, strategy_id, confidence, evidence_summary, surface, created_at -recommendations_df = edges_df.select( - F.concat(F.col("context_id"), F.lit("_"), F.col("strategy_id")).alias("recommendation_id"), - F.col("context_id"), - F.col("strategy_id"), - F.col("confidence"), - F.concat( - F.lit("success_count="), - F.col("success_count").cast("string"), - F.lit(", failure_count="), - F.col("failure_count").cast("string"), - ).alias("evidence_summary"), - F.lit("dashboard").alias("surface"), - F.current_timestamp().alias("created_at"), -) - -# COMMAND ---------- - -# Write recommendations (overwrite for demo) -recommendations_df.write.mode("overwrite").saveAsTable(f"{CATALOG}.{SCHEMA}.recommendations") - -# COMMAND ---------- - -# Verify -display( - spark.sql(f"SELECT COUNT(*) as total_recommendations FROM {CATALOG}.{SCHEMA}.recommendations") -) -display( - spark.sql(f"SELECT * FROM {CATALOG}.{SCHEMA}.recommendations ORDER BY confidence DESC LIMIT 10") -) diff --git a/_contrib/cortexdbx/scripts/run_e2e_local.py b/_contrib/cortexdbx/scripts/run_e2e_local.py deleted file mode 100644 index 938c1b7..0000000 --- a/_contrib/cortexdbx/scripts/run_e2e_local.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 -""" -Run CortexDBx unit and e2e tests locally and print summary. - -Usage: from repo root: python cortexdbx/scripts/run_e2e_local.py - or: cd cortex && python cortexdbx/scripts/run_e2e_local.py -""" - -import os -import subprocess -import sys - - -def main(): - script_dir = os.path.dirname(os.path.abspath(__file__)) - repo_root = os.path.dirname(os.path.dirname(script_dir)) - os.chdir(repo_root) - - result = subprocess.run( - [sys.executable, "-m", "pytest", "cortexdbx/tests/", "-v", "--tb=short"], - capture_output=True, - text=True, - ) - print(result.stdout) - if result.stderr: - print(result.stderr, file=sys.stderr) - - if result.returncode == 0: - print("All tests passed.") - else: - print(f"Some tests failed (exit {result.returncode}).") - return result.returncode - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/_contrib/cortexdbx/scripts/run_e2e_local.sh b/_contrib/cortexdbx/scripts/run_e2e_local.sh deleted file mode 100755 index 769805b..0000000 --- a/_contrib/cortexdbx/scripts/run_e2e_local.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -# Run CortexDBx unit tests and e2e tests locally (validate before Databricks). -# Usage: from repo root: ./cortexdbx/scripts/run_e2e_local.sh -# or: cd cortex && ../cortexdbx/scripts/run_e2e_local.sh - -set -e -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" -cd "$REPO_ROOT" - -echo "Running CortexDBx tests (unit + e2e)..." -python -m pytest cortexdbx/tests/ -v --tb=short -EXIT_CODE=$? -if [ $EXIT_CODE -eq 0 ]; then - echo "All tests passed." -else - echo "Some tests failed (exit $EXIT_CODE)." -fi -exit $EXIT_CODE diff --git a/_contrib/cortexdbx/sdk/__init__.py b/_contrib/cortexdbx/sdk/__init__.py deleted file mode 100644 index 7c8e067..0000000 --- a/_contrib/cortexdbx/sdk/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""CortexDBx Python SDK.""" - -from cortexdbx.sdk.client import CortexDBxClient, CortexDBxConfig - -__all__ = ["CortexDBxClient", "CortexDBxConfig"] diff --git a/_contrib/cortexdbx/sdk/client.py b/_contrib/cortexdbx/sdk/client.py deleted file mode 100644 index 64fe836..0000000 --- a/_contrib/cortexdbx/sdk/client.py +++ /dev/null @@ -1,348 +0,0 @@ -""" -CortexDBx Python SDK. - -Works with local in-memory backend (for dev/tests) or Spark/Delta (Databricks). -""" - -import hashlib -import json -import uuid -from dataclasses import dataclass -from datetime import datetime -from typing import Any, Dict, List, Optional - - -@dataclass -class CortexDBxConfig: - """Configuration for CortexDBx client.""" - - catalog: str = "cortex_catalog" - schema: str = "cortex_mvp" - use_local_backend: bool = True - - -class LocalBackend: - """In-memory backend for dev and testing.""" - - def __init__(self) -> None: - self.contexts: Dict[str, Dict[str, Any]] = {} - self.strategies: Dict[str, Dict[str, Any]] = {} - self.outcomes: List[Dict[str, Any]] = [] - self._calibration: Dict[tuple, Dict[str, Any]] = {} - - def ensure_context(self, context_id: str, context_hash: str, domain: str, factors: str) -> str: - if context_id not in self.contexts: - self.contexts[context_id] = { - "context_id": context_id, - "context_hash": context_hash, - "domain": domain, - "factors": factors, - "first_seen": datetime.now().isoformat(), - "last_seen": datetime.now().isoformat(), - } - return context_id - - def ensure_strategy(self, strategy_id: str, name: str, domain: str) -> str: - if strategy_id not in self.strategies: - self.strategies[strategy_id] = { - "strategy_id": strategy_id, - "name": name, - "domain": domain, - "created_at": datetime.now().isoformat(), - } - return strategy_id - - def append_outcome(self, outcome: Dict[str, Any]) -> None: - self.outcomes.append(outcome) - ctx = outcome["context_id"] - strat = outcome["strategy_id"] - result = outcome["result"] - key = (ctx, strat) - if key not in self._calibration: - self._calibration[key] = { - "alpha": 1.0, - "beta": 1.0, - "success_count": 0, - "failure_count": 0, - "partial_count": 0, - } - s = self._calibration[key] - if result == "SUCCESS": - s["alpha"] += 1.0 - s["success_count"] += 1 - elif result == "PARTIAL": - s["alpha"] += 0.5 - s["beta"] += 0.5 - s["partial_count"] += 1 - else: - s["beta"] += 1.0 - s["failure_count"] += 1 - - def get_recommendations( - self, context_hash: str, min_confidence: float, limit: int - ) -> List[Dict[str, Any]]: - context_id = None - for cid, c in self.contexts.items(): - if c["context_hash"] == context_hash: - context_id = cid - break - if not context_id: - return [] - - recs = [] - for (ctx_id, strat_id), cal in self._calibration.items(): - if ctx_id != context_id: - continue - conf = cal["alpha"] / (cal["alpha"] + cal["beta"]) - if conf >= min_confidence: - strat = self.strategies.get(strat_id, {"name": strat_id}) - recs.append( - { - "strategy_id": strat_id, - "strategy_name": strat.get("name", strat_id), - "confidence": conf, - "evidence_count": cal["success_count"] - + cal["failure_count"] - + cal["partial_count"], - "explanation": f"{conf:.0%} confidence based on {cal['success_count']} successes and {cal['failure_count']} failures", - } - ) - recs.sort(key=lambda x: x["confidence"], reverse=True) - return recs[:limit] - - def get_confidence(self, context_hash: str, strategy_name: str) -> Optional[tuple]: - context_id = None - for cid, c in self.contexts.items(): - if c["context_hash"] == context_hash: - context_id = cid - break - if not context_id: - return None - - for strat_id, strat in self.strategies.items(): - if strat.get("name") == strategy_name: - key = (context_id, strat_id) - if key in self._calibration: - cal = self._calibration[key] - conf = cal["alpha"] / (cal["alpha"] + cal["beta"]) - exp = f"{conf:.0%} confidence based on {cal['success_count']} successes and {cal['failure_count']} failures" - return (conf, exp) - return (0.5, "No historical data for this context/strategy") - return None - - -class CortexDBxClient: - """ - Python SDK for CortexDBx. - - With use_local_backend=True (default): in-memory store for dev/tests. - With use_local_backend=False: expects Spark session for Delta/UC. - """ - - def __init__( - self, - config: Optional[CortexDBxConfig] = None, - spark_session: Optional[Any] = None, - ): - self.config = config or CortexDBxConfig() - self._spark = spark_session - self._local = LocalBackend() if self.config.use_local_backend else None - - def _fingerprint_context(self, context: Dict[str, Any]) -> str: - normalized = json.dumps(sorted(context.items())) - return hashlib.sha256(normalized.encode()).hexdigest()[:16] - - def _ensure_context(self, context: Dict[str, Any], context_hash: str) -> str: - domain = context.get("domain", "unknown") - factors = json.dumps([{"key": k, "value": str(v)} for k, v in sorted(context.items())]) - context_id = f"ctx_{context_hash}" - - if self._local: - self._local.ensure_context(context_id, context_hash, domain, factors) - return context_id - - if self._spark is None: - raise RuntimeError("Spark session required when use_local_backend=False") - table = f"{self.config.catalog}.{self.config.schema}.contexts" - existing = self._spark.sql( - f"SELECT context_id FROM {table} WHERE context_hash = '{context_hash}'" - ).collect() - if existing: - return existing[0]["context_id"] - row = { - "context_id": context_id, - "context_hash": context_hash, - "domain": domain, - "factors": factors, - "first_seen": datetime.now().isoformat(), - "last_seen": datetime.now().isoformat(), - } - df = self._spark.createDataFrame([row]) - df.write.mode("append").saveAsTable(table) - return context_id - - def _ensure_strategy(self, strategy: str) -> str: - strategy_id = f"strat_{hashlib.sha256(strategy.encode()).hexdigest()[:8]}" - - if self._local: - self._local.ensure_strategy(strategy_id, strategy, "unknown") - return strategy_id - - if self._spark is None: - raise RuntimeError("Spark session required when use_local_backend=False") - table = f"{self.config.catalog}.{self.config.schema}.strategies" - existing = self._spark.sql( - f"SELECT strategy_id FROM {table} WHERE name = '{strategy}' OR strategy_id = '{strategy_id}'" - ).collect() - if existing: - return existing[0]["strategy_id"] - row = { - "strategy_id": strategy_id, - "name": strategy, - "description": None, - "domain": "unknown", - "category": "user_defined", - "created_at": datetime.now().isoformat(), - } - df = self._spark.createDataFrame([row]) - df.write.mode("append").saveAsTable(table) - return strategy_id - - def log_outcome( - self, - context: Dict[str, Any], - strategy: str, - result: str, - evidence: Optional[Dict[str, Any]] = None, - notes: Optional[str] = None, - ) -> str: - """ - Log an outcome to CortexDBx. - - Args: - context: Dictionary of context factors - strategy: Name or ID of strategy used - result: 'SUCCESS', 'FAILURE', or 'PARTIAL' - evidence: Optional evidence linking to source data - notes: Optional human notes - - Returns: - outcome_id: ID of created outcome - """ - context_hash = self._fingerprint_context(context) - outcome_id = str(uuid.uuid4()) - context_id = self._ensure_context(context, context_hash) - strategy_id = self._ensure_strategy(strategy) - result_upper = result.upper() - if result_upper not in ("SUCCESS", "FAILURE", "PARTIAL"): - result_upper = "PARTIAL" - - outcome = { - "outcome_id": outcome_id, - "context_id": context_id, - "strategy_id": strategy_id, - "result": result_upper, - "evidence": json.dumps(evidence or {}), - "notes": notes, - "actor": "sdk_user", - "created_at": datetime.now().isoformat(), - } - - if self._local: - self._local.append_outcome(outcome) - return outcome_id - - if self._spark is None: - raise RuntimeError("Spark session required when use_local_backend=False") - table = f"{self.config.catalog}.{self.config.schema}.outcomes" - df = self._spark.createDataFrame([outcome]) - df.write.mode("append").saveAsTable(table) - return outcome_id - - def recommend( - self, - context: Dict[str, Any], - min_confidence: float = 0.5, - limit: int = 5, - ) -> List[Dict[str, Any]]: - """ - Get recommendations for a context. - - Args: - context: Dictionary of context factors - min_confidence: Minimum confidence threshold - limit: Maximum recommendations to return - - Returns: - List of recommendations with confidence and evidence - """ - context_hash = self._fingerprint_context(context) - - if self._local: - return self._local.get_recommendations(context_hash, min_confidence, limit) - - if self._spark is None: - raise RuntimeError("Spark session required when use_local_backend=False") - table_prefix = f"{self.config.catalog}.{self.config.schema}" - query = f""" - SELECT - s.strategy_id, - s.name as strategy_name, - s.description, - e.confidence, - e.success_count, - e.failure_count - FROM {table_prefix}.context_strategy_edges e - JOIN {table_prefix}.contexts c ON e.context_id = c.context_id - JOIN {table_prefix}.strategies s ON e.strategy_id = s.strategy_id - WHERE c.context_hash = '{context_hash}' - AND e.confidence >= {min_confidence} - ORDER BY e.confidence DESC - LIMIT {limit} - """ - rows = self._spark.sql(query).collect() - return [ - { - "strategy_id": r["strategy_id"], - "strategy_name": r["strategy_name"], - "description": r["description"], - "confidence": r["confidence"], - "evidence_count": r["success_count"] + r["failure_count"], - "explanation": f"{r['confidence']:.0%} confidence based on {r['success_count']} successes and {r['failure_count']} failures", - } - for r in rows - ] - - def get_confidence(self, context: Dict[str, Any], strategy: str) -> tuple: - """ - Get confidence for a specific strategy in a context. - - Returns: - (confidence, explanation) or (0.5, "No historical data...") - """ - context_hash = self._fingerprint_context(context) - - if self._local: - result = self._local.get_confidence(context_hash, strategy) - if result: - return result - return 0.5, "No historical data for this context/strategy" - - if self._spark is None: - raise RuntimeError("Spark session required when use_local_backend=False") - table_prefix = f"{self.config.catalog}.{self.config.schema}" - query = f""" - SELECT e.confidence, e.success_count, e.failure_count - FROM {table_prefix}.context_strategy_edges e - JOIN {table_prefix}.contexts c ON e.context_id = c.context_id - JOIN {table_prefix}.strategies s ON e.strategy_id = s.strategy_id - WHERE c.context_hash = '{context_hash}' AND s.name = '{strategy}' - """ - rows = self._spark.sql(query).collect() - if not rows: - return 0.5, "No historical data for this context/strategy" - r = rows[0] - return r["confidence"], ( - f"{r['confidence']:.0%} confidence based on " - f"{r['success_count']} successes and {r['failure_count']} failures" - ) diff --git a/_contrib/cortexdbx/synthetic/__init__.py b/_contrib/cortexdbx/synthetic/__init__.py deleted file mode 100644 index 5ae420a..0000000 --- a/_contrib/cortexdbx/synthetic/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Synthetic data generation for CortexDBx MVP testing.""" - -from cortexdbx.synthetic.generator import ( - DOMAIN_CONFIGS, - DomainConfig, - SyntheticDataGenerator, -) - -__all__ = ["SyntheticDataGenerator", "DOMAIN_CONFIGS", "DomainConfig"] diff --git a/_contrib/cortexdbx/synthetic/generator.py b/_contrib/cortexdbx/synthetic/generator.py deleted file mode 100644 index 8cdaf0f..0000000 --- a/_contrib/cortexdbx/synthetic/generator.py +++ /dev/null @@ -1,281 +0,0 @@ -""" -Synthetic data generator for CortexDBx MVP. - -Generates realistic outcomes across 6 domains for dev and testing. -""" - -import hashlib -import json -import random -import uuid -from dataclasses import dataclass -from datetime import datetime, timedelta -from typing import Any, Dict, Generator, List, Optional - - -@dataclass -class DomainConfig: - """Domain-specific configuration for realistic data generation.""" - - context_factors: List[str] - strategy_types: List[str] - outcome_weights: Dict[str, float] - - -DOMAIN_CONFIGS: Dict[str, DomainConfig] = { - "fraud_investigation": DomainConfig( - context_factors=[ - "alert_type", - "transaction_amount_bucket", - "account_age_bucket", - "swift_code_region", - "time_of_day", - "device_fingerprint_match", - "historical_fraud_rate", - "velocity_score", - ], - strategy_types=[ - "auto_close_low_risk", - "escalate_to_analyst", - "freeze_account", - "request_verification", - "manual_review_queue", - "ml_rescore", - ], - outcome_weights={"SUCCESS": 0.70, "FAILURE": 0.20, "PARTIAL": 0.10}, - ), - "clinical_trial": DomainConfig( - context_factors=[ - "trial_phase", - "therapeutic_area", - "inclusion_criteria_count", - "site_location", - "patient_demographics", - "prior_trial_history", - "enrollment_velocity", - "dropout_rate", - ], - strategy_types=[ - "relax_bmi_criteria", - "add_recruitment_site", - "extend_age_range", - "modify_exclusion_criteria", - "increase_compensation", - "digital_outreach", - ], - outcome_weights={"SUCCESS": 0.55, "FAILURE": 0.30, "PARTIAL": 0.15}, - ), - "maintenance": DomainConfig( - context_factors=[ - "equipment_type", - "sensor_reading_pattern", - "operating_hours", - "last_maintenance_days", - "failure_history", - "manufacturer", - "environmental_conditions", - "criticality_level", - ], - strategy_types=[ - "replace_bearing", - "check_alignment", - "lubrication_cycle", - "full_inspection", - "sensor_recalibration", - "schedule_shutdown", - ], - outcome_weights={"SUCCESS": 0.65, "FAILURE": 0.25, "PARTIAL": 0.10}, - ), - "marketing_campaign": DomainConfig( - context_factors=[ - "audience_segment", - "channel", - "creative_type", - "offer_type", - "time_of_year", - "competitor_activity", - "budget_tier", - "campaign_duration", - ], - strategy_types=[ - "urgency_messaging", - "value_messaging", - "social_proof", - "personalization", - "retargeting", - "influencer_partnership", - ], - outcome_weights={"SUCCESS": 0.45, "FAILURE": 0.35, "PARTIAL": 0.20}, - ), - "security_incident": DomainConfig( - context_factors=[ - "alert_severity", - "attack_vector", - "affected_systems", - "time_to_detection", - "attacker_sophistication", - "data_sensitivity", - "business_hours", - "prior_incident_similarity", - ], - strategy_types=[ - "block_ip", - "geo_block_rate_limit", - "isolate_system", - "credential_rotation", - "forensic_capture", - "executive_notification", - ], - outcome_weights={"SUCCESS": 0.60, "FAILURE": 0.25, "PARTIAL": 0.15}, - ), - "supply_chain": DomainConfig( - context_factors=[ - "product_category", - "origin_region", - "destination_region", - "shipping_mode", - "weather_conditions", - "supplier_tier", - "demand_urgency", - "customs_complexity", - ], - strategy_types=[ - "primary_supplier", - "backup_supplier", - "air_freight_upgrade", - "split_shipment", - "local_warehouse", - "expedited_customs", - ], - outcome_weights={"SUCCESS": 0.55, "FAILURE": 0.30, "PARTIAL": 0.15}, - ), -} - - -class SyntheticDataGenerator: - """Generate realistic synthetic data for CortexDBx MVP.""" - - def __init__(self, domain: str, seed: Optional[int] = 42): - if domain not in DOMAIN_CONFIGS: - raise ValueError(f"Unknown domain: {domain}. Valid: {list(DOMAIN_CONFIGS.keys())}") - self.domain = domain - self.config = DOMAIN_CONFIGS[domain] - random.seed(seed) - self._strategies: Dict[str, Dict[str, Any]] = {} - self._generate_strategies() - - def _generate_strategies(self) -> None: - """Pre-generate strategies with inherent success rates.""" - for i, name in enumerate(self.config.strategy_types): - base_success_rate = random.uniform(0.3, 0.9) - sid = f"strategy_{self.domain}_{i}" - self._strategies[sid] = { - "strategy_id": sid, - "name": name, - "domain": self.domain, - "inherent_success_rate": base_success_rate, - "category": random.choice(["primary", "fallback", "experimental"]), - } - - def _generate_context(self) -> Dict[str, Any]: - """Generate a random context.""" - factors = [] - for factor_name in self.config.context_factors: - if "bucket" in factor_name or "tier" in factor_name: - value = random.choice(["low", "medium", "high"]) - elif "rate" in factor_name or "score" in factor_name: - value = str(round(random.uniform(0, 1), 2)) - elif "count" in factor_name: - value = str(random.randint(1, 20)) - elif "days" in factor_name or "hours" in factor_name: - value = str(random.randint(1, 365)) - else: - value = f"value_{random.randint(1, 10)}" - factors.append({"key": factor_name, "value": value}) - - factors_str = json.dumps(sorted(factors, key=lambda x: x["key"])) - context_hash = hashlib.sha256(factors_str.encode()).hexdigest()[:16] - return { - "context_id": f"ctx_{context_hash}", - "context_hash": context_hash, - "domain": self.domain, - "factors": factors, - } - - def _generate_outcome( - self, context: Dict[str, Any], strategy: Dict[str, Any], timestamp: datetime - ) -> Dict[str, Any]: - """Generate an outcome based on strategy inherent success rate.""" - inherent_rate = strategy["inherent_success_rate"] - effective_rate = inherent_rate + random.gauss(0, 0.1) - effective_rate = max(0.05, min(0.95, effective_rate)) - - roll = random.random() - if roll < effective_rate: - result = "SUCCESS" - elif roll < effective_rate + 0.15: - result = "PARTIAL" - else: - result = "FAILURE" - - return { - "outcome_id": str(uuid.uuid4()), - "context_id": context["context_id"], - "strategy_id": strategy["strategy_id"], - "result": result, - "evidence": json.dumps( - { - "source": "synthetic_generator", - "context_factors": context["factors"][:3], - "strategy_name": strategy["name"], - } - ), - "actor": f"synthetic_user_{random.randint(1, 10)}", - "created_at": timestamp.isoformat(), - } - - def generate_dataset( - self, num_outcomes: int, days_span: int = 90 - ) -> Generator[Dict[str, Any], None, None]: - """Generate a complete synthetic dataset.""" - num_contexts = max(1, min(num_outcomes // 10, 500)) - contexts = [self._generate_context() for _ in range(num_contexts)] - start_date = datetime.now() - timedelta(days=days_span) - strategies_list = list(self._strategies.values()) - - for _ in range(num_outcomes): - random_days = random.uniform(0, days_span) - timestamp = start_date + timedelta(days=random_days) - context = random.choice(contexts) - strategy = random.choice(strategies_list) - outcome = self._generate_outcome(context, strategy, timestamp) - yield { - "context": context, - "strategy": strategy, - "outcome": outcome, - } - - def get_ground_truth(self) -> Dict[str, float]: - """Return ground truth success rates for validation.""" - return {s["strategy_id"]: s["inherent_success_rate"] for s in self._strategies.values()} - - @property - def strategies(self) -> Dict[str, Dict[str, Any]]: - """Return strategy definitions.""" - return dict(self._strategies) - - -def generate_all_domains( - outcomes_per_domain: int = 1000, -) -> Dict[str, Dict[str, Any]]: - """Generate synthetic data for all 6 use cases.""" - all_data: Dict[str, Dict[str, Any]] = {} - for domain in DOMAIN_CONFIGS: - generator = SyntheticDataGenerator(domain) - data = list(generator.generate_dataset(outcomes_per_domain)) - all_data[domain] = { - "outcomes": data, - "ground_truth": generator.get_ground_truth(), - "strategies": generator.strategies, - } - return all_data diff --git a/_contrib/cortexdbx/tests/__init__.py b/_contrib/cortexdbx/tests/__init__.py deleted file mode 100644 index ca78405..0000000 --- a/_contrib/cortexdbx/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""CortexDBx tests.""" diff --git a/_contrib/cortexdbx/tests/conftest.py b/_contrib/cortexdbx/tests/conftest.py deleted file mode 100644 index 3fd559a..0000000 --- a/_contrib/cortexdbx/tests/conftest.py +++ /dev/null @@ -1,144 +0,0 @@ -""" -Shared pytest fixtures for CortexDBx tests. - -Provides test fixtures for: -- CortexDBx clients (local backend) -- Synthetic data generators -- Calibration engines -- Agent orchestrators -- Temporary data directories - -Aligned with cortex/tests/conftest.py testing framework. -""" - -import sys -import tempfile -from pathlib import Path - -import pytest - -# Add parent directories to path for imports -sys.path.insert(0, str(Path(__file__).parent.parent.parent)) - -from cortexdbx.agents.orchestrator import AgentOrchestrator -from cortexdbx.calibration.engine import CalibrationEngine -from cortexdbx.sdk.client import CortexDBxClient -from cortexdbx.synthetic.generator import SyntheticDataGenerator - -# ============================================================================ -# Client Fixtures -# ============================================================================ - - -@pytest.fixture -def client(): - """Create a fresh CortexDBxClient with local backend.""" - return CortexDBxClient() - - -@pytest.fixture -def orchestrator(): - """Create an AgentOrchestrator for testing.""" - orch = AgentOrchestrator(max_workers=2) - yield orch - orch.shutdown() - - -# ============================================================================ -# Generator Fixtures -# ============================================================================ - - -@pytest.fixture -def fraud_generator(): - """Create a generator for fraud_investigation domain.""" - return SyntheticDataGenerator("fraud_investigation", seed=42) - - -@pytest.fixture -def clinical_generator(): - """Create a generator for clinical_trial domain.""" - return SyntheticDataGenerator("clinical_trial", seed=42) - - -@pytest.fixture( - params=[ - "fraud_investigation", - "clinical_trial", - "maintenance", - "marketing_campaign", - "security_incident", - "supply_chain", - ] -) -def domain_generator(request): - """Parametrized generator for all 6 domains.""" - return SyntheticDataGenerator(request.param, seed=42) - - -# ============================================================================ -# Calibration Fixtures -# ============================================================================ - - -@pytest.fixture -def calibration_engine(): - """Create a fresh CalibrationEngine.""" - return CalibrationEngine() - - -@pytest.fixture -def seeded_calibration_engine(fraud_generator): - """Create a CalibrationEngine pre-seeded with fraud data.""" - engine = CalibrationEngine() - data = list(fraud_generator.generate_dataset(100)) - - for record in data: - engine.update( - record["context"]["context_id"], - record["strategy"]["strategy_id"], - record["outcome"]["result"], - ) - - return engine - - -# ============================================================================ -# Data Fixtures -# ============================================================================ - - -@pytest.fixture -def sample_outcomes(fraud_generator): - """Generate sample outcomes for testing.""" - data = list(fraud_generator.generate_dataset(50)) - return [ - { - "context_id": r["context"]["context_id"], - "strategy_id": r["strategy"]["strategy_id"], - "result": r["outcome"]["result"], - "domain": r["context"]["domain"], - } - for r in data - ] - - -@pytest.fixture -def temp_data_dir(): - """Create a temporary directory for test data.""" - with tempfile.TemporaryDirectory() as tmpdir: - yield Path(tmpdir) - - -# ============================================================================ -# Markers -# ============================================================================ - - -def pytest_configure(config): - """Register custom markers.""" - config.addinivalue_line( - "markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')" - ) - config.addinivalue_line("markers", "e2e: marks tests as end-to-end integration tests") - config.addinivalue_line("markers", "databricks: marks tests requiring Databricks connection") diff --git a/_contrib/cortexdbx/tests/test_calibration.py b/_contrib/cortexdbx/tests/test_calibration.py deleted file mode 100644 index f839a50..0000000 --- a/_contrib/cortexdbx/tests/test_calibration.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Tests for calibration engine.""" - -from cortexdbx.calibration.engine import CalibrationEngine, CalibrationState -from cortexdbx.synthetic.generator import SyntheticDataGenerator - - -def test_calibration_state_confidence_no_evidence(): - state = CalibrationState(alpha=1.0, beta=1.0, success_count=0, failure_count=0, partial_count=0) - assert state.confidence == 0.5 - assert state.evidence_count == 0 - - -def test_calibration_state_confidence_after_success(): - state = CalibrationState(alpha=2.0, beta=1.0, success_count=1, failure_count=0, partial_count=0) - assert state.confidence == 2.0 / 3.0 - - -def test_calibration_engine_update_success(): - engine = CalibrationEngine(prior_alpha=1.0, prior_beta=1.0) - state = engine.update("ctx_1", "strat_1", "SUCCESS") - assert state.success_count == 1 - assert state.failure_count == 0 - assert state.confidence > 0.5 - - -def test_calibration_engine_update_failure(): - engine = CalibrationEngine(prior_alpha=1.0, prior_beta=1.0) - state = engine.update("ctx_1", "strat_1", "FAILURE") - assert state.success_count == 0 - assert state.failure_count == 1 - assert state.confidence < 0.5 - - -def test_calibration_engine_update_partial(): - engine = CalibrationEngine(prior_alpha=1.0, prior_beta=1.0) - engine.update("ctx_1", "strat_1", "PARTIAL") - state = engine.get_state("ctx_1", "strat_1") - assert state.partial_count == 1 - assert state.alpha == 1.5 - assert state.beta == 1.5 - - -def test_calibration_engine_get_confidence_no_data(): - engine = CalibrationEngine() - conf, explanation = engine.get_confidence("ctx_x", "strat_x") - assert conf == 0.5 - assert "No historical data" in explanation - - -def test_calibration_engine_ingest_outcomes(): - engine = CalibrationEngine() - outcomes = [ - {"context_id": "c1", "strategy_id": "s1", "result": "SUCCESS"}, - {"context_id": "c1", "strategy_id": "s1", "result": "FAILURE"}, - {"context_id": "c1", "strategy_id": "s1", "result": "SUCCESS"}, - ] - count = engine.ingest_outcomes(outcomes) - assert count == 3 - conf, _ = engine.get_confidence("c1", "s1") - assert 0.4 < conf < 0.7 - - -def test_calibration_engine_evaluate_against_ground_truth(): - gen = SyntheticDataGenerator("fraud_investigation", seed=42) - data = list(gen.generate_dataset(200)) - ground_truth = gen.get_ground_truth() - - engine = CalibrationEngine() - outcomes = [ - { - "context_id": r["context"]["context_id"], - "strategy_id": r["strategy"]["strategy_id"], - "result": r["outcome"]["result"], - } - for r in data - ] - engine.ingest_outcomes(outcomes) - - metrics = engine.evaluate_calibration(ground_truth) - assert "error" not in metrics or metrics.get("num_evaluated", 0) > 0 - if metrics.get("num_evaluated", 0) > 0: - assert "brier_score" in metrics - assert "mean_absolute_error" in metrics - assert 0 <= metrics["brier_score"] <= 1 - assert 0 <= metrics["mean_absolute_error"] <= 1 - - -def test_calibration_get_top_strategies(): - engine = CalibrationEngine() - engine.update("ctx_1", "strat_high", "SUCCESS") - engine.update("ctx_1", "strat_high", "SUCCESS") - engine.update("ctx_1", "strat_low", "FAILURE") - engine.update("ctx_1", "strat_low", "FAILURE") - - top = engine.get_top_strategies("ctx_1", min_confidence=0.0, limit=5) - assert len(top) == 2 - assert top[0][1] >= top[1][1] - assert top[0][0] == "strat_high" diff --git a/_contrib/cortexdbx/tests/test_e2e_learning_loop.py b/_contrib/cortexdbx/tests/test_e2e_learning_loop.py deleted file mode 100644 index cc58847..0000000 --- a/_contrib/cortexdbx/tests/test_e2e_learning_loop.py +++ /dev/null @@ -1,102 +0,0 @@ -""" -End-to-end test: full CortexDBx learning loop (generate, ingest, calibrate, recommend). - -Validates MVP success criteria locally before Databricks deployment. -""" - -from cortexdbx.agents.orchestrator import AgentOrchestrator -from cortexdbx.sdk.client import CortexDBxClient -from cortexdbx.synthetic.generator import SyntheticDataGenerator - - -def test_e2e_learning_loop_orchestrator(): - """ - Full learning loop via AgentOrchestrator: generate -> ingest -> calibrate -> recommend. - - Success criteria: Brier < 0.25, num_evaluated >= 1, at least one recommendation - for a seen context with confidence in [0, 1]. - """ - domain = "fraud_investigation" - num_outcomes = 800 - seed = 42 - - # 1. Generate dataset - generator = SyntheticDataGenerator(domain, seed=seed) - data = list(generator.generate_dataset(num_outcomes)) - assert len(data) >= num_outcomes - - # 2. Build outcomes list (context_id, strategy_id, result, domain) - outcomes = [] - for r in data: - o = { - "context_id": r["context"]["context_id"], - "strategy_id": r["strategy"]["strategy_id"], - "result": r["outcome"]["result"], - "domain": r["context"]["domain"], - } - outcomes.append(o) - - # 3. Ingest via orchestrator - orchestrator = AgentOrchestrator(max_workers=2) - result = orchestrator.process_outcomes(outcomes) - assert result["total_processed"] == len(outcomes) - assert result["total_ingested"] == len(outcomes) - - # 4. Ground truth and calibration evaluation - ground_truth = generator.get_ground_truth() - metrics = orchestrator.evaluate_calibration(ground_truth) - assert "error" not in metrics or metrics.get("num_evaluated", 0) > 0 - assert metrics.get("num_evaluated", 0) >= 1, "At least one strategy should have enough evidence" - if metrics.get("brier_score") is not None: - assert metrics["brier_score"] < 0.25, ( - f"Brier score {metrics['brier_score']} should be < 0.25" - ) - - # 5. Recommendations for a known context from the dataset - sample_context_id = data[0]["context"]["context_id"] - recs = orchestrator.get_recommendations(sample_context_id, min_confidence=0.5, limit=10) - assert len(recs) >= 1, "Should have at least one recommendation for a seen context" - for strategy_id, confidence in recs: - assert 0 <= confidence <= 1, f"Confidence {confidence} should be in [0, 1]" - - orchestrator.shutdown() - - -def test_e2e_learning_loop_sdk_local(): - """ - Full learning loop via CortexDBxClient (local backend): log outcomes -> recommend. - - Validates SDK path: log_outcome in a loop, then recommend and assert non-empty - and sane confidence. - """ - client = CortexDBxClient() - domain = "clinical_trial" - num_outcomes = 300 - seed = 123 - - generator = SyntheticDataGenerator(domain, seed=seed) - data = list(generator.generate_dataset(num_outcomes)) - assert len(data) >= num_outcomes - - # Log outcomes via SDK (context dict, strategy name, result) - for r in data: - ctx = {f["key"]: f["value"] for f in r["context"]["factors"]} - ctx["domain"] = r["context"]["domain"] - client.log_outcome( - context=ctx, - strategy=r["strategy"]["name"], - result=r["outcome"]["result"], - evidence={"source": "e2e_test"}, - ) - - # Use same context factors as one of the records to get recommendations - sample = data[0] - ctx = {f["key"]: f["value"] for f in sample["context"]["factors"]} - ctx["domain"] = sample["context"]["domain"] - - recs = client.recommend(ctx, min_confidence=0.3, limit=5) - assert len(recs) >= 1, "SDK should return at least one recommendation after logging" - for rec in recs: - assert "confidence" in rec - assert 0 <= rec["confidence"] <= 1 - assert "strategy_name" in rec or "strategy_id" in rec diff --git a/_contrib/cortexdbx/tests/test_generator.py b/_contrib/cortexdbx/tests/test_generator.py deleted file mode 100644 index ed53213..0000000 --- a/_contrib/cortexdbx/tests/test_generator.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Tests for synthetic data generator.""" - -import pytest -from cortexdbx.synthetic.generator import ( - DOMAIN_CONFIGS, - SyntheticDataGenerator, - generate_all_domains, -) - - -def test_domain_configs_has_six_domains(): - assert len(DOMAIN_CONFIGS) == 6 - assert "fraud_investigation" in DOMAIN_CONFIGS - assert "clinical_trial" in DOMAIN_CONFIGS - assert "maintenance" in DOMAIN_CONFIGS - assert "marketing_campaign" in DOMAIN_CONFIGS - assert "security_incident" in DOMAIN_CONFIGS - assert "supply_chain" in DOMAIN_CONFIGS - - -def test_generator_requires_valid_domain(): - with pytest.raises(ValueError, match="Unknown domain"): - SyntheticDataGenerator("invalid_domain") - - -def test_generator_produces_context_with_required_fields(): - gen = SyntheticDataGenerator("fraud_investigation", seed=42) - data = list(gen.generate_dataset(5)) - assert len(data) == 5 - for record in data: - ctx = record["context"] - assert "context_id" in ctx - assert "context_hash" in ctx - assert "domain" in ctx - assert ctx["domain"] == "fraud_investigation" - assert "factors" in ctx - assert len(ctx["factors"]) == len(DOMAIN_CONFIGS["fraud_investigation"].context_factors) - - -def test_generator_produces_outcome_with_required_fields(): - gen = SyntheticDataGenerator("clinical_trial", seed=123) - data = list(gen.generate_dataset(10)) - for record in data: - outcome = record["outcome"] - assert "outcome_id" in outcome - assert "context_id" in outcome - assert "strategy_id" in outcome - assert outcome["result"] in ("SUCCESS", "FAILURE", "PARTIAL") - assert "evidence" in outcome - assert "created_at" in outcome - - -def test_generator_ground_truth_matches_strategies(): - gen = SyntheticDataGenerator("maintenance", seed=1) - gt = gen.get_ground_truth() - assert len(gt) == len(DOMAIN_CONFIGS["maintenance"].strategy_types) - for sid, rate in gt.items(): - assert 0 <= rate <= 1 - assert sid.startswith("strategy_maintenance_") - - -def test_generator_reproducible_with_same_seed(): - gen1 = SyntheticDataGenerator("supply_chain", seed=99) - data1 = list(gen1.generate_dataset(20)) - gen2 = SyntheticDataGenerator("supply_chain", seed=99) - data2 = list(gen2.generate_dataset(20)) - assert len(data1) == len(data2) == 20 - results1 = [r["outcome"]["result"] for r in data1] - results2 = [r["outcome"]["result"] for r in data2] - assert results1 == results2 - strategies1 = [r["outcome"]["strategy_id"] for r in data1] - strategies2 = [r["outcome"]["strategy_id"] for r in data2] - assert strategies1 == strategies2 - - -def test_generate_all_domains(): - all_data = generate_all_domains(outcomes_per_domain=50) - assert len(all_data) == 6 - for domain, payload in all_data.items(): - assert "outcomes" in payload - assert "ground_truth" in payload - assert "strategies" in payload - assert len(payload["outcomes"]) == 50 - assert len(payload["ground_truth"]) > 0 - assert len(payload["strategies"]) > 0 - - -def test_generator_large_dataset(): - gen = SyntheticDataGenerator("security_incident", seed=0) - data = list(gen.generate_dataset(500)) - assert len(data) == 500 - results = [r["outcome"]["result"] for r in data] - assert "SUCCESS" in results - assert "FAILURE" in results - assert "PARTIAL" in results diff --git a/_contrib/cortexdbx/tests/test_orchestrator.py b/_contrib/cortexdbx/tests/test_orchestrator.py deleted file mode 100644 index 746a2b8..0000000 --- a/_contrib/cortexdbx/tests/test_orchestrator.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Tests for agent orchestrator.""" - -from cortexdbx.agents.orchestrator import AgentOrchestrator -from cortexdbx.synthetic.generator import SyntheticDataGenerator - - -def test_orchestrator_process_outcomes(): - orch = AgentOrchestrator(max_workers=2) - outcomes = [ - { - "context_id": "c1", - "strategy_id": "s1", - "result": "SUCCESS", - "domain": "fraud_investigation", - }, - { - "context_id": "c1", - "strategy_id": "s1", - "result": "FAILURE", - "domain": "fraud_investigation", - }, - {"context_id": "c2", "strategy_id": "s2", "result": "SUCCESS", "domain": "clinical_trial"}, - ] - result = orch.process_outcomes(outcomes) - assert result["total_processed"] == 3 - assert result["total_ingested"] == 3 - assert "fraud_investigation" in result["by_domain"] - assert "clinical_trial" in result["by_domain"] - orch.shutdown() - - -def test_orchestrator_calibration_updated(): - orch = AgentOrchestrator(max_workers=2) - outcomes = [ - {"context_id": "c1", "strategy_id": "s1", "result": "SUCCESS"}, - {"context_id": "c1", "strategy_id": "s1", "result": "SUCCESS"}, - {"context_id": "c1", "strategy_id": "s1", "result": "FAILURE"}, - ] - orch.process_outcomes(outcomes) - top = orch.get_recommendations("c1", min_confidence=0.0, limit=5) - assert len(top) == 1 - assert top[0][0] == "s1" - assert top[0][1] > 0.5 - orch.shutdown() - - -def test_orchestrator_with_synthetic_data(): - gen = SyntheticDataGenerator("maintenance", seed=0) - data = list(gen.generate_dataset(100)) - outcomes = [] - for r in data: - o = r["outcome"].copy() - o["domain"] = r["context"]["domain"] - outcomes.append(o) - - orch = AgentOrchestrator(max_workers=4) - result = orch.process_outcomes(outcomes) - assert result["total_processed"] == 100 - assert result["total_ingested"] == 100 - - ground_truth = gen.get_ground_truth() - metrics = orch.evaluate_calibration(ground_truth) - assert "error" not in metrics or metrics.get("num_evaluated", 0) >= 0 - if metrics.get("num_evaluated", 0) > 0: - assert metrics["brier_score"] < 0.5 - orch.shutdown() - - -def test_orchestrator_parallel_processing(): - gen = SyntheticDataGenerator("supply_chain", seed=1) - data = list(gen.generate_dataset(200)) - outcomes = [r["outcome"].copy() for r in data] - for i, o in enumerate(outcomes): - o["domain"] = data[i]["context"]["domain"] - - orch = AgentOrchestrator(max_workers=4) - result = orch.process_outcomes_parallel(outcomes, chunk_size=50) - assert result["total_processed"] == 200 - assert result["total_ingested"] == 200 - assert result["chunks_processed"] >= 1 - orch.shutdown() diff --git a/_contrib/cortexdbx/tests/test_sdk.py b/_contrib/cortexdbx/tests/test_sdk.py deleted file mode 100644 index 61964eb..0000000 --- a/_contrib/cortexdbx/tests/test_sdk.py +++ /dev/null @@ -1,83 +0,0 @@ -"""Tests for CortexDBx SDK client (local backend).""" - -from cortexdbx.sdk.client import CortexDBxClient - - -def test_client_default_uses_local_backend(): - client = CortexDBxClient() - assert client._local is not None - - -def test_client_log_outcome_returns_id(): - client = CortexDBxClient() - outcome_id = client.log_outcome( - context={"alert_type": "fraud", "amount": "high"}, - strategy="escalate_to_analyst", - result="SUCCESS", - evidence={"alert_id": "ALT-123"}, - ) - assert outcome_id is not None - assert len(outcome_id) == 36 # UUID length with hyphens - - -def test_client_recommend_after_logging(): - client = CortexDBxClient() - ctx = {"table": "orders", "operation": "aggregate"} - client.log_outcome(ctx, "incremental_refresh", "SUCCESS") - client.log_outcome(ctx, "incremental_refresh", "SUCCESS") - client.log_outcome(ctx, "full_scan", "FAILURE") - - recs = client.recommend(ctx, min_confidence=0.3, limit=5) - assert len(recs) >= 1 - assert recs[0]["strategy_name"] == "incremental_refresh" - assert recs[0]["confidence"] > 0.5 - assert "explanation" in recs[0] - - -def test_client_recommend_empty_without_data(): - client = CortexDBxClient() - recs = client.recommend({"unknown": "context"}, min_confidence=0.5) - assert recs == [] - - -def test_client_get_confidence(): - client = CortexDBxClient() - ctx = {"domain": "test", "key": "value"} - client.log_outcome(ctx, "strategy_a", "SUCCESS") - client.log_outcome(ctx, "strategy_a", "SUCCESS") - client.log_outcome(ctx, "strategy_a", "FAILURE") - - conf, explanation = client.get_confidence(ctx, "strategy_a") - assert conf > 0.5 - assert "confidence" in explanation.lower() - assert "successes" in explanation.lower() - - -def test_client_get_confidence_no_data(): - client = CortexDBxClient() - conf, explanation = client.get_confidence({"x": "y"}, "nonexistent") - assert conf == 0.5 - assert "No historical data" in explanation - - -def test_client_same_context_same_hash(): - client = CortexDBxClient() - ctx1 = {"a": 1, "b": 2} - ctx2 = {"b": 2, "a": 1} - h1 = client._fingerprint_context(ctx1) - h2 = client._fingerprint_context(ctx2) - assert h1 == h2 - - -def test_client_multiple_strategies_ranked_by_confidence(): - client = CortexDBxClient() - ctx = {"domain": "test"} - for _ in range(5): - client.log_outcome(ctx, "good_strategy", "SUCCESS") - for _ in range(5): - client.log_outcome(ctx, "bad_strategy", "FAILURE") - - recs = client.recommend(ctx, min_confidence=0.0, limit=10) - assert len(recs) == 2 - assert recs[0]["strategy_name"] == "good_strategy" - assert recs[0]["confidence"] > recs[1]["confidence"] diff --git a/_contrib/site/.gitignore b/_contrib/site/.gitignore deleted file mode 100644 index de23d07..0000000 --- a/_contrib/site/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -# Dependencies -node_modules - -# Build output -dist -dist-ssr -*.local - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? - -# Environment variables -.env -.env.local -.env.*.local diff --git a/_contrib/site/API_LAYER_README.md b/_contrib/site/API_LAYER_README.md deleted file mode 100644 index 9189c3d..0000000 --- a/_contrib/site/API_LAYER_README.md +++ /dev/null @@ -1,251 +0,0 @@ -# Cortex Batch Command Center - API Layer - -## Overview - -Task 2 implementation: Complete API integration layer for communicating with Cortex batch services. - -## Files Created - -### API Layer (`src/api/`) - -#### `types.ts` (1.6KB) -Core API response types matching Cortex backend: -- `BatchStatus` - Batch job status from Anthropic API -- `PendingTask` - Local queue task definition -- `UsageMetrics` - Cost/usage statistics -- `HealthResponse` - System health check -- `APIError` - Error response structure - -#### `client.ts` (4.0KB) -Axios-based API client with error handling: -- Base URL: `http://localhost:8765` (configurable via `VITE_CORTEX_API_URL`) -- **Health API**: `getHealth()` -- **Batch API**: `listBatches()`, `getBatchStatus()`, `cancelBatch()` -- **Queue API**: `getQueue()`, `addTask()`, `updateTaskPriority()`, `deleteTask()` -- **Metrics API**: `getMetrics(days)` -- Request/response interceptors with friendly error messages - -#### `hooks.ts` (4.9KB) -React Query hooks with automatic polling: -- `useHealthQuery()` - Poll every 60s -- `useBatchesQuery()` - Poll every 30s -- `useBatchStatusQuery(id, enabled)` - Poll every 5s (stops when batch ends) -- `useQueueQuery()` - Poll every 10s -- `useMetricsQuery(days)` - Poll every 5m -- Mutations: `useAddTaskMutation()`, `useUpdateTaskPriorityMutation()`, `useDeleteTaskMutation()`, `useCancelBatchMutation()` - -#### `index.ts` (0.6KB) -Central export for all API modules - -### Domain Types (`src/types/`) - -#### `batch.ts` (1.6KB) -Enriched types for UI: -- `EnrichedBatchStatus` - Batch with computed metrics (progress %, error rate, elapsed time, req/sec) -- `EnrichedPendingTask` - Task with AI recommendations and queue position -- `AIRecommendation` - AI-generated scheduling suggestions -- `DashboardSummary` - Summary statistics -- `QueueHealth` - Queue health metrics - -### Utilities (`src/utils/`) - -#### `calculations.ts` (8.9KB) -Business logic for metrics and recommendations: - -**Batch Calculations:** -- `calculateProgress(batch)` - Progress percentage (0-100) -- `calculateErrorRate(batch)` - Error rate percentage -- `calculateElapsedSeconds(batch)` - Time since batch creation -- `calculateRequestsPerSecond(batch)` - Processing rate -- `enrichBatchStatus(batch)` - Add all computed fields - -**Task Calculations:** -- `estimateDuration(tokens)` - Duration in minutes (~100 tokens/sec) -- `calculateQueuePosition(task, allTasks)` - Position based on priority + timestamp -- `generateRecommendation(task, allTasks)` - AI suggestions (mock implementation) -- `enrichPendingTask(task, allTasks)` - Add computed fields + AI rec - -**Formatters:** -- `formatDuration(seconds)` - "2h 15m", "45s" -- `formatTokens(tokens)` - "1.2K", "3.5M" -- `calculateSavingsPercentage(realTime, batch)` - Savings % - -#### `index.ts` (0.7KB) -Central export for all utilities - -### Config - -#### `vite-env.d.ts` (163B) -TypeScript environment types: -- `VITE_CORTEX_API_URL` - API base URL override - -## Usage Examples - -### Basic Query -```typescript -import { useBatchesQuery, useQueueQuery } from '@/api' - -function Dashboard() { - const { data: batches, isLoading } = useBatchesQuery() - const { data: queue } = useQueueQuery() - - return
{batches?.length} active batches
-} -``` - -### Specific Batch Monitoring -```typescript -import { useBatchStatusQuery } from '@/api' -import { enrichBatchStatus } from '@/utils' - -function BatchMonitor({ batchId }: { batchId: string }) { - const { data } = useBatchStatusQuery(batchId, true) - - if (!data) return null - - const enriched = enrichBatchStatus(data) - return ( -
-
Progress: {enriched.progress_percentage}%
-
Error Rate: {enriched.error_rate}%
-
Speed: {enriched.requests_per_second} req/s
-
- ) -} -``` - -### Queue with AI Recommendations -```typescript -import { useQueueQuery } from '@/api' -import { enrichPendingTask } from '@/utils' - -function QueueView() { - const { data: tasks } = useQueueQuery() - - const enrichedTasks = tasks?.map(task => - enrichPendingTask(task, tasks) - ) - - return ( -
- {enrichedTasks?.map(task => ( -
-
Position: #{task.position_in_queue}
-
Duration: ~{task.estimated_duration_minutes}m
- {task.ai_recommendation && ( -
- Suggestion: {task.ai_recommendation.suggestion} - ({(task.ai_recommendation.confidence * 100).toFixed(0)}% confidence) -
- )} -
- ))} -
- ) -} -``` - -### Mutations -```typescript -import { useAddTaskMutation, useUpdateTaskPriorityMutation } from '@/api' - -function QueueActions() { - const addTask = useAddTaskMutation() - const updatePriority = useUpdateTaskPriorityMutation() - - const handleAdd = () => { - addTask.mutate({ - title: 'New task', - description: 'Do something', - priority: 'NORMAL', - estimated_tokens: 5000, - }) - } - - const handleUpgrade = (taskId: string) => { - updatePriority.mutate({ taskId, priority: 'HIGH' }) - } - - return ( -
- -
- ) -} -``` - -## API Endpoints - -All endpoints are relative to `http://localhost:8765` (Cortex Bridge): - -- `GET /health` - Health check -- `GET /batches?limit=20` - List batches -- `GET /batches/:id` - Batch details -- `POST /batches/:id/cancel` - Cancel batch -- `GET /queue` - Get queue -- `POST /queue` - Add task -- `PATCH /queue/:id` - Update task -- `DELETE /queue/:id` - Delete task -- `GET /metrics?days=7` - Usage metrics - -## Polling Intervals - -- Health: 60s -- Batch list: 30s -- Specific batch: 5s (stops when ended) -- Queue: 10s -- Metrics: 5m - -## AI Recommendations - -Mock implementation uses heuristics: -- **run_now**: CRITICAL priority, deadline < 2h, blocking other tasks -- **reprioritize_up**: Deadline approaching but LOW priority -- **defer**: submit_after constraint, LOW priority with no deadline -- **none**: Appropriately scheduled - -Future: Replace with actual Cortex intelligence API. - -## Type Safety - -All API responses are fully typed. TypeScript will catch: -- Missing required fields -- Incorrect priority values -- Invalid status transitions -- Type mismatches - -Run `npm run type-check` to verify. - -## Testing - -Type check: `npm run type-check` ✅ PASSING - -Integration testing framework not yet configured. Test files were excluded. - -## Next Steps - -1. Create React components to consume these hooks (Task 3) -2. Build dashboard layout (Task 4) -3. Add charts for visualizations (Task 5) -4. Implement backend Cortex Bridge endpoints -5. Replace mock AI recommendations with real Cortex intelligence - -## Reference Files - -Patterns inspired by: -- `/Users/jesse.kemp/Dev/Vortex/VortexV3/src/api/client.ts` -- `/Users/jesse.kemp/Dev/Vortex/VortexV3/src/api/hooks.ts` -- `/Users/jesse.kemp/Dev/cortex/batch/batch_api_client.py` -- `/Users/jesse.kemp/Dev/cortex/batch/dashboard.py` - -## File Locations - -All files in `/Users/jesse.kemp/Dev/cortex/site/src/`: -- `api/types.ts` -- `api/client.ts` -- `api/hooks.ts` -- `api/index.ts` -- `types/batch.ts` -- `utils/calculations.ts` -- `utils/index.ts` -- `vite-env.d.ts` diff --git a/_contrib/site/README.md b/_contrib/site/README.md deleted file mode 100644 index fd23c53..0000000 --- a/_contrib/site/README.md +++ /dev/null @@ -1,196 +0,0 @@ -# Cortex Batch Command Center - -Military-themed command center dashboard for AI-powered batch orchestration and intelligence. - -## Tech Stack - -- **React 18** with TypeScript -- **Vite** - Build tool -- **Tailwind CSS** - Styling -- **Zustand** - State management -- **TanStack Query** - Data fetching -- **Axios** - HTTP client - -## Design System - -Military/tactical command center theme inspired by Anduril Lattice. - -### Color Palette - -- **Background**: Near-black hierarchy (`#05050A`, `#0F1117`, `#1A1D27`) -- **Status Colors**: - - Nominal (Green): `#10B981` - Systems operational - - Warning (Amber): `#F59E0B` - Attention required - - Critical (Red): `#EF4444` - Critical alert - - Processing (Blue): `#3B82F6` - Active operations - - AI Suggestion (Purple): `#A855F7` - AI recommendations - -### Components - -All components located in `/src/design-system/components/`: - -1. **Card** - Container with variants (default, elevated, outlined) -2. **Button** - Military styled with variants (primary, secondary, ghost, danger) -3. **StatusBadge** - Status indicator with pulse animation for active states -4. **ProgressRing** - SVG circular progress with percentage display -5. **MetricDisplay** - Large metric display with trend indicators -6. **PriorityBadge** - Priority levels (CRITICAL, HIGH, MEDIUM, LOW) - -### Typography - -- **Sans**: Inter - UI text -- **Mono**: JetBrains Mono - Data, metrics, labels - -## Development - -```bash -# Install dependencies -npm install - -# Start dev server (http://localhost:5173) -npm run dev - -# Type check -npm run type-check - -# Build for production -npm run build - -# Preview production build -npm run preview -``` - -## Project Structure - -``` -site/ -├── src/ -│ ├── layouts/ -│ │ └── CommandCenterLayout.tsx # Main 3-column layout -│ ├── panels/ -│ │ ├── shared/ -│ │ │ ├── StatusBar.tsx # Top status bar -│ │ │ └── BottomActionBar.tsx # Bottom action bar -│ │ ├── running/ -│ │ │ ├── RunningPanel.tsx # Active batches panel -│ │ │ └── ActiveBatchCard.tsx # Batch card component -│ │ ├── pending/ -│ │ │ ├── PendingQueuePanel.tsx # Queued tasks panel -│ │ │ ├── PendingTaskCard.tsx # Task card component -│ │ │ └── AIRecommendations.tsx # AI suggestion component -│ │ └── completed/ -│ │ ├── CompletedPanel.tsx # History panel -│ │ └── CompletedJobCard.tsx # Completed batch card -│ ├── design-system/ -│ │ ├── components/ # Reusable UI components -│ │ │ ├── Button.tsx -│ │ │ ├── Card.tsx -│ │ │ ├── MetricDisplay.tsx -│ │ │ ├── PriorityBadge.tsx -│ │ │ ├── ProgressRing.tsx -│ │ │ ├── StatusBadge.tsx -│ │ │ └── index.ts -│ │ └── tokens.ts # Design tokens -│ ├── api/ -│ │ ├── client.ts # Axios instance -│ │ ├── hooks.ts # React Query hooks -│ │ └── types.ts # API types -│ ├── stores/ -│ │ └── batchStore.ts # Zustand state -│ ├── utils/ -│ │ ├── cn.ts # Class name utility -│ │ ├── formatters.ts # Format utilities -│ │ └── calculations.ts # Metric calculations -│ ├── types/ -│ │ └── batch.ts # Domain types -│ ├── App.tsx # Main app component -│ ├── main.tsx # Entry point -│ └── index.css # Global styles -├── package.json -├── vite.config.ts -├── tailwind.config.js -├── tsconfig.json -└── index.html -``` - -## Dashboard Architecture - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ STATUS BAR │ -│ [● NOMINAL] [API: Connected] [CORTEX COMMAND CENTER] [UTC TIME] │ -├──────────────────┬────────────────────────┬─────────────────────────┤ -│ │ │ │ -│ RUNNING PANEL │ PENDING PANEL │ COMPLETED PANEL │ -│ (Left - 25%) │ (Center - 50%) │ (Right - 25%) │ -│ │ │ │ -│ - Active Ops │ - Queued Tasks │ - Historical Data │ -│ - Capacity │ - AI Recommendations │ - Success Rates │ -│ - Progress │ - Queue Metrics │ - Cost Savings │ -│ │ │ │ -├──────────────────┴────────────────────────┴─────────────────────────┤ -│ BOTTOM ACTION BAR │ -│ [Submit New Batch] [Refresh All] [Settings] Last updated: 5s │ -└─────────────────────────────────────────────────────────────────────┘ -``` - -## Features - -### StatusBar (Top) -- Real-time system health monitoring -- API connection status -- Live UTC clock (updates every second) -- Cost savings tracker - -### Running Panel (Left) -- Active batch operations with real-time progress -- Capacity indicator (0/5 slots) -- Progress rings with percentage -- Error rates and processing speeds - -### Pending Queue Panel (Center) -- Queued tasks awaiting submission -- AI-powered recommendations -- Priority badges (CRITICAL, HIGH, MEDIUM, LOW) -- Queue metrics and estimates - -### Completed Panel (Right) -- Historical batch completion data -- Time filters: Today | 7 Days | 30 Days -- Success rate tracking -- Collapsible cards with detailed results - -### BottomActionBar (Bottom) -- Quick actions: Submit, Refresh, Settings -- Last updated timestamp - -## API Integration - -The dashboard connects to the Cortex Bridge API (default: http://localhost:8765): - -``` -GET /health - System health check -GET /batches - List active/completed batches -GET /batches/:id - Get specific batch status -POST /batches/:id/cancel - Cancel a running batch -GET /queue - List pending tasks -POST /queue - Add task to queue -PATCH /queue/:id - Update task priority -DELETE /queue/:id - Remove task from queue -GET /metrics?days=N - Get usage metrics -``` - -Set via environment variable: -```bash -VITE_CORTEX_API_URL=http://localhost:8765 -``` - -## Design Principles - -1. **Functional First** - No decorative elements, everything serves a purpose -2. **Status-Driven** - Color indicates system state -3. **High Contrast** - Dark backgrounds with bright status colors -4. **Monospace Data** - All metrics and data use JetBrains Mono -5. **Tactical Aesthetics** - Military command center inspiration -6. **Pulse Animations** - Active states show pulse to indicate processing -7. **Auto-refresh** - Queries refresh every 5 seconds for real-time updates diff --git a/_contrib/site/TASK6_COMPLETION_SUMMARY.md b/_contrib/site/TASK6_COMPLETION_SUMMARY.md deleted file mode 100644 index 65f1441..0000000 --- a/_contrib/site/TASK6_COMPLETION_SUMMARY.md +++ /dev/null @@ -1,132 +0,0 @@ -# Task 6 Completion Summary: Layout, StatusBar, CompletedPanel & Final Integration - -## Files Created - -### 1. Layout Component -**Location:** `/Users/jesse.kemp/Dev/cortex/site/src/layouts/CommandCenterLayout.tsx` - -- Main 3-column responsive grid layout -- Full-height (100vh) dark-themed interface -- Accepts statusBar, bottomBar, and 3 panel children -- Responsive design with proper border separations -- Grid columns: 25% (Running) | 50% (Pending) | 25% (Completed) - -### 2. Shared Components - -#### StatusBar -**Location:** `/Users/jesse.kemp/Dev/cortex/site/src/panels/shared/StatusBar.tsx` - -Features: -- System health indicator (NOMINAL/DEGRADED) with pulsing dot -- API connection status -- Live UTC time (updates every second) -- Cost savings badge from last 7 days metrics -- "CORTEX COMMAND CENTER" title - -#### BottomActionBar -**Location:** `/Users/jesse.kemp/Dev/cortex/site/src/panels/shared/BottomActionBar.tsx` - -Features: -- "Submit New Batch" button (primary action) -- "Refresh All" button (secondary action) -- "Settings" button (ghost style) -- Last updated timestamp with relative time - -#### Barrel Export -**Location:** `/Users/jesse.kemp/Dev/cortex/site/src/panels/shared/index.ts` - -### 3. Completed Panel Components - -#### CompletedPanel -**Location:** `/Users/jesse.kemp/Dev/cortex/site/src/panels/completed/CompletedPanel.tsx` - -Features: -- Header with "COMPLETED" title and today's count badge -- Success rate metric -- Cost savings total (7 days) -- Time filter buttons: Today | 7 Days | 30 Days -- Scrollable list of completed batches -- Empty state handling -- Loading and error states - -#### CompletedJobCard -**Location:** `/Users/jesse.kemp/Dev/cortex/site/src/panels/completed/CompletedJobCard.tsx` - -Features: -- Collapsible card design -- Batch ID (truncated) -- Status badge (COMPLETED/FAILED) -- Success rate percentage with color coding -- Duration display -- Expandable details: - - Completion timestamp - - Token usage - - Success/error counts - - Batch ID preview - -#### Barrel Export -**Location:** `/Users/jesse.kemp/Dev/cortex/site/src/panels/completed/index.ts` - -### 4. App Integration -**Location:** `/Users/jesse.kemp/Dev/cortex/site/src/App.tsx` - -Updates: -- Replaced design system showcase with full dashboard -- Integrated QueryClient with auto-refresh (5s interval) -- Wired up all three panels (Running, Pending, Completed) -- Connected StatusBar and BottomActionBar -- Used CommandCenterLayout for structure - -## TypeScript Compilation Status - -**My components (Task 6):** ✅ No TypeScript errors -- CommandCenterLayout.tsx -- StatusBar.tsx -- BottomActionBar.tsx -- CompletedPanel.tsx -- CompletedJobCard.tsx - -**Pre-existing components (Previous tasks):** ⚠️ Have TypeScript errors -- PendingQueuePanel.tsx (uses undefined `colors.text`, `colors.primary`, etc.) -- AIRecommendations.tsx (uses undefined `colors.ai`) -- PendingTaskCard.tsx (uses undefined color properties) - -Note: The errors in pending panel components are from previous tasks and are outside the scope of Task 6. - -## Design Patterns Used - -1. **Consistent styling**: All components use `colors` from design tokens -2. **Responsive layout**: CSS Grid with proper breakpoints -3. **Loading states**: Skeleton states and loading indicators -4. **Empty states**: Friendly messages when no data -5. **Error handling**: Clear error messages with retry options -6. **Type safety**: Proper TypeScript types throughout -7. **Data filtering**: Time-based filtering with memoization -8. **Collapsible UI**: Expandable cards for details on demand - -## API Integration - -- `useBatchesQuery`: Fetches batch list for filtering -- `useHealthQuery`: System health status -- `useMetricsQuery`: Cost savings and metrics -- Auto-refresh: 5-second interval for real-time updates - -## Acceptance Criteria Met - -✅ 3-column layout renders correctly -✅ StatusBar shows health, time, savings -✅ CompletedPanel shows historical batches -✅ Collapsible cards work smoothly -✅ Time updates every second -✅ All panels communicate via Zustand store (selectedBatchId) -✅ Responsive on smaller screens (stacks vertically) -✅ Full app integrates all panels - -## Next Steps (Not part of this task) - -1. Fix TypeScript errors in PendingQueuePanel and related components (from previous tasks) -2. Implement modal for "Submit New Batch" button -3. Implement settings modal -4. Add actual refresh logic (currently uses window.reload) -5. Connect to real Cortex API backend -6. Add unit tests for components diff --git a/_contrib/site/data/models/ensemble_weights.json b/_contrib/site/data/models/ensemble_weights.json deleted file mode 100644 index 3be6444..0000000 --- a/_contrib/site/data/models/ensemble_weights.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "weights": { - "lstm": 0.2612474934036939, - "gfs": 0.27913034300791556, - "ecmwf_hres": 0.29418047493403693, - "hrrr": 0.16544168865435355 - }, - "last_updated": "2026-02-17T16:10:34.758463", - "performance_history": {}, - "validation_based": true -} diff --git a/_contrib/site/data/models/persistence_model.pkl b/_contrib/site/data/models/persistence_model.pkl deleted file mode 100644 index 8f2dded..0000000 Binary files a/_contrib/site/data/models/persistence_model.pkl and /dev/null differ diff --git a/_contrib/site/index.html b/_contrib/site/index.html deleted file mode 100644 index 36754ce..0000000 --- a/_contrib/site/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - Cortex Mission Control - - - - - -
- - - diff --git a/_contrib/site/landing_bottom.png b/_contrib/site/landing_bottom.png deleted file mode 100644 index fe163bc..0000000 Binary files a/_contrib/site/landing_bottom.png and /dev/null differ diff --git a/_contrib/site/landing_hero.png b/_contrib/site/landing_hero.png deleted file mode 100644 index 97c6079..0000000 Binary files a/_contrib/site/landing_hero.png and /dev/null differ diff --git a/_contrib/site/landing_mistake_prevention.png b/_contrib/site/landing_mistake_prevention.png deleted file mode 100644 index a5c6ef1..0000000 Binary files a/_contrib/site/landing_mistake_prevention.png and /dev/null differ diff --git a/_contrib/site/landing_screenshot_full.png b/_contrib/site/landing_screenshot_full.png deleted file mode 100644 index 6d9687f..0000000 Binary files a/_contrib/site/landing_screenshot_full.png and /dev/null differ diff --git a/_contrib/site/landing_terminal.png b/_contrib/site/landing_terminal.png deleted file mode 100644 index 8bfbf30..0000000 Binary files a/_contrib/site/landing_terminal.png and /dev/null differ diff --git a/_contrib/site/landing_terminal_v2.png b/_contrib/site/landing_terminal_v2.png deleted file mode 100644 index 113db3d..0000000 Binary files a/_contrib/site/landing_terminal_v2.png and /dev/null differ diff --git a/_contrib/site/landing_terminal_v2b.png b/_contrib/site/landing_terminal_v2b.png deleted file mode 100644 index 06c192f..0000000 Binary files a/_contrib/site/landing_terminal_v2b.png and /dev/null differ diff --git a/_contrib/site/landing_v3_hero.png b/_contrib/site/landing_v3_hero.png deleted file mode 100644 index 88ebcd6..0000000 Binary files a/_contrib/site/landing_v3_hero.png and /dev/null differ diff --git a/_contrib/site/package-lock.json b/_contrib/site/package-lock.json deleted file mode 100644 index 9d69427..0000000 --- a/_contrib/site/package-lock.json +++ /dev/null @@ -1,4993 +0,0 @@ -{ - "name": "cortex-mission-control", - "version": "0.2.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "cortex-mission-control", - "version": "0.2.0", - "dependencies": { - "@tanstack/react-query": "^5.66.0", - "axios": "^1.7.9", - "clsx": "^2.1.1", - "framer-motion": "^12.0.6", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-router-dom": "^7.1.2", - "recharts": "^2.15.0", - "tailwind-merge": "^2.6.0", - "zustand": "^5.0.3" - }, - "devDependencies": { - "@eslint/js": "^9.19.0", - "@playwright/test": "^1.51.1", - "@types/node": "^22.12.0", - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", - "@vitejs/plugin-react": "^4.3.4", - "autoprefixer": "^10.4.20", - "eslint": "^9.19.0", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.18", - "globals": "^15.14.0", - "postcss": "^8.5.1", - "tailwindcss": "^3.4.17", - "typescript": "~5.7.3", - "typescript-eslint": "^8.21.0", - "vite": "^6.0.11" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.0.tgz", - "integrity": "sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@playwright/test": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", - "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.58.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", - "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", - "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", - "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", - "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", - "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", - "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", - "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", - "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", - "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", - "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", - "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", - "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", - "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", - "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", - "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", - "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", - "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", - "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", - "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", - "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", - "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", - "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", - "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", - "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", - "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", - "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@tanstack/query-core": { - "version": "5.90.20", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz", - "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-query": { - "version": "5.90.20", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.20.tgz", - "integrity": "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.90.20" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^18 || ^19" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", - "license": "MIT" - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-shape": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", - "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.19.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", - "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.27", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", - "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", - "devOptional": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", - "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/type-utils": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.54.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", - "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", - "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.54.0", - "@typescript-eslint/types": "^8.54.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", - "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", - "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", - "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", - "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", - "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.54.0", - "@typescript-eslint/tsconfig-utils": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "debug": "^4.4.3", - "minimatch": "^9.0.5", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", - "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", - "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.54.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", - "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.28.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.27", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/autoprefixer": { - "version": "10.4.24", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", - "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001766", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/axios": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz", - "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001767", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz", - "integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", - "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", - "license": "MIT" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, - "license": "MIT" - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.283", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.283.tgz", - "integrity": "sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==", - "dev": true, - "license": "ISC" - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.26", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", - "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": ">=8.40" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-equals": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", - "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/framer-motion": { - "version": "12.34.0", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.0.tgz", - "integrity": "sha512-+/H49owhzkzQyxtn7nZeF4kdH++I2FWrESQ184Zbcw5cEqNHYkE5yxWxcTLSj5lNx3NWdbIRy5FHqUvetD8FWg==", - "license": "MIT", - "dependencies": { - "motion-dom": "^12.34.0", - "motion-utils": "^12.29.2", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "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" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/motion-dom": { - "version": "12.34.0", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.34.0.tgz", - "integrity": "sha512-Lql3NuEcScRDxTAO6GgUsRHBZOWI/3fnMlkMcH5NftzcN37zJta+bpbMAV9px4Nj057TuvRooMK7QrzMCgtz6Q==", - "license": "MIT", - "dependencies": { - "motion-utils": "^12.29.2" - } - }, - "node_modules/motion-utils": { - "version": "12.29.2", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.29.2.tgz", - "integrity": "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/playwright": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", - "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.58.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", - "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", - "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/react-refresh": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.0.tgz", - "integrity": "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router-dom": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.0.tgz", - "integrity": "sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g==", - "license": "MIT", - "dependencies": { - "react-router": "7.13.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - } - }, - "node_modules/react-smooth": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", - "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", - "license": "MIT", - "dependencies": { - "fast-equals": "^5.0.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "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" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recharts": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", - "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", - "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^18.3.1", - "react-smooth": "^4.0.4", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/recharts-scale": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", - "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "license": "MIT", - "dependencies": { - "decimal.js-light": "^2.4.1" - } - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", - "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.57.1", - "@rollup/rollup-android-arm64": "4.57.1", - "@rollup/rollup-darwin-arm64": "4.57.1", - "@rollup/rollup-darwin-x64": "4.57.1", - "@rollup/rollup-freebsd-arm64": "4.57.1", - "@rollup/rollup-freebsd-x64": "4.57.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", - "@rollup/rollup-linux-arm-musleabihf": "4.57.1", - "@rollup/rollup-linux-arm64-gnu": "4.57.1", - "@rollup/rollup-linux-arm64-musl": "4.57.1", - "@rollup/rollup-linux-loong64-gnu": "4.57.1", - "@rollup/rollup-linux-loong64-musl": "4.57.1", - "@rollup/rollup-linux-ppc64-gnu": "4.57.1", - "@rollup/rollup-linux-ppc64-musl": "4.57.1", - "@rollup/rollup-linux-riscv64-gnu": "4.57.1", - "@rollup/rollup-linux-riscv64-musl": "4.57.1", - "@rollup/rollup-linux-s390x-gnu": "4.57.1", - "@rollup/rollup-linux-x64-gnu": "4.57.1", - "@rollup/rollup-linux-x64-musl": "4.57.1", - "@rollup/rollup-openbsd-x64": "4.57.1", - "@rollup/rollup-openharmony-arm64": "4.57.1", - "@rollup/rollup-win32-arm64-msvc": "4.57.1", - "@rollup/rollup-win32-ia32-msvc": "4.57.1", - "@rollup/rollup-win32-x64-gnu": "4.57.1", - "@rollup/rollup-win32-x64-msvc": "4.57.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tailwind-merge": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz", - "integrity": "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", - "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.7", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.54.0.tgz", - "integrity": "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.54.0", - "@typescript-eslint/parser": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, - "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "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 - } - } - }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zustand": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.11.tgz", - "integrity": "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==", - "license": "MIT", - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "@types/react": ">=18.0.0", - "immer": ">=9.0.6", - "react": ">=18.0.0", - "use-sync-external-store": ">=1.2.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "immer": { - "optional": true - }, - "react": { - "optional": true - }, - "use-sync-external-store": { - "optional": true - } - } - } - } -} diff --git a/_contrib/site/package.json b/_contrib/site/package.json deleted file mode 100644 index e4ab8fb..0000000 --- a/_contrib/site/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "cortex-mission-control", - "private": true, - "version": "0.2.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "build:check": "npm run type-check && npm run lint && npm run build", - "type-check": "tsc -b", - "lint": "eslint .", - "preview": "vite preview" - }, - "dependencies": { - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-router-dom": "^7.1.2", - "zustand": "^5.0.3", - "@tanstack/react-query": "^5.66.0", - "axios": "^1.7.9", - "clsx": "^2.1.1", - "tailwind-merge": "^2.6.0", - "recharts": "^2.15.0", - "framer-motion": "^12.0.6" - }, - "devDependencies": { - "@eslint/js": "^9.19.0", - "@types/node": "^22.12.0", - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", - "@vitejs/plugin-react": "^4.3.4", - "autoprefixer": "^10.4.20", - "eslint": "^9.19.0", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.18", - "globals": "^15.14.0", - "postcss": "^8.5.1", - "tailwindcss": "^3.4.17", - "typescript": "~5.7.3", - "typescript-eslint": "^8.21.0", - "vite": "^6.0.11" - } -} diff --git a/_contrib/site/postcss.config.js b/_contrib/site/postcss.config.js deleted file mode 100644 index 2e7af2b..0000000 --- a/_contrib/site/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/_contrib/site/src/App.tsx b/_contrib/site/src/App.tsx deleted file mode 100644 index 0ec11db..0000000 --- a/_contrib/site/src/App.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { lazy, Suspense } from 'react' -import { BrowserRouter, Routes, Route } from 'react-router-dom' -import { MissionControlLayout } from '@/layouts/MissionControlLayout' -import { CoNavigatorLayout } from '@/layouts/CoNavigatorLayout' - -// Co-Navigator mode panels -const BriefingMode = lazy(() => import('@/panels/navigator/BriefingMode')) -const NavigateMode = lazy(() => import('@/panels/navigator/NavigateMode')) -const MonitorMode = lazy(() => import('@/panels/navigator/MonitorMode')) -const ExploreMode = lazy(() => import('@/panels/navigator/ExploreMode')) - -// Legacy panels -const OverviewPanel = lazy(() => import('@/panels/overview/OverviewPanel')) -const AnomalyPanel = lazy(() => import('@/panels/anomalies/AnomalyPanel')) -const IntelligencePanel = lazy(() => import('@/panels/intelligence/IntelligencePanel')) -const RecommendationsPanel = lazy(() => import('@/panels/recommendations/RecommendationsPanel')) -const BatchDashboard = lazy(() => import('@/panels/batch/BatchDashboard')) -const MetricsPanel = lazy(() => import('@/panels/metrics/MetricsPanel')) -const VortexHealthPanel = lazy(() => import('@/panels/vortex/VortexHealthPanel')) -const SessionPanel = lazy(() => import('@/panels/sessions/SessionPanel')) -const TaskBoardPanel = lazy(() => import('@/panels/taskboard/TaskBoardPanel')) -const InfrastructurePanel = lazy(() => import('@/panels/infrastructure/InfrastructurePanel')) -const ConductorPanel = lazy(() => import('@/panels/conductor/ConductorPanel')) - -function LoadingFallback() { - return ( -
-
-
- LOADING MODULE -
-
- ) -} - -function S({ children }: { children: React.ReactNode }) { - return }>{children} -} - -export default function App() { - return ( - - - {/* Co-Navigator (new default) */} - }> - } /> - } /> - } /> - } /> - } /> - - - {/* Legacy Mission Control */} - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - ) -} diff --git a/_contrib/site/src/api/README.md b/_contrib/site/src/api/README.md deleted file mode 100644 index 42db205..0000000 --- a/_contrib/site/src/api/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Cortex Batch API Integration Layer - -Complete API integration for Cortex Batch Command Center. - -## Quick Start - -```typescript -import { useBatchesQuery, useQueueQuery, useMetricsQuery } from '@/api' - -function MyComponent() { - const { data: batches } = useBatchesQuery() - const { data: queue } = useQueueQuery() - const { data: metrics } = useMetricsQuery(7) - - return ( -
-
Active Batches: {batches?.length ?? 0}
-
Queued Tasks: {queue?.length ?? 0}
-
7-Day Savings: ${metrics?.cost_estimate.savings ?? 0}
-
- ) -} -``` - -## Files - -- **`types.ts`** - API response types (BatchStatus, PendingTask, UsageMetrics, etc.) -- **`client.ts`** - Axios client with error handling and interceptors -- **`hooks.ts`** - React Query hooks with automatic polling -- **`index.ts`** - Central export for easy imports - -## Features - -✅ Full TypeScript support -✅ Automatic polling with configurable intervals -✅ Request/response interceptors -✅ Error handling with friendly messages -✅ Mutations with automatic cache invalidation -✅ Prefetching utilities - -## Polling Intervals - -| Hook | Interval | Notes | -|------|----------|-------| -| `useHealthQuery()` | 60s | System health | -| `useBatchesQuery()` | 30s | Batch list | -| `useBatchStatusQuery()` | 5s | Stops when ended | -| `useQueueQuery()` | 10s | Queue status | -| `useMetricsQuery()` | 5m | Cost/usage metrics | - -## Environment Variables - -```bash -# .env -VITE_CORTEX_API_URL=http://localhost:8765 # Default -``` - -## See Also - -- `/Users/jesse.kemp/Dev/cortex/site/API_LAYER_README.md` - Full documentation -- `../types/batch.ts` - Enriched domain types -- `../utils/calculations.ts` - Calculation utilities diff --git a/_contrib/site/src/api/client.ts b/_contrib/site/src/api/client.ts deleted file mode 100644 index e23055c..0000000 --- a/_contrib/site/src/api/client.ts +++ /dev/null @@ -1,392 +0,0 @@ -import axios, { AxiosError } from 'axios' -import type { - BatchStatus, - PendingTask, - UsageMetrics, - HealthResponse, - APIError, -} from './types' -import type { AnomalyResponse, RecommendationResponse, IntelligenceQuery, IntelligenceResult, ProjectStatus } from '@/types/cortex' -import type { VortexHealth, SchedulerStatusRaw, ModelPerformance } from '@/types/vortex' -import type { SessionsResponse } from '@/types/session' -import type { TaskBoardResponse, Task, DecomposeResponse } from '@/types/task' -import type { DocsTreeResponse, DocContentResponse, ServicesStatusResponse, PredictionsResponse, DecisionRecord, ActivityHeatmapResponse } from '@/types/navigator' - -const API_BASE_URL = import.meta.env.VITE_CORTEX_API_URL || '/api' - -export const cortexApi = axios.create({ - baseURL: API_BASE_URL, - timeout: 30000, - headers: { - 'Content-Type': 'application/json', - }, -}) - -cortexApi.interceptors.response.use( - (response) => response, - (error: AxiosError) => { - const message = error.response?.data?.message || error.message - - if (error.code === 'ECONNREFUSED') { - error.message = 'Cortex Bridge is not running. Start with: python cortex/bridge.py' - } else if (error.response?.status === 404) { - error.message = `Endpoint not found: ${error.config?.url}` - } else if (error.response?.status === 500) { - error.message = `Internal server error: ${message}` - } - - return Promise.reject(error) - } -) - -// Vortex API — proxied through /vortex → :8000 -export const vortexApi = axios.create({ - baseURL: '/vortex-api/api/v2', - timeout: 15000, - headers: { 'Content-Type': 'application/json' }, -}) - -// ── Cortex: Health ── -export const healthApi = { - getHealth: async (): Promise => { - const response = await cortexApi.get('/health') - // Bridge returns {status, service} — normalize to HealthResponse - return { - status: response.data.status === 'healthy' ? 'healthy' : 'degraded', - timestamp: new Date().toISOString(), - version: response.data.version, - } - }, -} - -// ── Cortex: Service Health (ecosystem-wide) ── -export const serviceHealthApi = { - getServiceHealth: async () => { - const response = await cortexApi.get('/service-health') - return response.data - }, -} - -// ── Cortex: Batches ── -export const batchApi = { - listBatches: async (limit = 20): Promise => { - const response = await cortexApi.get('/batches', { params: { limit } }) - // Bridge wraps in {batches: [...]} - return response.data.batches ?? response.data - }, - - getBatchStatus: async (batchId: string): Promise => { - const response = await cortexApi.get(`/batches/${batchId}`) - return response.data - }, - - cancelBatch: async (batchId: string): Promise => { - await cortexApi.post(`/batches/${batchId}/cancel`) - }, -} - -// ── Cortex: Queue ── -export const queueApi = { - getQueue: async (): Promise => { - const response = await cortexApi.get('/queue') - // Bridge wraps in {tasks: [...], metadata: {...}} - return response.data.tasks ?? response.data - }, - - addTask: async (task: Omit): Promise => { - const response = await cortexApi.post('/queue', task) - return response.data.task ?? response.data - }, - - updateTaskPriority: async ( - taskId: string, - priority: PendingTask['priority'] - ): Promise => { - const response = await cortexApi.patch(`/queue/${taskId}`, { priority }) - return response.data.task ?? response.data - }, - - deleteTask: async (taskId: string): Promise => { - await cortexApi.delete(`/queue/${taskId}`) - }, -} - -// ── Cortex: Metrics ── -export const metricsApi = { - getMetrics: async (days = 7): Promise => { - const response = await cortexApi.get('/metrics', { params: { days } }) - const d = response.data - // Bridge returns flat metrics — transform to UsageMetrics shape - return { - days: d.period_days ?? days, - total_tasks: d.total_requests ?? 0, - total_tokens: 0, - by_priority: {}, - cost_estimate: { - real_time_would_be: (d.estimated_savings_usd ?? 0) * 2, - batch_actual: d.estimated_savings_usd ?? 0, - savings: d.estimated_savings_usd ?? 0, - }, - } - }, -} - -// ── Cortex: Status / Intelligence / Anomalies / Projects ── -export const statusApi = { - getStatus: async () => { - const response = await cortexApi.get('/status') - return response.data - }, -} - -export const intelligenceApi = { - query: async (payload: IntelligenceQuery): Promise => { - const response = await cortexApi.post('/intelligence/query', { - request: payload.query, - project: 'cortex', - query_type: 'analysis', - }) - const d = response.data - return { - query: payload.query, - response: d.result ?? d.response ?? '', - confidence: d.confidence ?? 0.5, - sources: d.sources ?? [], - reasoning: d.reasoning, - timestamp: new Date().toISOString(), - } - }, -} - -export const anomalyApi = { - getAnomalies: async (): Promise => { - const response = await cortexApi.get('/anomalies') - const d = response.data - // Bridge returns {count, anomalies: [{id, type, severity, title, ...}]} - const anomalies = (d.anomalies ?? []).map((a: Record) => ({ - id: a.id ?? '', - severity: a.severity ?? 'INFO', - title: a.title ?? '', - description: a.description ?? '', - project: (a.type as string)?.toLowerCase() ?? 'cortex', - detected_at: (a.detected_at as string) ?? new Date().toISOString(), - recommendation: a.recommendation as string | undefined, - })) - const bySeverity: Record = {} - for (const a of anomalies) { - bySeverity[a.severity] = (bySeverity[a.severity] ?? 0) + 1 - } - return { anomalies, total: d.count ?? anomalies.length, by_severity: bySeverity } - }, -} - -export const recommendationsApi = { - getRecommendations: async (): Promise => { - const response = await cortexApi.get('/recommendations') - const d = response.data - // Bridge returns {next_action, risk_alerts, priority_projects, goals_summary} - // Transform risk_alerts + next_action into Recommendation[] - const recs: Array> = [] - - if (d.next_action) { - recs.push({ - id: 'next_action', - priority: d.next_action.priority === 'HIGH' ? 'HIGH' : d.next_action.priority === 'LOW' ? 'LOW' : 'MEDIUM', - title: d.next_action.action ?? 'Next Action', - description: d.next_action.action ?? '', - project: 'cortex', - reasoning: `Type: ${d.next_action.type ?? 'maintenance'}`, - created_at: d.generated_at ?? new Date().toISOString(), - }) - } - - for (const [i, alert] of (d.risk_alerts ?? []).entries()) { - recs.push({ - id: `alert_${i}`, - priority: alert.severity === 'HIGH' ? 'HIGH' : alert.severity === 'MEDIUM' ? 'MEDIUM' : 'LOW', - title: alert.message ?? 'Risk Alert', - description: alert.recommendation ?? alert.message ?? '', - project: 'cortex', - reasoning: `Type: ${alert.type ?? 'unknown'}`, - created_at: d.generated_at ?? new Date().toISOString(), - }) - } - - return { recommendations: recs as unknown as RecommendationResponse['recommendations'], total: recs.length } - }, -} - -export const projectsApi = { - getProjects: async (): Promise => { - const response = await cortexApi.get('/projects') - // Bridge now returns ProjectStatus[] directly - return response.data - }, -} - -// ── VortexV2: Health (detailed) / Scheduler / Models ── -export const vortexHealthApi = { - getHealth: async (): Promise => { - const response = await vortexApi.get('/health/detailed') - return response.data - }, -} - -export const vortexSchedulerApi = { - getScheduler: async (): Promise => { - const response = await vortexApi.get('/scheduler/status') - return response.data - }, -} - -export const vortexModelsApi = { - getPerformance: async (): Promise => { - // Default location: offshore buoy 41002 (South Hatteras) - const response = await vortexApi.get('/models/performance', { - params: { lat: 32.27, lon: -75.42, days: 7 }, - }) - return response.data - }, -} - -// ── Cortex: Sessions ── -export const sessionsApi = { - getSessions: async (activeOnly = false, limit = 20): Promise => { - const response = await cortexApi.get('/sessions', { - params: { active_only: activeOnly, limit }, - }) - return response.data - }, -} - -// ── Cortex: TaskBoard ── -export const taskBoardApi = { - getTasks: async (filters?: { - status?: string - project?: string - priority?: string - }): Promise => { - const response = await cortexApi.get('/taskboard', { params: filters }) - return response.data - }, - - createTask: async (task: { - title: string - description?: string - status?: string - priority?: string - project?: string - tags?: string[] - parent_id?: string | null - }): Promise<{ status: string; task: Task }> => { - const response = await cortexApi.post('/taskboard', task) - return response.data - }, - - updateTask: async ( - taskId: string, - update: Partial> - ): Promise<{ status: string; task: Task }> => { - const response = await cortexApi.patch(`/taskboard/${taskId}`, update) - return response.data - }, - - deleteTask: async (taskId: string): Promise => { - await cortexApi.delete(`/taskboard/${taskId}`) - }, - - decompose: async (req: { - description: string - project?: string - context?: string - }): Promise => { - const response = await cortexApi.post('/taskboard/decompose', req) - return response.data - }, -} - -// ── Conductor: Prompt History ── -export interface PromptHistoryEntry { - timestamp: string - intent: string - project_id: string - intent_level: string - prompt: string - token_estimate: number -} - -export const conductorApi = { - getPromptHistory: async (limit = 20): Promise<{ entries: PromptHistoryEntry[]; total: number }> => { - const response = await cortexApi.get('/conductor/history', { params: { limit } }) - return response.data - }, -} - -// ── Co-Navigator: Docs ── -export const docsApi = { - getTree: async (): Promise => { - const response = await cortexApi.get('/docs/tree') - return response.data - }, - getContent: async (path: string): Promise => { - const response = await cortexApi.get('/docs/content', { params: { path } }) - return response.data - }, -} - -// ── Co-Navigator: Services ── -export const servicesApi = { - getStatus: async (): Promise => { - const response = await cortexApi.get('/services/status') - return response.data - }, -} - -// ── Co-Navigator: Predictions ── -export const predictionsApi = { - getCurrent: async (): Promise => { - const response = await cortexApi.get('/predictions/current') - return response.data - }, - recordDecision: async (decision: { - prediction_id: string - scenario_chosen: string - scenario_name: string - domain: string - override_reason?: string - }): Promise => { - const response = await cortexApi.post('/decisions/record', decision) - return response.data - }, -} - -// ── Co-Navigator: Activity ── -export const activityApi = { - getHeatmap: async (): Promise => { - const response = await cortexApi.get('/activity/heatmap') - return response.data - }, -} - -export default { - health: healthApi, - serviceHealth: serviceHealthApi, - batch: batchApi, - queue: queueApi, - metrics: metricsApi, - status: statusApi, - intelligence: intelligenceApi, - anomaly: anomalyApi, - recommendations: recommendationsApi, - projects: projectsApi, - sessions: sessionsApi, - taskBoard: taskBoardApi, - conductor: conductorApi, - vortexHealth: vortexHealthApi, - vortexScheduler: vortexSchedulerApi, - vortexModels: vortexModelsApi, - docs: docsApi, - services: servicesApi, - predictions: predictionsApi, - activity: activityApi, -} diff --git a/_contrib/site/src/api/hooks.ts b/_contrib/site/src/api/hooks.ts deleted file mode 100644 index 887b68a..0000000 --- a/_contrib/site/src/api/hooks.ts +++ /dev/null @@ -1,398 +0,0 @@ -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' -import { - healthApi, - serviceHealthApi, - batchApi, - queueApi, - metricsApi, - anomalyApi, - recommendationsApi, - intelligenceApi, - projectsApi, - sessionsApi, - taskBoardApi, - conductorApi, - vortexHealthApi, - vortexSchedulerApi, - vortexModelsApi, - docsApi, - servicesApi, - predictionsApi, - activityApi, -} from './client' -import type { PendingTask } from './types' -import type { IntelligenceQuery } from '@/types/cortex' -import type { SchedulerStatusRaw } from '@/types/vortex' -import type { Task } from '@/types/task' - -export const queryKeys = { - health: ['health'] as const, - serviceHealth: ['serviceHealth'] as const, - batches: ['batches'] as const, - batch: (id: string) => ['batch', id] as const, - queue: ['queue'] as const, - metrics: (days: number) => ['metrics', days] as const, - anomalies: ['anomalies'] as const, - recommendations: ['recommendations'] as const, - projects: ['projects'] as const, - sessions: ['sessions'] as const, - taskboard: ['taskboard'] as const, - conductorHistory: (limit: number) => ['conductor', 'history', limit] as const, - vortexHealth: ['vortex', 'health'] as const, - vortexScheduler: ['vortex', 'scheduler'] as const, - vortexModels: ['vortex', 'models'] as const, - docsTree: ['docs', 'tree'] as const, - docContent: (path: string) => ['docs', 'content', path] as const, - servicesStatus: ['services', 'status'] as const, - predictions: ['predictions'] as const, - activityHeatmap: ['activity', 'heatmap'] as const, -} - -// ── Cortex: Health ── -export function useHealthQuery() { - return useQuery({ - queryKey: queryKeys.health, - queryFn: healthApi.getHealth, - staleTime: 1000 * 60, - refetchInterval: 1000 * 60, - retry: 1, - refetchOnWindowFocus: true, - }) -} - -// ── Cortex: Service Health ── -export function useServiceHealthQuery() { - return useQuery({ - queryKey: queryKeys.serviceHealth, - queryFn: serviceHealthApi.getServiceHealth, - staleTime: 1000 * 30, - refetchInterval: 1000 * 30, - retry: 1, - refetchOnWindowFocus: true, - }) -} - -// ── Cortex: Batches ── -export function useBatchesQuery(limit = 20) { - return useQuery({ - queryKey: queryKeys.batches, - queryFn: () => batchApi.listBatches(limit), - staleTime: 1000 * 30, - refetchInterval: 1000 * 30, - retry: 2, - refetchOnWindowFocus: true, - }) -} - -export function useBatchStatusQuery(batchId: string, enabled = true) { - return useQuery({ - queryKey: queryKeys.batch(batchId), - queryFn: () => batchApi.getBatchStatus(batchId), - enabled: enabled && !!batchId, - staleTime: 1000 * 5, - refetchInterval: (query) => { - if (query.state.data?.status === 'ended') return false - return 1000 * 5 - }, - retry: 2, - }) -} - -// ── Cortex: Queue ── -export function useQueueQuery() { - return useQuery({ - queryKey: queryKeys.queue, - queryFn: queueApi.getQueue, - staleTime: 1000 * 10, - refetchInterval: 1000 * 10, - retry: 2, - refetchOnWindowFocus: true, - }) -} - -// ── Cortex: Metrics ── -export function useMetricsQuery(days = 7) { - return useQuery({ - queryKey: queryKeys.metrics(days), - queryFn: () => metricsApi.getMetrics(days), - staleTime: 1000 * 60 * 5, - refetchInterval: 1000 * 60 * 5, - retry: 2, - }) -} - -// ── Cortex: Anomalies ── -export function useAnomaliesQuery() { - return useQuery({ - queryKey: queryKeys.anomalies, - queryFn: anomalyApi.getAnomalies, - staleTime: 1000 * 60 * 2, - refetchInterval: 1000 * 60 * 2, - retry: 1, - }) -} - -// ── Cortex: Recommendations ── -export function useRecommendationsQuery() { - return useQuery({ - queryKey: queryKeys.recommendations, - queryFn: recommendationsApi.getRecommendations, - staleTime: 1000 * 60 * 5, - refetchInterval: 1000 * 60 * 5, - retry: 1, - }) -} - -// ── Cortex: Intelligence ── -export function useIntelligenceMutation() { - return useMutation({ - mutationFn: (payload: IntelligenceQuery) => intelligenceApi.query(payload), - }) -} - -// ── Cortex: Projects ── -export function useProjectsQuery() { - return useQuery({ - queryKey: queryKeys.projects, - queryFn: projectsApi.getProjects, - staleTime: 1000 * 60 * 2, - refetchInterval: 1000 * 60 * 2, - retry: 1, - }) -} - -// ── Vortex: Health ── -export function useVortexHealthQuery() { - return useQuery({ - queryKey: queryKeys.vortexHealth, - queryFn: vortexHealthApi.getHealth, - staleTime: 1000 * 30, - refetchInterval: 1000 * 30, - retry: 1, - }) -} - -// ── Vortex: Scheduler ── -export function useVortexSchedulerQuery() { - return useQuery({ - queryKey: queryKeys.vortexScheduler, - queryFn: vortexSchedulerApi.getScheduler, - staleTime: 1000 * 30, - refetchInterval: 1000 * 30, - retry: 1, - }) -} - -// ── Vortex: Models ── -export function useVortexModelsQuery() { - return useQuery({ - queryKey: queryKeys.vortexModels, - queryFn: vortexModelsApi.getPerformance, - staleTime: 1000 * 60, - refetchInterval: 1000 * 60, - retry: 1, - }) -} - -// ── Mutations ── -export function useAddTaskMutation() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: (task: Omit) => - queueApi.addTask(task), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: queryKeys.queue }) - }, - }) -} - -export function useUpdateTaskPriorityMutation() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: ({ taskId, priority }: { taskId: string; priority: PendingTask['priority'] }) => - queueApi.updateTaskPriority(taskId, priority), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: queryKeys.queue }) - }, - }) -} - -export function useDeleteTaskMutation() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: (taskId: string) => queueApi.deleteTask(taskId), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: queryKeys.queue }) - }, - }) -} - -export function useCancelBatchMutation() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: (batchId: string) => batchApi.cancelBatch(batchId), - onSuccess: (_, batchId) => { - queryClient.invalidateQueries({ queryKey: queryKeys.batch(batchId) }) - queryClient.invalidateQueries({ queryKey: queryKeys.batches }) - }, - }) -} - -export function usePrefetchBatchStatus() { - const queryClient = useQueryClient() - return (batchId: string) => { - queryClient.prefetchQuery({ - queryKey: queryKeys.batch(batchId), - queryFn: () => batchApi.getBatchStatus(batchId), - staleTime: 1000 * 5, - }) - } -} - -// ── Cortex: Sessions ── -export function useSessionsQuery(activeOnly = false) { - return useQuery({ - queryKey: queryKeys.sessions, - queryFn: () => sessionsApi.getSessions(activeOnly), - staleTime: 1000 * 10, - refetchInterval: 1000 * 15, - retry: 1, - refetchOnWindowFocus: true, - }) -} - -// ── Cortex: TaskBoard ── -export function useTaskBoardQuery(filters?: { status?: string; project?: string; priority?: string }) { - return useQuery({ - queryKey: queryKeys.taskboard, - queryFn: () => taskBoardApi.getTasks(filters), - staleTime: 1000 * 5, - refetchInterval: 1000 * 10, - retry: 2, - refetchOnWindowFocus: true, - }) -} - -export function useCreateTaskBoardTaskMutation() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: (task: Parameters[0]) => - taskBoardApi.createTask(task), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: queryKeys.taskboard }) - }, - }) -} - -export function useUpdateTaskBoardTaskMutation() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: ({ - taskId, - update, - }: { - taskId: string - update: Partial> - }) => taskBoardApi.updateTask(taskId, update), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: queryKeys.taskboard }) - }, - }) -} - -export function useDeleteTaskBoardTaskMutation() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: (taskId: string) => taskBoardApi.deleteTask(taskId), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: queryKeys.taskboard }) - }, - }) -} - -// ── Conductor: Prompt History ── -export function useConductorHistoryQuery(limit = 5) { - return useQuery({ - queryKey: queryKeys.conductorHistory(limit), - queryFn: () => conductorApi.getPromptHistory(limit), - staleTime: 1000 * 30, - refetchInterval: 1000 * 60, - retry: 1, - }) -} - -export function useDecomposeTaskMutation() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: (req: Parameters[0]) => - taskBoardApi.decompose(req), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: queryKeys.taskboard }) - }, - }) -} - -// ── Co-Navigator: Docs ── -export function useDocsTreeQuery() { - return useQuery({ - queryKey: queryKeys.docsTree, - queryFn: docsApi.getTree, - staleTime: 1000 * 60 * 5, - refetchInterval: 1000 * 60 * 5, - retry: 1, - }) -} - -export function useDocContentQuery(path: string, enabled = true) { - return useQuery({ - queryKey: queryKeys.docContent(path), - queryFn: () => docsApi.getContent(path), - enabled: enabled && !!path, - staleTime: 1000 * 60, - retry: 1, - }) -} - -// ── Co-Navigator: Services ── -export function useServicesStatusQuery() { - return useQuery({ - queryKey: queryKeys.servicesStatus, - queryFn: servicesApi.getStatus, - staleTime: 1000 * 10, - refetchInterval: 1000 * 10, - retry: 1, - refetchOnWindowFocus: true, - }) -} - -// ── Co-Navigator: Predictions ── -export function usePredictionsQuery() { - return useQuery({ - queryKey: queryKeys.predictions, - queryFn: predictionsApi.getCurrent, - staleTime: 1000 * 60, - refetchInterval: 1000 * 60, - retry: 1, - }) -} - -export function useRecordDecisionMutation() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: predictionsApi.recordDecision, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: queryKeys.predictions }) - }, - }) -} - -// ── Co-Navigator: Activity ── -export function useActivityHeatmapQuery() { - return useQuery({ - queryKey: queryKeys.activityHeatmap, - queryFn: activityApi.getHeatmap, - staleTime: 1000 * 60 * 5, - refetchInterval: 1000 * 60 * 5, - retry: 1, - }) -} diff --git a/_contrib/site/src/api/index.ts b/_contrib/site/src/api/index.ts deleted file mode 100644 index 4dca547..0000000 --- a/_contrib/site/src/api/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -export { cortexApi, healthApi, batchApi, queueApi, metricsApi } from './client' -export { default as api } from './client' - -export { - useHealthQuery, - useBatchesQuery, - useBatchStatusQuery, - useQueueQuery, - useMetricsQuery, - useAddTaskMutation, - useUpdateTaskPriorityMutation, - useDeleteTaskMutation, - useCancelBatchMutation, - usePrefetchBatchStatus, - queryKeys, -} from './hooks' - -export type { - BatchStatus, - PendingTask, - UsageMetrics, - HealthResponse, - APIError, -} from './types' diff --git a/_contrib/site/src/api/types.ts b/_contrib/site/src/api/types.ts deleted file mode 100644 index 990fef9..0000000 --- a/_contrib/site/src/api/types.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * API Types for Cortex Batch Command Center - * - * Response types matching the Cortex backend batch API. - */ - -export interface BatchStatus { - id: string - status: 'in_progress' | 'validating' | 'finalizing' | 'ended' - created_at: string - ended_at?: string - request_counts: { - processing: number - succeeded: number - errored: number - expired: number - canceled: number - } -} - -export interface PendingTask { - id: string - title: string - description: string - priority: 'CRITICAL' | 'HIGH' | 'NORMAL' | 'LOW' - status: 'queued' | 'submitted' | 'completed' - estimated_tokens: number - submit_after?: string - deadline?: string - created_at: string - source?: string - blocked_by?: string[] -} - -export interface UsageMetrics { - days: number - total_tasks: number - total_tokens: number - by_priority: Record - by_source?: Record - cost_estimate: { - real_time_would_be: number - batch_actual: number - savings: number - } -} - -export interface HealthResponse { - status: 'healthy' | 'degraded' | 'down' - version?: string - timestamp: string -} - -export interface APIError { - error: string - message: string - status?: number -} diff --git a/_contrib/site/src/design-system/components/Button.tsx b/_contrib/site/src/design-system/components/Button.tsx deleted file mode 100644 index a8b0686..0000000 --- a/_contrib/site/src/design-system/components/Button.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { forwardRef, type ButtonHTMLAttributes } from 'react' -import { cn } from '@/utils/cn' - -export interface ButtonProps extends ButtonHTMLAttributes { - variant?: 'primary' | 'secondary' | 'ghost' | 'danger' - size?: 'sm' | 'md' | 'lg' -} - -/** - * Military styled button component - * Tactical, functional design with status-based variants - */ -export const Button = forwardRef( - ({ className, variant = 'primary', size = 'md', children, disabled, ...props }, ref) => { - const baseStyles = 'inline-flex items-center justify-center font-mono font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-cortex-accent focus:ring-offset-2 focus:ring-offset-cortex-bg disabled:opacity-50 disabled:cursor-not-allowed' - - const variantStyles = { - primary: 'bg-cortex-accent hover:bg-cortex-accent-muted text-white border border-cortex-accent', - secondary: 'bg-cortex-elevated hover:bg-cortex-border text-cortex-text-primary border border-cortex-border', - ghost: 'bg-transparent hover:bg-cortex-surface text-cortex-text-secondary hover:text-cortex-text-primary', - danger: 'bg-cortex-critical hover:bg-cortex-critical-muted text-white border border-cortex-critical', - } - - const sizeStyles = { - sm: 'px-3 py-1.5 text-sm rounded', - md: 'px-4 py-2 text-base rounded-md', - lg: 'px-6 py-3 text-lg rounded-lg', - } - - return ( - - ) - } -) - -Button.displayName = 'Button' diff --git a/_contrib/site/src/design-system/components/Card.tsx b/_contrib/site/src/design-system/components/Card.tsx deleted file mode 100644 index a15b253..0000000 --- a/_contrib/site/src/design-system/components/Card.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { forwardRef, type HTMLAttributes, type ReactNode } from 'react' -import { cn } from '@/utils/cn' - -export interface CardProps extends HTMLAttributes { - variant?: 'default' | 'elevated' | 'outlined' | 'accent' - padding?: 'none' | 'sm' | 'md' | 'lg' -} - -/** - * Card component for military command center interface - * Dark themed container with subtle borders and shadows - */ -export const Card = forwardRef( - ({ className, variant = 'default', padding = 'md', children, ...props }, ref) => { - const variantStyles = { - default: 'bg-cortex-surface border border-cortex-border', - elevated: 'bg-cortex-elevated border border-cortex-border shadow-elevated', - outlined: 'bg-transparent border border-cortex-border', - accent: 'bg-cortex-surface border border-cortex-border border-l-[3px] border-l-cortex-cyan', - } - - const paddingStyles = { - none: '', - sm: 'p-3', - md: 'p-4', - lg: 'p-6', - } - - return ( -
- {children} -
- ) - } -) - -Card.displayName = 'Card' - -// Card Header -export interface CardHeaderProps extends HTMLAttributes { - title: string - subtitle?: string - action?: ReactNode -} - -export const CardHeader = forwardRef( - ({ className, title, subtitle, action, ...props }, ref) => ( -
-
-

- {title} -

- {subtitle && ( -

{subtitle}

- )} -
- {action &&
{action}
} -
- ) -) - -CardHeader.displayName = 'CardHeader' - -// Card Content -export const CardContent = forwardRef>( - ({ className, ...props }, ref) => ( -
- ) -) - -CardContent.displayName = 'CardContent' - -// Card Footer -export const CardFooter = forwardRef>( - ({ className, ...props }, ref) => ( -
- ) -) - -CardFooter.displayName = 'CardFooter' diff --git a/_contrib/site/src/design-system/components/MetricDisplay.tsx b/_contrib/site/src/design-system/components/MetricDisplay.tsx deleted file mode 100644 index 1c07cbd..0000000 --- a/_contrib/site/src/design-system/components/MetricDisplay.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { forwardRef, type HTMLAttributes, type ReactNode } from 'react' -import { cn } from '@/utils/cn' - -export interface MetricDisplayProps extends HTMLAttributes { - label: string - value: string | number - sublabel?: string - icon?: ReactNode - trend?: 'up' | 'down' | 'neutral' - trendValue?: string - color?: string -} - -/** - * Large metric display for command center dashboards - * Shows primary value with label, optional trend, and icon - */ -export const MetricDisplay = forwardRef( - ({ - className, - label, - value, - sublabel, - icon, - trend, - trendValue, - color = '#F9FAFB', - ...props - }, ref) => { - const trendColors = { - up: '#10B981', // cortex-nominal - down: '#EF4444', // cortex-critical - neutral: '#9CA3AF', // cortex-text-secondary - } - - const trendIcons = { - up: '↑', - down: '↓', - neutral: '→', - } - - return ( -
- {/* Label and Icon */} -
- {icon &&
{icon}
} - - {label} - -
- - {/* Main Value */} -
- - {value} - - - {/* Trend Indicator */} - {trend && trendValue && ( - - {trendIcons[trend]} {trendValue} - - )} -
- - {/* Sublabel */} - {sublabel && ( -

- {sublabel} -

- )} -
- ) - } -) - -MetricDisplay.displayName = 'MetricDisplay' diff --git a/_contrib/site/src/design-system/components/PanelContainer.tsx b/_contrib/site/src/design-system/components/PanelContainer.tsx deleted file mode 100644 index 511d381..0000000 --- a/_contrib/site/src/design-system/components/PanelContainer.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { cn } from '@/utils/cn' -import { ScanningBorder } from './ScanningBorder' - -interface PanelContainerProps { - title: string - count?: number - children: React.ReactNode - className?: string - scanning?: boolean - accent?: boolean -} - -export function PanelContainer({ - title, - count, - children, - className, - scanning = false, - accent = true, -}: PanelContainerProps) { - return ( -
- {scanning && } -
-
-

- {title} -

- {count !== undefined && ( - - {count} - - )} -
- {children} -
-
- ) -} diff --git a/_contrib/site/src/design-system/components/PriorityBadge.tsx b/_contrib/site/src/design-system/components/PriorityBadge.tsx deleted file mode 100644 index 40b8ba7..0000000 --- a/_contrib/site/src/design-system/components/PriorityBadge.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { forwardRef, type HTMLAttributes } from 'react' -import { cn } from '@/utils/cn' -import { getPriorityColor, type PriorityLevel } from '../tokens' - -export interface PriorityBadgeProps extends HTMLAttributes { - priority: PriorityLevel - showIcon?: boolean -} - -/** - * Priority badge for tactical command center - * Color-coded priority levels: CRITICAL, HIGH, MEDIUM, LOW - */ -export const PriorityBadge = forwardRef( - ({ className, priority, showIcon = true, ...props }, ref) => { - const color = getPriorityColor(priority) - - const priorityIcons: Record = { - CRITICAL: '⚠', - HIGH: '▲', - MEDIUM: '●', - LOW: '▽', - } - - return ( -
- {showIcon && ( - - )} - {priority} -
- ) - } -) - -PriorityBadge.displayName = 'PriorityBadge' diff --git a/_contrib/site/src/design-system/components/ProgressRing.tsx b/_contrib/site/src/design-system/components/ProgressRing.tsx deleted file mode 100644 index f39d180..0000000 --- a/_contrib/site/src/design-system/components/ProgressRing.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { forwardRef, type HTMLAttributes } from 'react' -import { cn } from '@/utils/cn' - -export interface ProgressRingProps extends HTMLAttributes { - progress: number // 0-1 - size?: number // diameter in pixels - strokeWidth?: number - color?: string - backgroundColor?: string - showPercentage?: boolean -} - -/** - * SVG-based circular progress indicator - * Clean, military-styled progress ring with optional percentage display - */ -export const ProgressRing = forwardRef( - ({ - className, - progress, - size = 80, - strokeWidth = 6, - color = '#3B82F6', // cortex-processing - backgroundColor = '#2A2D3A', // cortex-border - showPercentage = true, - ...props - }, ref) => { - const normalizedProgress = Math.min(Math.max(progress, 0), 1) - const radius = (size - strokeWidth) / 2 - const circumference = radius * 2 * Math.PI - const offset = circumference - normalizedProgress * circumference - - const percentage = Math.round(normalizedProgress * 100) - - return ( -
- {/* SVG Progress Ring */} - - {/* Background circle */} - - - {/* Progress circle */} - - - - {/* Centered percentage text */} - {showPercentage && ( -
- - {percentage}% - -
- )} -
- ) - } -) - -ProgressRing.displayName = 'ProgressRing' diff --git a/_contrib/site/src/design-system/components/ScanningBorder.tsx b/_contrib/site/src/design-system/components/ScanningBorder.tsx deleted file mode 100644 index 7968d9d..0000000 --- a/_contrib/site/src/design-system/components/ScanningBorder.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { cn } from '@/utils/cn' - -interface ScanningBorderProps { - className?: string - color?: string -} - -export function ScanningBorder({ className, color }: ScanningBorderProps) { - return ( -
-
-
-
- ) -} diff --git a/_contrib/site/src/design-system/components/SectionHeader.tsx b/_contrib/site/src/design-system/components/SectionHeader.tsx deleted file mode 100644 index cfcb448..0000000 --- a/_contrib/site/src/design-system/components/SectionHeader.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { cn } from '@/utils/cn' - -interface SectionHeaderProps { - title: string - count?: number - className?: string - actions?: React.ReactNode -} - -export function SectionHeader({ title, count, className, actions }: SectionHeaderProps) { - return ( -
-
-

- {title} -

- {count !== undefined && ( - - {count} - - )} -
- {actions &&
{actions}
} -
- ) -} diff --git a/_contrib/site/src/design-system/components/SeverityIndicator.tsx b/_contrib/site/src/design-system/components/SeverityIndicator.tsx deleted file mode 100644 index 335b6d0..0000000 --- a/_contrib/site/src/design-system/components/SeverityIndicator.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { cn } from '@/utils/cn' - -export type Severity = 'CRITICAL' | 'WARNING' | 'INFO' - -interface SeverityIndicatorProps { - severity: Severity - className?: string - pulse?: boolean -} - -const severityConfig: Record = { - CRITICAL: { - bg: 'bg-cortex-critical-muted', - text: 'text-cortex-critical', - border: 'border-cortex-critical/30', - }, - WARNING: { - bg: 'bg-cortex-warning-muted', - text: 'text-cortex-warning', - border: 'border-cortex-warning/30', - }, - INFO: { - bg: 'bg-cortex-processing-muted', - text: 'text-cortex-processing', - border: 'border-cortex-processing/30', - }, -} - -export function SeverityIndicator({ severity, className, pulse = false }: SeverityIndicatorProps) { - const config = severityConfig[severity] - - return ( - - - {severity} - - ) -} diff --git a/_contrib/site/src/design-system/components/StatusBadge.tsx b/_contrib/site/src/design-system/components/StatusBadge.tsx deleted file mode 100644 index edf6ad5..0000000 --- a/_contrib/site/src/design-system/components/StatusBadge.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { forwardRef, type HTMLAttributes } from 'react' -import { cn } from '@/utils/cn' -import { getStatusColor, isActiveStatus, type StatusType } from '../tokens' - -export interface StatusBadgeProps extends HTMLAttributes { - status: StatusType - showPulse?: boolean -} - -/** - * Status badge with optional pulse animation for active states - * Military command center style with status color coding - */ -export const StatusBadge = forwardRef( - ({ className, status, showPulse = true, ...props }, ref) => { - const color = getStatusColor(status) - const shouldPulse = showPulse && isActiveStatus(status) - - return ( -
- {/* Status indicator dot with optional pulse */} - - {shouldPulse && ( - - )} - - - - {/* Status text */} - {status} -
- ) - } -) - -StatusBadge.displayName = 'StatusBadge' diff --git a/_contrib/site/src/design-system/components/TimeDisplay.tsx b/_contrib/site/src/design-system/components/TimeDisplay.tsx deleted file mode 100644 index 185f453..0000000 --- a/_contrib/site/src/design-system/components/TimeDisplay.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { useState, useEffect } from 'react' -import { cn } from '@/utils/cn' - -interface TimeDisplayProps { - className?: string - showDate?: boolean -} - -export function TimeDisplay({ className, showDate = false }: TimeDisplayProps) { - const [now, setNow] = useState(new Date()) - - useEffect(() => { - const timer = setInterval(() => setNow(new Date()), 1000) - return () => clearInterval(timer) - }, []) - - const formatUTC = (d: Date) => { - const h = d.getUTCHours().toString().padStart(2, '0') - const m = d.getUTCMinutes().toString().padStart(2, '0') - const s = d.getUTCSeconds().toString().padStart(2, '0') - return `${h}:${m}:${s}` - } - - const formatDate = (d: Date) => { - const y = d.getUTCFullYear() - const m = (d.getUTCMonth() + 1).toString().padStart(2, '0') - const day = d.getUTCDate().toString().padStart(2, '0') - return `${y}-${m}-${day}` - } - - return ( -
- UTC - {showDate && {formatDate(now)}} - {formatUTC(now)} -
- ) -} diff --git a/_contrib/site/src/design-system/components/index.ts b/_contrib/site/src/design-system/components/index.ts deleted file mode 100644 index 2417545..0000000 --- a/_contrib/site/src/design-system/components/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Cortex Mission Control Design System Components - * Military/Tactical command center themed React components - */ - -export { Card, CardHeader, CardContent, CardFooter } from './Card' -export type { CardProps, CardHeaderProps } from './Card' - -export { Button } from './Button' -export type { ButtonProps } from './Button' - -export { StatusBadge } from './StatusBadge' -export type { StatusBadgeProps } from './StatusBadge' - -export { ProgressRing } from './ProgressRing' -export type { ProgressRingProps } from './ProgressRing' - -export { MetricDisplay } from './MetricDisplay' -export type { MetricDisplayProps } from './MetricDisplay' - -export { PriorityBadge } from './PriorityBadge' -export type { PriorityBadgeProps } from './PriorityBadge' - -export { ScanningBorder } from './ScanningBorder' -export { SectionHeader } from './SectionHeader' -export { PanelContainer } from './PanelContainer' -export { SeverityIndicator } from './SeverityIndicator' -export type { Severity } from './SeverityIndicator' -export { TimeDisplay } from './TimeDisplay' diff --git a/_contrib/site/src/design-system/tokens.ts b/_contrib/site/src/design-system/tokens.ts deleted file mode 100644 index a373ac7..0000000 --- a/_contrib/site/src/design-system/tokens.ts +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Cortex Batch Command Center Design Tokens - * Military/Tactical Command Center Theme - * Inspired by Anduril Lattice and mission control interfaces - */ - -export const colors = { - // Background hierarchy (near black military aesthetic) - bg: '#05050A', - surface: '#0F1117', - elevated: '#1A1D27', - border: '#2A2D3A', - - // Status colors (tactical operations) - nominal: '#10B981', // green - systems nominal - nominalMuted: '#065F46', - warning: '#F59E0B', // amber - attention required - warningMuted: '#92400E', - critical: '#EF4444', // red - critical alert - criticalMuted: '#991B1B', - processing: '#3B82F6', // blue - processing/active - processingMuted: '#1E40AF', - aiSuggestion: '#A855F7', // purple - AI recommendations - aiSuggestionMuted: '#6B21A8', - - // Text hierarchy - textPrimary: '#F9FAFB', // near white - textSecondary: '#9CA3AF', // gray-400 - textMuted: '#6B7280', // gray-500 - - // Primary accent — Palantir cyan - cyan: '#06b6d4', - cyanMuted: '#164e63', - cyanGlow: 'rgba(6, 182, 212, 0.15)', - - // Accent (violet — kept for AI suggestions) - accent: '#8B5CF6', - accentMuted: '#6D28D9', -} as const - -export const spacing = { - xs: 4, - sm: 8, - md: 16, - lg: 24, - xl: 32, - '2xl': 48, - panelGap: 16, - sectionGap: 24, -} as const - -export const typography = { - fontFamily: { - sans: 'Inter, system-ui, sans-serif', - mono: 'JetBrains Mono, monospace', - display: 'Orbitron, monospace', - data: 'Share Tech Mono, monospace', - }, - fontSize: { - xs: '0.75rem', // 12px - sm: '0.875rem', // 14px - base: '1rem', // 16px - lg: '1.125rem', // 18px - xl: '1.25rem', // 20px - '2xl': '1.5rem', // 24px - '3xl': '1.875rem', // 30px - metric: '2.5rem', // 40px - for large metrics - hero: '3rem', // 48px - for hero metrics - }, - fontWeight: { - normal: 400, - medium: 500, - semibold: 600, - bold: 700, - }, -} as const - -export const animation = { - fast: '150ms', - normal: '300ms', - slow: '500ms', - pulse: '1500ms', - easing: { - default: 'cubic-bezier(0.4, 0, 0.2, 1)', - in: 'cubic-bezier(0.4, 0, 1, 1)', - out: 'cubic-bezier(0, 0, 0.2, 1)', - inOut: 'cubic-bezier(0.4, 0, 0.2, 1)', - }, -} as const - -// Priority levels for batch jobs -export const priorityLevels = { - CRITICAL: 'CRITICAL', - HIGH: 'HIGH', - MEDIUM: 'MEDIUM', - LOW: 'LOW', -} as const - -export type PriorityLevel = keyof typeof priorityLevels - -// Status types for batch jobs -export const statusTypes = { - QUEUED: 'QUEUED', - PROCESSING: 'PROCESSING', - COMPLETED: 'COMPLETED', - FAILED: 'FAILED', - CANCELLED: 'CANCELLED', -} as const - -export type StatusType = keyof typeof statusTypes - -/** - * Get color for priority level - */ -export function getPriorityColor(priority: PriorityLevel): string { - switch (priority) { - case 'CRITICAL': - return colors.critical - case 'HIGH': - return colors.warning - case 'MEDIUM': - return colors.processing - case 'LOW': - return colors.textSecondary - } -} - -/** - * Get color for status type - */ -export function getStatusColor(status: StatusType): string { - switch (status) { - case 'QUEUED': - return colors.textSecondary - case 'PROCESSING': - return colors.processing - case 'COMPLETED': - return colors.nominal - case 'FAILED': - return colors.critical - case 'CANCELLED': - return colors.textMuted - } -} - -/** - * Check if status is active (should show pulse animation) - */ -export function isActiveStatus(status: StatusType): boolean { - return status === 'PROCESSING' || status === 'QUEUED' -} diff --git a/_contrib/site/src/index.css b/_contrib/site/src/index.css deleted file mode 100644 index f586fd7..0000000 --- a/_contrib/site/src/index.css +++ /dev/null @@ -1,74 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - * { - @apply border-cortex-border; - } - - body { - @apply bg-cortex-bg text-cortex-text-primary; - font-family: 'Inter', system-ui, sans-serif; - } - - code, pre { - font-family: 'JetBrains Mono', monospace; - } -} - -@layer components { - /* Scanning border — animated cyan line across top of panels */ - .scanning-border { - @apply relative overflow-hidden; - } - - .scanning-border::before { - content: ''; - @apply absolute top-0 h-[2px] w-[60px] bg-gradient-to-r from-transparent via-cortex-cyan to-transparent; - animation: scan 3s ease-in-out infinite; - } - - /* Panel accent — 3px left cyan border */ - .panel-accent { - @apply border-l-[3px] border-l-cortex-cyan; - } - - /* Cyan glow effect */ - .glow-cyan { - box-shadow: 0 0 15px rgba(6, 182, 212, 0.2), 0 0 30px rgba(6, 182, 212, 0.05); - } - - /* Section divider with military styling */ - .section-divider { - @apply border-t border-cortex-border/50 my-4; - } -} - -@layer utilities { - /* Custom scrollbar for dark theme */ - .scrollbar-thin::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - .scrollbar-thin::-webkit-scrollbar-track { - @apply bg-cortex-surface; - } - - .scrollbar-thin::-webkit-scrollbar-thumb { - @apply bg-cortex-border rounded; - } - - .scrollbar-thin::-webkit-scrollbar-thumb:hover { - @apply bg-cortex-text-muted; - } - - /* Tactical grid pattern for backgrounds */ - .tactical-grid { - background-image: - linear-gradient(rgba(42, 45, 58, 0.1) 1px, transparent 1px), - linear-gradient(90deg, rgba(42, 45, 58, 0.1) 1px, transparent 1px); - background-size: 20px 20px; - } -} diff --git a/_contrib/site/src/layouts/CoNavigatorLayout.tsx b/_contrib/site/src/layouts/CoNavigatorLayout.tsx deleted file mode 100644 index dd8a817..0000000 --- a/_contrib/site/src/layouts/CoNavigatorLayout.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { Outlet, useNavigate, useLocation } from 'react-router-dom' -import { ScanningBorder } from '@/design-system/components/ScanningBorder' -import { TimeDisplay } from '@/design-system/components/TimeDisplay' -import { useHealthQuery } from '@/api/hooks' -import { useNavigatorStore } from '@/stores/navigatorStore' -import { cn } from '@/utils/cn' -import type { NavigatorMode } from '@/types/navigator' -import { useEffect } from 'react' - -const modes: { id: NavigatorMode; label: string; icon: string; key: string }[] = [ - { id: 'briefing', label: 'BRIEFING', icon: '◈', key: '1' }, - { id: 'navigate', label: 'NAVIGATE', icon: '▷', key: '2' }, - { id: 'monitor', label: 'MONITOR', icon: '◎', key: '3' }, - { id: 'explore', label: 'EXPLORE', icon: '⬡', key: '4' }, -] - -const modeToPath: Record = { - briefing: '/briefing', - navigate: '/navigate', - monitor: '/monitor', - explore: '/explore', -} - -const pathToMode: Record = { - '/': 'navigate', - '/briefing': 'briefing', - '/navigate': 'navigate', - '/monitor': 'monitor', - '/explore': 'explore', -} - -export function CoNavigatorLayout() { - const { data: health } = useHealthQuery() - const { currentMode, autoMode, setMode } = useNavigatorStore() - const navigate = useNavigate() - const location = useLocation() - - const isHealthy = health?.status === 'healthy' - - // Sync URL → store on mount/navigation - useEffect(() => { - const modeFromPath = pathToMode[location.pathname] - if (modeFromPath && modeFromPath !== currentMode) { - setMode(modeFromPath) - } - }, [location.pathname]) - - // Keyboard shortcuts: 1-4 for modes - useEffect(() => { - const handler = (e: KeyboardEvent) => { - if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return - const mode = modes.find((m) => m.key === e.key) - if (mode) { - setMode(mode.id) - navigate(modeToPath[mode.id]) - } - } - window.addEventListener('keydown', handler) - return () => window.removeEventListener('keydown', handler) - }, [navigate, setMode]) - - const handleModeClick = (mode: NavigatorMode) => { - setMode(mode) - navigate(modeToPath[mode]) - } - - return ( -
- {/* Header */} -
- -
- {/* Left: status */} -
-
-
- - {isHealthy ? 'NOMINAL' : 'DEGRADED'} - -
-
- - {/* Center: title */} -

- CORTEX CO-NAVIGATOR -

- - {/* Right: auto indicator + time */} -
- {autoMode && ( - - AUTO - - )} - -
-
- - {/* Mode tabs */} -
- {modes.map((mode) => { - const isActive = currentMode === mode.id - return ( - - ) - })} - - {/* Legacy link */} - -
-
- - {/* Main content */} -
- -
-
- ) -} diff --git a/_contrib/site/src/layouts/CommandCenterLayout.tsx b/_contrib/site/src/layouts/CommandCenterLayout.tsx deleted file mode 100644 index 36aa391..0000000 --- a/_contrib/site/src/layouts/CommandCenterLayout.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Command Center Layout - * Main 3-column responsive layout for the Cortex Batch Command Center - * - * Layout Structure: - * ┌─────────────────────────────────────────────────────────────────────┐ - * │ STATUS BAR │ - * ├──────────────────┬────────────────────────┬─────────────────────────┤ - * │ │ │ │ - * │ RUNNING PANEL │ PENDING PANEL │ COMPLETED PANEL │ - * │ (Left - 25%) │ (Center - 50%) │ (Right - 25%) │ - * │ │ │ │ - * │ │ │ │ - * ├──────────────────┴────────────────────────┴─────────────────────────┤ - * │ BOTTOM ACTION BAR │ - * └─────────────────────────────────────────────────────────────────────┘ - */ - -import { type ReactNode } from 'react' -import { colors } from '@/design-system/tokens' - -export interface CommandCenterLayoutProps { - statusBar: ReactNode - bottomBar: ReactNode - children: [ReactNode, ReactNode, ReactNode] // [RunningPanel, PendingPanel, CompletedPanel] -} - -export function CommandCenterLayout({ - statusBar, - bottomBar, - children, -}: CommandCenterLayoutProps) { - const [runningPanel, pendingPanel, completedPanel] = children - - return ( -
- {/* Status Bar - Fixed Top */} -
- {statusBar} -
- - {/* Main Content - 3 Column Grid */} -
-
- {/* Left Panel - Running (25%) */} -
- {runningPanel} -
- - {/* Center Panel - Pending (50%) */} -
- {pendingPanel} -
- - {/* Right Panel - Completed (25%) */} -
- {completedPanel} -
-
-
- - {/* Bottom Action Bar - Fixed Bottom */} -
- {bottomBar} -
-
- ) -} diff --git a/_contrib/site/src/layouts/MissionControlLayout.tsx b/_contrib/site/src/layouts/MissionControlLayout.tsx deleted file mode 100644 index fdfe31a..0000000 --- a/_contrib/site/src/layouts/MissionControlLayout.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { Outlet } from 'react-router-dom' -import { Sidebar } from '@/panels/shared/Sidebar' -import { ScanningBorder } from '@/design-system/components/ScanningBorder' -import { TimeDisplay } from '@/design-system/components/TimeDisplay' -import { useHealthQuery, useMetricsQuery } from '@/api/hooks' -import { formatCost } from '@/utils/formatters' - -export function MissionControlLayout() { - const { data: health } = useHealthQuery() - const { data: metrics } = useMetricsQuery(7) - - const isHealthy = health?.status === 'healthy' - const savings = metrics?.cost_estimate?.savings ?? 0 - - return ( -
- {/* Status Bar — top */} -
- -
- {/* Left: status */} -
-
-
- - {isHealthy ? 'NOMINAL' : 'DEGRADED'} - -
- | - - API: - {health ? 'CONNECTED' : 'OFFLINE'} - - -
- - {/* Center: title */} -

- CORTEX MISSION CONTROL -

- - {/* Right: time + savings */} -
- {savings > 0 && ( - - {formatCost(savings)} SAVED - - )} - -
-
-
- - {/* Main: Sidebar + Content */} -
- -
- -
-
-
- ) -} diff --git a/_contrib/site/src/main.tsx b/_contrib/site/src/main.tsx deleted file mode 100644 index 1529189..0000000 --- a/_contrib/site/src/main.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import App from './App' -import './index.css' - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - refetchOnWindowFocus: true, - retry: 1, - staleTime: 30000, - }, - }, -}) - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - - - , -) diff --git a/_contrib/site/src/panels/anomalies/AnomalyCard.tsx b/_contrib/site/src/panels/anomalies/AnomalyCard.tsx deleted file mode 100644 index 613d1ad..0000000 --- a/_contrib/site/src/panels/anomalies/AnomalyCard.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { SeverityIndicator } from '@/design-system/components' -import type { Anomaly } from '@/types/cortex' - -interface AnomalyCardProps { - anomaly: Anomaly -} - -export function AnomalyCard({ anomaly }: AnomalyCardProps) { - return ( -
-
- - - {new Date(anomaly.detected_at).toLocaleString('en-US', { hour12: false, timeZone: 'UTC' })} - -
-

{anomaly.title}

-

{anomaly.description}

-
- - {anomaly.project} - -
- {anomaly.recommendation && ( -
- RECOMMENDATION -

{anomaly.recommendation}

-
- )} -
- ) -} diff --git a/_contrib/site/src/panels/anomalies/AnomalyPanel.tsx b/_contrib/site/src/panels/anomalies/AnomalyPanel.tsx deleted file mode 100644 index fae2d6a..0000000 --- a/_contrib/site/src/panels/anomalies/AnomalyPanel.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { SectionHeader } from '@/design-system/components' -import { useAnomaliesQuery } from '@/api/hooks' -import { useAnomalyStore } from '@/stores/anomalyStore' -import { AnomalyCard } from './AnomalyCard' -import { AnomalySeverityBar } from './AnomalySeverityBar' -import type { Severity } from '@/design-system/components/SeverityIndicator' -import { cn } from '@/utils/cn' - -const severities: (Severity | null)[] = [null, 'CRITICAL', 'WARNING', 'INFO'] - -export default function AnomalyPanel() { - const { data, isLoading } = useAnomaliesQuery() - const { severityFilter, setSeverityFilter } = useAnomalyStore() - - const anomalies = data?.anomalies ?? [] - const filtered = severityFilter - ? anomalies.filter((a) => a.severity === severityFilter) - : anomalies - - return ( -
- - - {/* Severity breakdown chart */} - {data?.by_severity && } - - {/* Filter bar */} -
- {severities.map((sev) => ( - - ))} -
- - {/* Anomaly list */} - {isLoading ? ( -
- SCANNING... -
- ) : filtered.length === 0 ? ( -
- ALL CLEAR -

No anomalies detected

-
- ) : ( -
- {filtered.map((anomaly) => ( - - ))} -
- )} -
- ) -} diff --git a/_contrib/site/src/panels/anomalies/AnomalySeverityBar.tsx b/_contrib/site/src/panels/anomalies/AnomalySeverityBar.tsx deleted file mode 100644 index 4dcffd1..0000000 --- a/_contrib/site/src/panels/anomalies/AnomalySeverityBar.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Cell } from 'recharts' - -interface AnomalySeverityBarProps { - bySeverity: Record -} - -const severityColors: Record = { - CRITICAL: '#EF4444', - WARNING: '#F59E0B', - INFO: '#3B82F6', -} - -export function AnomalySeverityBar({ bySeverity }: AnomalySeverityBarProps) { - const data = Object.entries(bySeverity).map(([severity, count]) => ({ - severity, - count, - })) - - if (data.length === 0) return null - - return ( -
- - - - - - - {data.map((entry) => ( - - ))} - - - -
- ) -} diff --git a/_contrib/site/src/panels/batch/BatchDashboard.tsx b/_contrib/site/src/panels/batch/BatchDashboard.tsx deleted file mode 100644 index 350f1f6..0000000 --- a/_contrib/site/src/panels/batch/BatchDashboard.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { SectionHeader } from '@/design-system/components' -import { RunningPanel } from '@/panels/running' -import { PendingQueuePanel } from '@/panels/pending' -import { CompletedPanel } from '@/panels/completed' - -export default function BatchDashboard() { - return ( -
- - -
- {/* Running (25%) */} -
- -
- - {/* Pending (50%) */} -
- -
- - {/* Completed (25%) */} -
- -
-
-
- ) -} diff --git a/_contrib/site/src/panels/completed/CompletedJobCard.tsx b/_contrib/site/src/panels/completed/CompletedJobCard.tsx deleted file mode 100644 index 844c24e..0000000 --- a/_contrib/site/src/panels/completed/CompletedJobCard.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Completed Job Card Component - * Collapsible card showing completed batch information - */ - -import { useState } from 'react' -import { Card } from '@/design-system/components' -import { StatusBadge } from '@/design-system/components' -import { colors } from '@/design-system/tokens' -import { formatTokens, formatDuration, formatDateTime } from '@/utils/formatters' -import type { EnrichedBatchStatus } from '@/types/batch' - -export interface CompletedJobCardProps { - batch: EnrichedBatchStatus -} - -export function CompletedJobCard({ batch }: CompletedJobCardProps) { - const [isExpanded, setIsExpanded] = useState(false) - - const succeeded = batch.request_counts.succeeded - const errored = batch.request_counts.errored - const total = succeeded + errored - const successRate = total > 0 ? (succeeded / total) * 100 : 0 - - const status = errored > 0 ? 'FAILED' : 'COMPLETED' - const duration = batch.ended_at - ? new Date(batch.ended_at).getTime() - new Date(batch.created_at).getTime() - : 0 - - const totalTokens = succeeded * 1000 // Rough estimate - - return ( - setIsExpanded(!isExpanded)} - > - {/* Header */} -
-
- - {batch.id.slice(0, 12)}... - - -
- - -
- - {/* Metrics */} -
-
- Success Rate -
= 95 ? colors.nominal : successRate >= 80 ? colors.warning : colors.critical, - }} - > - {successRate.toFixed(1)}% -
-
- -
- Duration -
- {formatDuration(duration)} -
-
-
- - {/* Expanded Details */} - {isExpanded && ( -
-
- Completed - - {batch.ended_at ? formatDateTime(new Date(batch.ended_at)) : 'N/A'} - -
- -
- Tokens - - {formatTokens(totalTokens)} - -
- -
- Succeeded - {succeeded} -
- - {errored > 0 && ( -
- Errored - {errored} -
- )} - - {/* Result Preview */} -
- Batch ID: {batch.id} -
-
- )} -
- ) -} diff --git a/_contrib/site/src/panels/completed/CompletedPanel.tsx b/_contrib/site/src/panels/completed/CompletedPanel.tsx deleted file mode 100644 index 78ff58a..0000000 --- a/_contrib/site/src/panels/completed/CompletedPanel.tsx +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Completed Panel Component - * Right panel showing completed batches with filtering options - */ - -import { useState, useMemo } from 'react' -import { Card } from '@/design-system/components' -import { colors } from '@/design-system/tokens' -import { useBatchesQuery, useMetricsQuery } from '@/api/hooks' -import { enrichBatchStatus } from '@/utils/calculations' -import { formatCost } from '@/utils/formatters' -import { CompletedJobCard } from './CompletedJobCard' -import type { EnrichedBatchStatus } from '@/types/batch' - -type TimeFilter = 'today' | '7days' | '30days' - -/** - * Filter for only completed batches - */ -function filterCompletedBatches(batches: any[]): EnrichedBatchStatus[] { - return batches - .filter((b) => b.status === 'ended') - .map(enrichBatchStatus) -} - -/** - * Filter batches by time period - */ -function filterByTime(batches: EnrichedBatchStatus[], filter: TimeFilter): EnrichedBatchStatus[] { - const now = Date.now() - const cutoffTimes = { - today: now - 24 * 60 * 60 * 1000, - '7days': now - 7 * 24 * 60 * 60 * 1000, - '30days': now - 30 * 24 * 60 * 60 * 1000, - } - - const cutoff = cutoffTimes[filter] - return batches.filter((b) => { - const endTime = b.ended_at ? new Date(b.ended_at).getTime() : 0 - return endTime >= cutoff - }) -} - -export function CompletedPanel() { - const [timeFilter, setTimeFilter] = useState('7days') - const { data: batchesData, isLoading, isError } = useBatchesQuery(100) - const { data: metricsData } = useMetricsQuery(7) - - const completedBatches = useMemo(() => { - if (!batchesData || !Array.isArray(batchesData)) return [] - const completed = filterCompletedBatches(batchesData) - return filterByTime(completed, timeFilter) - }, [batchesData, timeFilter]) - - const todayCount = useMemo(() => { - if (!batchesData || !Array.isArray(batchesData)) return 0 - const completed = filterCompletedBatches(batchesData) - return filterByTime(completed, 'today').length - }, [batchesData]) - - // Calculate success rate - const successRate = useMemo(() => { - if (completedBatches.length === 0) return 100 - const total = completedBatches.reduce( - (sum, b) => sum + b.request_counts.succeeded + b.request_counts.errored, - 0 - ) - const succeeded = completedBatches.reduce( - (sum, b) => sum + b.request_counts.succeeded, - 0 - ) - return total > 0 ? (succeeded / total) * 100 : 100 - }, [completedBatches]) - - const totalSavings = metricsData?.cost_estimate?.savings ?? 0 - - const filterButtons: { value: TimeFilter; label: string }[] = [ - { value: 'today', label: 'Today' }, - { value: '7days', label: '7 Days' }, - { value: '30days', label: '30 Days' }, - ] - - return ( -
- {/* Panel Header */} - -
-

- COMPLETED -

-
- {todayCount} -
-
- - {/* Metrics */} -
-
- SUCCESS RATE - = 95 ? colors.nominal : successRate >= 80 ? colors.warning : colors.critical, - }} - > - {successRate.toFixed(1)}% - -
- -
- SAVINGS (7D) - - {formatCost(totalSavings)} - -
-
- - {/* Time Filter */} -
- {filterButtons.map((btn) => ( - - ))} -
-
- - {/* Batch List */} -
- {isLoading && ( -
-
- LOADING HISTORY... -
-
- )} - - {isError && ( - -
-
- CONNECTION FAILED -
-
- Unable to fetch batch history -
-
-
- )} - - {!isLoading && !isError && completedBatches.length === 0 && ( - -
-
-
- NO COMPLETED BATCHES -
-
- Completed batches will appear here -
-
-
- )} - - {!isLoading && !isError && completedBatches.map((batch) => ( - - ))} -
-
- ) -} diff --git a/_contrib/site/src/panels/completed/index.ts b/_contrib/site/src/panels/completed/index.ts deleted file mode 100644 index 3232c53..0000000 --- a/_contrib/site/src/panels/completed/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { CompletedPanel } from './CompletedPanel' -export { CompletedJobCard } from './CompletedJobCard' diff --git a/_contrib/site/src/panels/conductor/ConductorPanel.tsx b/_contrib/site/src/panels/conductor/ConductorPanel.tsx deleted file mode 100644 index 1439b20..0000000 --- a/_contrib/site/src/panels/conductor/ConductorPanel.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import { useState } from 'react' -import { useQuery, useMutation } from '@tanstack/react-query' -import { SectionHeader } from '@/design-system/components' -import { cn } from '@/utils/cn' -import { cortexApi } from '@/api/client' -import { StartupWizard } from './StartupWizard' -import { SessionMonitor } from './SessionMonitor' -import { PromptComposer } from './PromptComposer' -import type { ConductorStartup, PromptTemplate, ComposedPrompt, IntentLevel } from '@/types/conductor' - -type TabId = 'startup' | 'monitor' | 'composer' - -const TABS: { id: TabId; label: string; icon: string }[] = [ - { id: 'startup', label: 'STARTUP', icon: '▶' }, - { id: 'monitor', label: 'MONITOR', icon: '◎' }, - { id: 'composer', label: 'COMPOSE', icon: '◇' }, -] - -export default function ConductorPanel() { - const [activeTab, setActiveTab] = useState('startup') - - // Fetch startup data - const { data: startupData, isLoading: startupLoading } = useQuery({ - queryKey: ['conductor', 'startup'], - queryFn: async () => { - const res = await cortexApi.get('/conductor/startup') - return res.data - }, - staleTime: 1000 * 30, - refetchInterval: 1000 * 60, - }) - - // Fetch templates - const { data: templatesData } = useQuery<{ templates: PromptTemplate[] }>({ - queryKey: ['conductor', 'templates'], - queryFn: async () => { - const res = await cortexApi.get('/conductor/templates') - return res.data - }, - staleTime: 1000 * 60 * 10, - }) - - // Compose prompt mutation - interface ComposeParams { - intent: string - project_id: string - intent_level: string - include_context: boolean - } - - const composeMutation = useMutation({ - mutationFn: async (params) => { - const res = await cortexApi.post('/conductor/compose', params) - return res.data - }, - }) - - const handleLaunch = async (projectId: string, intentLevel: IntentLevel) => { - const result = await composeMutation.mutateAsync({ - intent: `Starting new session. Continue from .next_session if available.`, - project_id: projectId, - intent_level: intentLevel, - include_context: true, - }) - - // Copy to clipboard - try { - await navigator.clipboard.writeText(result.prompt) - } catch { - const textarea = document.createElement('textarea') - textarea.value = result.prompt - document.body.appendChild(textarea) - textarea.select() - document.execCommand('copy') - document.body.removeChild(textarea) - } - } - - const handleCompose = async (intent: string, projectId: string, intentLevel: IntentLevel, includeContext: boolean) => { - await composeMutation.mutateAsync({ - intent, - project_id: projectId, - intent_level: intentLevel, - include_context: includeContext, - }) - } - - const projects = startupData?.projects.map(p => ({ id: p.id, name: p.name, icon: p.icon })) ?? [] - - return ( -
- {/* Header */} -
- - - HUMAN-AI COLLABORATION COCKPIT - -
- - {/* Tab Navigation */} -
- {TABS.map((tab) => ( - - ))} -
- - {/* Tab Content */} - {activeTab === 'startup' && ( - - )} - - {activeTab === 'monitor' && ( - - )} - - {activeTab === 'composer' && ( - - )} -
- ) -} diff --git a/_contrib/site/src/panels/conductor/PromptComposer.tsx b/_contrib/site/src/panels/conductor/PromptComposer.tsx deleted file mode 100644 index 21f61e4..0000000 --- a/_contrib/site/src/panels/conductor/PromptComposer.tsx +++ /dev/null @@ -1,328 +0,0 @@ -import { useState } from 'react' -import { Card, SectionHeader } from '@/design-system/components' -import { cn } from '@/utils/cn' -import { useConductorStore } from '@/stores/conductorStore' -import { useConductorHistoryQuery } from '@/api/hooks' -import type { PromptTemplate, ComposedPrompt, IntentLevel } from '@/types/conductor' -import type { PromptHistoryEntry } from '@/api/client' - -function formatRelativeTime(isoTimestamp: string): string { - const now = Date.now() - const then = new Date(isoTimestamp).getTime() - const diffMs = now - then - const diffSec = Math.floor(diffMs / 1000) - if (diffSec < 60) return 'just now' - const diffMin = Math.floor(diffSec / 60) - if (diffMin < 60) return `${diffMin}m ago` - const diffHr = Math.floor(diffMin / 60) - if (diffHr < 24) return `${diffHr}h ago` - const diffDay = Math.floor(diffHr / 24) - return `${diffDay}d ago` -} - -const INTENT_LEVEL_COLORS: Record = { - advisory: 'text-emerald-400 border-emerald-400/30 bg-emerald-400/10', - collaborative: 'text-cortex-cyan border-cortex-cyan/30 bg-cortex-cyan-muted', - autonomous: 'text-amber-400 border-amber-400/30 bg-amber-400/10', - supervisory: 'text-purple-400 border-purple-400/30 bg-purple-400/10', -} - -interface PromptComposerProps { - templates: PromptTemplate[] - composedPrompt: ComposedPrompt | null - isComposing: boolean - onCompose: (intent: string, projectId: string, intentLevel: IntentLevel, includeContext: boolean) => void - projects: { id: string; name: string; icon: string }[] -} - -export function PromptComposer({ templates, composedPrompt, isComposing, onCompose, projects }: PromptComposerProps) { - const { - selectedProject, setSelectedProject, - intentLevel, setIntentLevel, - userIntent, setUserIntent, - includeContext, setIncludeContext, - selectedTemplate, setSelectedTemplate, - } = useConductorStore() - - const [copied, setCopied] = useState(false) - const [expandedEntry, setExpandedEntry] = useState(null) - - const { data: historyData } = useConductorHistoryQuery(5) - - const handleTemplateSelect = (template: PromptTemplate) => { - setSelectedTemplate(template.id) - setIntentLevel(template.intent_level) - if (!userIntent) { - setUserIntent(template.template.replace('{intent}', '')) - } - } - - const handleCompose = () => { - if (!userIntent.trim() || !selectedProject) return - - let finalIntent = userIntent - if (selectedTemplate) { - const tmpl = templates.find(t => t.id === selectedTemplate) - if (tmpl) { - finalIntent = tmpl.template.replace('{intent}', userIntent) - } - } - - onCompose(finalIntent, selectedProject, intentLevel, includeContext) - } - - const handleCopy = async () => { - if (!composedPrompt) return - try { - await navigator.clipboard.writeText(composedPrompt.prompt) - setCopied(true) - setTimeout(() => setCopied(false), 2000) - } catch { - // Fallback - const textarea = document.createElement('textarea') - textarea.value = composedPrompt.prompt - document.body.appendChild(textarea) - textarea.select() - document.execCommand('copy') - document.body.removeChild(textarea) - setCopied(true) - setTimeout(() => setCopied(false), 2000) - } - } - - const handleCopyHistoryEntry = async (prompt: string) => { - try { - await navigator.clipboard.writeText(prompt) - } catch { - const textarea = document.createElement('textarea') - textarea.value = prompt - document.body.appendChild(textarea) - textarea.select() - document.execCommand('copy') - document.body.removeChild(textarea) - } - } - - const projectNameMap = Object.fromEntries(projects.map(p => [p.id, p])) - - return ( -
- {/* Template Quick-Select */} -
- -
- {templates.map((tmpl) => ( - - ))} -
-
- - {/* Composer Form */} -
- {/* Left: Input */} -
-
- -
- {projects.map((proj) => ( - - ))} -
-
- -
- -