diff --git a/.env.template b/.env.template index 9eb23631..b0ed202c 100644 --- a/.env.template +++ b/.env.template @@ -12,6 +12,11 @@ COPILOTJ_VLM_API_KEY=sk-xxxxxxxx # When enabled, COPILOTJ_VLM_MODEL and COPILOTJ_VLM_API_KEY must be set. # COPILOTJ_VISION_ENABLED=1 # COPILOTJ_VLM_PROXY=http://127.0.0.1:8080 +# Global download proxy — passed explicitly to all backend download clients (HuggingFace, +# model DB, bioimage model zoo, web research/Tavily/DDGS, cellpose/stardist) and used as the +# fallback for the LLM/VLM API clients when COPILOTJ_LLM_PROXY/COPILOTJ_VLM_PROXY are unset. +# Passed via library proxy parameters (no global proxy env vars are set). +# CIJ_PROXY=http://127.0.0.1:8080 COPILOTJ_VLM_BASE_URL=https://api.siliconflow.cn/v1 # VLM provider (optional, falls back to COPILOTJ_LLM_PROVIDER) # COPILOTJ_VLM_PROVIDER=gemini diff --git a/AGENTS.md b/AGENTS.md index 12001899..7d769e15 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,6 +53,7 @@ The core agent framework follows a layered architecture: - `COPILOTJ_VLM_MODEL`: Vision model for image analysis (requires `COPILOTJ_VISION_ENABLED=1`) - `COPILOTJ_VLM_API_KEY`: API key for the vision model - `COPILOTJ_LLM_PROXY`: HTTP proxy +- `CIJ_PROXY`: Global download proxy for all backend downloads (HuggingFace, model DB, bioimage model zoo, research/Tavily/DDGS, cellpose/stardist); also the fallback proxy for the LLM/VLM clients when their own proxy is unset. Passed explicitly to each download client (no global proxy env vars are set) - `COPILOTJ_KB_AUTOSAVE`: Set `1` to auto-ingest dialog summaries into knowledge bank - `LANGFUSE_SECRET_KEY`/`LANGFUSE_PUBLIC_KEY`: Optional Langfuse observability diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 43537973..2b398ae1 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -217,24 +217,25 @@ Note: Ollama models generally do not support image input. If image understanding ### All configuration variables -| Variable | Description | -| ------------------------- | ------------------------------------------------------------------------------- | -| `COPILOTJ_LLM_MODEL` | Main LLM model name (required) | -| `COPILOTJ_LLM_API_KEY` | API key for the main model (required) | -| `COPILOTJ_LLM_BASE_URL` | Override API endpoint for the main model | -| `COPILOTJ_LLM_PROVIDER` | Explicit LLM provider selection (optional; auto-detected if not set) | -| `COPILOTJ_VLM_MODEL` | Vision-language model name (optional) | -| `COPILOTJ_VLM_API_KEY` | API key for the VLM (falls back to `COPILOTJ_LLM_API_KEY`) | -| `COPILOTJ_VLM_BASE_URL` | Override API endpoint for the VLM (falls back to `COPILOTJ_LLM_BASE_URL`) | -| `COPILOTJ_VLM_PROVIDER` | Explicit VLM provider (optional; falls back to `COPILOTJ_LLM_PROVIDER`) | -| `COPILOTJ_LLM_PROXY` | HTTP/HTTPS proxy for LLM outbound API requests | -| `COPILOTJ_VLM_PROXY` | HTTP/HTTPS proxy for VLM outbound requests (falls back to `COPILOTJ_LLM_PROXY`) | -| `COPILOTJ_TAVILY_API_KEY` | Tavily API key for live web search | -| `COPILOTJ_KB_AUTOSAVE` | Set `1` to auto-ingest dialog summaries into the knowledge bank | -| `COPILOTJ_DEV` | Development mode (presence-based flag) | -| `LANGFUSE_SECRET_KEY` | Langfuse secret key for observability | -| `LANGFUSE_PUBLIC_KEY` | Langfuse public key for observability | -| `LANGFUSE_HOST` | Langfuse host URL (e.g. `https://us.cloud.langfuse.com`) | +| Variable | Description | +| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| `COPILOTJ_LLM_MODEL` | Main LLM model name (required) | +| `COPILOTJ_LLM_API_KEY` | API key for the main model (required) | +| `COPILOTJ_LLM_BASE_URL` | Override API endpoint for the main model | +| `COPILOTJ_LLM_PROVIDER` | Explicit LLM provider selection (optional; auto-detected if not set) | +| `COPILOTJ_VLM_MODEL` | Vision-language model name (optional) | +| `COPILOTJ_VLM_API_KEY` | API key for the VLM (falls back to `COPILOTJ_LLM_API_KEY`) | +| `COPILOTJ_VLM_BASE_URL` | Override API endpoint for the VLM (falls back to `COPILOTJ_LLM_BASE_URL`) | +| `COPILOTJ_VLM_PROVIDER` | Explicit VLM provider (optional; falls back to `COPILOTJ_LLM_PROVIDER`) | +| `COPILOTJ_LLM_PROXY` | HTTP/HTTPS proxy for LLM outbound API requests | +| `COPILOTJ_VLM_PROXY` | HTTP/HTTPS proxy for VLM outbound requests (falls back to `CIJ_PROXY`) | +| `CIJ_PROXY` | Global download proxy for HuggingFace, model DB, bioimage zoo, research/Tavily/DDGS, cellpose/stardist; fallback for the LLM/VLM proxies | +| `COPILOTJ_TAVILY_API_KEY` | Tavily API key for live web search | +| `COPILOTJ_KB_AUTOSAVE` | Set `1` to auto-ingest dialog summaries into the knowledge bank | +| `COPILOTJ_DEV` | Development mode (presence-based flag) | +| `LANGFUSE_SECRET_KEY` | Langfuse secret key for observability | +| `LANGFUSE_PUBLIC_KEY` | Langfuse public key for observability | +| `LANGFUSE_HOST` | Langfuse host URL (e.g. `https://us.cloud.langfuse.com`) | A complete `.env.local` template: @@ -246,6 +247,10 @@ COPILOTJ_LLM_API_KEY=sk-xxxxxxxx #COPILOTJ_LLM_PROXY=http://PATH_TO_YOUR_PROXY #COPILOTJ_LLM_PROVIDER=openai +# Global download proxy (HuggingFace, model DB, bioimage, web research, cellpose/stardist). +# Also the fallback for the LLM/VLM API proxies above. Passed explicitly — no global proxy env vars. +#CIJ_PROXY=http://127.0.0.1:8080 + # Vision-language model (image understanding) — optional, choose one provider #COPILOTJ_VLM_MODEL=gemini-2.5-flash #COPILOTJ_VLM_API_KEY=AI-xxxxxxxx diff --git a/copilotj/appose_worker.py b/copilotj/appose_worker.py index 4ab0b9c5..c4b4b771 100644 --- a/copilotj/appose_worker.py +++ b/copilotj/appose_worker.py @@ -42,6 +42,7 @@ from copilotj.core import load_config from copilotj.core.config import bootstrap_assets, load_managed_config, save_managed_config +from copilotj.core.embedding import configure_download_proxy from copilotj.core.lifecycle import run_cleanup as _run_cleanup from copilotj.server import Server @@ -101,6 +102,8 @@ def start_server() -> None: saved_port = _extract_port(load_managed_config().get("server_url")) cfg = load_config() + configure_download_proxy(cfg) + _server = Server(cfg) _loop = asyncio.new_event_loop() diff --git a/copilotj/core/config.py b/copilotj/core/config.py index 422b52d9..a83c28e1 100644 --- a/copilotj/core/config.py +++ b/copilotj/core/config.py @@ -69,6 +69,11 @@ class Config: vlm_proxy: str | None = None vlm_provider: str | None = None + # Global download proxy — passed explicitly to all backend download clients (HF, + # model DB, bioimage zoo, research/Tavily/DDGS, cellpose/stardist). Also the + # fallback proxy for the LLM/VLM API clients when their own proxy is unset. + cij_proxy: str | None = None + # Tool keys tavily_api_key: str | None = None @@ -142,6 +147,7 @@ def load_config() -> Config: llm_provider=os.getenv("COPILOTJ_LLM_PROVIDER", None), vlm_proxy=os.getenv("COPILOTJ_VLM_PROXY", None), vlm_provider=os.getenv("COPILOTJ_VLM_PROVIDER", None), + cij_proxy=os.getenv("CIJ_PROXY"), tavily_api_key=os.getenv("COPILOTJ_TAVILY_API_KEY", None), kb_autosave=os.getenv("COPILOTJ_KB_AUTOSAVE", "0") == "1", dev=os.getenv("COPILOTJ_DEV") is not None, @@ -176,7 +182,7 @@ def resolve_vision_config(cfg: Config) -> Config: from copilotj.core.model_info import get_model_capabilities # Detect main model vision capability - main_caps = get_model_capabilities(cfg.llm_model) if cfg.llm_model else None + main_caps = get_model_capabilities(cfg.llm_model, cfg) if cfg.llm_model else None llm_supports_vision = bool(main_caps and main_caps.supports_vision) # Check if a separate VLM is explicitly configured diff --git a/copilotj/core/embedding.py b/copilotj/core/embedding.py index 776f3d14..a0bcbc9a 100644 --- a/copilotj/core/embedding.py +++ b/copilotj/core/embedding.py @@ -6,6 +6,8 @@ from langchain_core.embeddings import Embeddings +from copilotj.core.config import Config + logger = logging.getLogger(__name__) RAG_EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2" @@ -13,13 +15,18 @@ __all__ = [ "RAG_EMBEDDING_MODEL", + "configure_download_proxy", "get_embeddings", "new_local_embeddings", ] def new_local_embeddings() -> Embeddings: - """Create the local embedding model used by the bundled FAISS index.""" + """Create the local embedding model used by the bundled FAISS index. + + The HuggingFace download proxy is configured once at process startup via + :func:`configure_download_proxy`, so this function needs no ``Config``. + """ from langchain_huggingface import HuggingFaceEmbeddings device = _detect_embedding_device() @@ -32,6 +39,25 @@ def new_local_embeddings() -> Embeddings: ) +def configure_download_proxy(cfg: Config) -> None: + """Route HuggingFace downloads through ``CIJ_PROXY`` (explicit, no env vars). + + ``huggingface_hub`` (httpx-based) downloads weights via a client produced by its + client factory. Installing a factory that returns a proxied ``httpx.Client`` makes + sentence-transformers/huggingface_hub fetch the embedding weights through the + proxy without touching ``os.environ``. Call once at process startup (server / + appose_worker / rag_builder) before the first HF download; a no-op when no + download proxy is configured. + """ + proxy = cfg.cij_proxy + if not proxy: + return + import httpx + from huggingface_hub import set_client_factory + + set_client_factory(lambda: httpx.Client(proxy=proxy)) + + def get_embeddings() -> Embeddings: """Get the local embeddings used for ImageJ RAG.""" global _embeddings diff --git a/copilotj/core/model_client/__init__.py b/copilotj/core/model_client/__init__.py index aa66f00e..333addba 100644 --- a/copilotj/core/model_client/__init__.py +++ b/copilotj/core/model_client/__init__.py @@ -65,10 +65,11 @@ def new_vlm_model_client(cfg: Config) -> ModelClient: return _new_model_client( cfg.vlm_model, cfg.vlm_api_key, - proxy=cfg.llm_proxy, + proxy=cfg.vlm_proxy or cfg.cij_proxy, base_url=cfg.vlm_base_url, cfg=cfg, provider=cfg.vlm_provider or cfg.llm_provider, + allow_llm_proxy_fallback=False, ) @@ -127,9 +128,13 @@ def _new_model_client( base_url: str | None = None, cfg: Config | None = None, provider: str | None = None, + allow_llm_proxy_fallback: bool = True, ) -> ModelClient: cfg = cfg or load_config() - proxy = proxy or cfg.llm_proxy + if allow_llm_proxy_fallback: + proxy = proxy or cfg.llm_proxy or cfg.cij_proxy + else: + proxy = proxy or cfg.cij_proxy # If provider is explicitly given, use it directly. if provider is not None: diff --git a/copilotj/core/model_info.py b/copilotj/core/model_info.py index 7912e487..d8685f55 100644 --- a/copilotj/core/model_info.py +++ b/copilotj/core/model_info.py @@ -23,7 +23,7 @@ import aiohttp -from copilotj.core.config import get_home +from copilotj.core.config import Config, get_home, load_config __all__ = [ "CatalogModel", @@ -232,7 +232,7 @@ def _store_db(data: dict[str, Any]) -> None: # --------------------------------------------------------------------------- -async def download_db(*, force: bool = False) -> Path | None: +async def download_db(*, force: bool = False, cfg: Config | None = None) -> Path | None: """Download the model database from GitHub. Skips the download if the cache is fresh (unless *force* is ``True``). @@ -250,9 +250,10 @@ async def download_db(*, force: bool = False) -> Path | None: _log.info("Downloading model DB from %s", LITELLM_DB_URL) try: + proxy = (cfg or load_config()).cij_proxy timeout = aiohttp.ClientTimeout(total=_DOWNLOAD_TIMEOUT) async with aiohttp.ClientSession(timeout=timeout) as session: - async with session.get(LITELLM_DB_URL) as resp: + async with session.get(LITELLM_DB_URL, proxy=proxy) as resp: resp.raise_for_status() data = await resp.json(content_type=None) @@ -268,12 +269,17 @@ async def download_db(*, force: bool = False) -> Path | None: return path if path.exists() else None -def _download_db_sync() -> dict[str, Any]: +def _download_db_sync(cfg: Config | None = None) -> dict[str, Any]: """Synchronous fallback download using ``urllib`` (for CLI / first-run).""" _log.info("Downloading model DB (sync) from %s", LITELLM_DB_URL) try: + proxy = (cfg or load_config()).cij_proxy req = urllib.request.Request(LITELLM_DB_URL) - with urllib.request.urlopen(req, timeout=_DOWNLOAD_TIMEOUT) as resp: + if proxy: + opener = urllib.request.build_opener(urllib.request.ProxyHandler({"http": proxy, "https": proxy})) + else: + opener = urllib.request.build_opener() + with opener.open(req, timeout=_DOWNLOAD_TIMEOUT) as resp: data = json.loads(resp.read().decode("utf-8")) if isinstance(data, dict) and len(data) >= 100: @@ -382,7 +388,7 @@ def _ollama_vision_heuristic(model: str) -> bool: # --------------------------------------------------------------------------- -def get_model_capabilities(model: str) -> ModelCapabilities: +def get_model_capabilities(model: str, cfg: Config | None = None) -> ModelCapabilities: """Return capabilities for *model*. Loads the cached model database, looks up the model (with normalisation), @@ -402,7 +408,7 @@ def get_model_capabilities(model: str) -> ModelCapabilities: db = _load_db() if not db: # No cache available — try a blocking download on first use. - db = _download_db_sync() + db = _download_db_sync(cfg) entry = _lookup_model(db, model) if db else None @@ -464,7 +470,7 @@ def _merge_supplements(provider: str, catalog_models: list[CatalogModel]) -> lis return merged -def list_catalog_models(provider: str) -> list[CatalogModel]: +def list_catalog_models(provider: str, cfg: Config | None = None) -> list[CatalogModel]: """Return chat models from the cached LiteLLM catalog for *provider*. Filters entries whose ``litellm_provider`` equals *provider* and whose @@ -479,7 +485,7 @@ def list_catalog_models(provider: str) -> list[CatalogModel]: """ db = _load_db() if not db: - db = _download_db_sync() + db = _download_db_sync(cfg) if not db: return _merge_supplements(provider, []) @@ -514,11 +520,11 @@ def list_catalog_models(provider: str) -> list[CatalogModel]: return _merge_supplements(provider, models) -async def ensure_model_db_async() -> bool: +async def ensure_model_db_async(cfg: Config | None = None) -> bool: """Ensure the model DB is available (download if stale). Called at server startup as a background task. Returns ``True`` if the DB is available (fresh or stale). """ - path = await download_db() + path = await download_db(cfg=cfg) return path is not None and path.exists() diff --git a/copilotj/core/model_listing.py b/copilotj/core/model_listing.py index 09a99c56..71c084e2 100644 --- a/copilotj/core/model_listing.py +++ b/copilotj/core/model_listing.py @@ -19,6 +19,7 @@ import aiohttp +from copilotj.core.config import Config from copilotj.core.model_info import CatalogModel, get_model_capabilities, list_catalog_models __all__ = [ @@ -50,7 +51,9 @@ async def _fetch_ollama_tags(base_url: str, *, timeout: float) -> dict[str, Any] return None -async def list_ollama_models(base_url: str, *, timeout: float = 2.0) -> list[CatalogModel] | None: +async def list_ollama_models( + base_url: str, *, timeout: float = 2.0, cfg: Config | None = None +) -> list[CatalogModel] | None: """List models installed on a local Ollama instance. Performs a live ``GET {base_url}/api/tags``. Ollama runs locally, so we @@ -69,7 +72,7 @@ async def list_ollama_models(base_url: str, *, timeout: float = 2.0) -> list[Cat name = entry.get("name") or entry.get("model") if not name: continue - caps = get_model_capabilities(f"ollama/{name}") + caps = get_model_capabilities(f"ollama/{name}", cfg) models.append( CatalogModel( id=name, @@ -94,7 +97,9 @@ def _model_to_dict(m: CatalogModel) -> dict[str, Any]: } -async def list_provider_models(provider: str, *, base_url: str | None = None) -> dict[str, Any]: +async def list_provider_models( + provider: str, *, base_url: str | None = None, cfg: Config | None = None +) -> dict[str, Any]: """Resolve available models for *provider* into a uniform dict. - ``ollama`` → live ``/api/tags`` at ``base_url`` (default @@ -107,7 +112,7 @@ async def list_provider_models(provider: str, *, base_url: str | None = None) -> Never raises — callers can ``asyncio.gather`` over providers safely. """ if provider == "ollama": - models = await list_ollama_models(base_url or DEFAULT_OLLAMA_URL) + models = await list_ollama_models(base_url or DEFAULT_OLLAMA_URL, cfg=cfg) if models is None: return {"provider": provider, "source": "unreachable", "models": []} return { @@ -116,5 +121,5 @@ async def list_provider_models(provider: str, *, base_url: str | None = None) -> "models": [_model_to_dict(m) for m in models], } - models = list_catalog_models(provider) + models = list_catalog_models(provider, cfg) return {"provider": provider, "source": "catalog", "models": [_model_to_dict(m) for m in models]} diff --git a/copilotj/multiagent/agent_configs/research_agent.toml b/copilotj/multiagent/agent_configs/research_agent.toml index 0f5d3f3d..e3296398 100644 --- a/copilotj/multiagent/agent_configs/research_agent.toml +++ b/copilotj/multiagent/agent_configs/research_agent.toml @@ -147,7 +147,7 @@ Use this tool when you need to: [[tools]] display_name = "DuckDuckGo Search" -function = "copilotj.multiagent.research_tools.ddg_search" +factory = "copilotj.multiagent.research_tools.make_ddg_search" description = """ Use this tool to search for current information and image links using DuckDuckGo. diff --git a/copilotj/multiagent/agent_configs/tool_agent.toml b/copilotj/multiagent/agent_configs/tool_agent.toml index 9e16c91a..e93a99ed 100644 --- a/copilotj/multiagent/agent_configs/tool_agent.toml +++ b/copilotj/multiagent/agent_configs/tool_agent.toml @@ -133,7 +133,7 @@ Use this tool for specialized tools fail or need debugging. **ERROR RECOVERY ONL """ [[tools]] -function = "copilotj.multiagent.py_tools.cellpose_segmentation" +factory = "copilotj.multiagent.py_tools.make_cellpose_segmentation" description = """ Deep learning-based cell segmentation using Cellpose. Supports nuclei, cytoplasm models with GPU \ acceleration. Optimized with native cellpose image loading for maximum compatibility. @@ -161,7 +161,7 @@ signals. [[tools]] display_name = "StarDist Segmentation" -function = "copilotj.multiagent.py_tools.stardist_segmentation" +factory = "copilotj.multiagent.py_tools.make_stardist_segmentation" description = """ StarDist instance segmentation for cells and nuclei. diff --git a/copilotj/multiagent/leader_multiagent.py b/copilotj/multiagent/leader_multiagent.py index ec51b94e..95d35f1d 100644 --- a/copilotj/multiagent/leader_multiagent.py +++ b/copilotj/multiagent/leader_multiagent.py @@ -784,6 +784,7 @@ async def run(self, task: str, trace_ctx: Langfuse | None = None) -> None: "Reply with a single Thought + Action, or a Final Answer.", trace_ctx=trace_ctx, ) + syntax_error_counter = 0 # Reset syntax error counter on success except ModelSyntaxError as e: syntax_error_counter += 1 self.log_error( diff --git a/copilotj/multiagent/py_tools.py b/copilotj/multiagent/py_tools.py index 7b3fee2a..7dcdc13f 100644 --- a/copilotj/multiagent/py_tools.py +++ b/copilotj/multiagent/py_tools.py @@ -15,11 +15,12 @@ from skimage.util import img_as_float32, img_as_ubyte from stardist.models import StarDist2D -from copilotj.core.config import get_home +from copilotj.core.config import Config, get_home, load_config +from copilotj.util import temporary_proxy __all__ = [ - "cellpose_segmentation", - "stardist_segmentation", + "make_cellpose_segmentation", + "make_stardist_segmentation", "biapy_tool", # Segmentation tools "gauss_otsu_labeling_tool", @@ -51,218 +52,236 @@ def get_project_templates_dir() -> Path: return templates_dir -async def cellpose_segmentation( - image_path: Annotated[str | None, "Path to the input image or directory (use forward slashes)"] = None, - model_type: Annotated[str, "Type of model to use (nuclei, cyto, cyto2)"] = "nuclei", - diameter: Annotated[int | None, "Expected cell diameter (if None, will be estimated)"] = None, - channels: Annotated[list[int], "List of channels to use [0,0] for grayscale"] = [0, 0], - flow_threshold: Annotated[float, "Flow error threshold"] = 0.4, - cellprob_threshold: Annotated[float, "Cell probability threshold"] = 0.0, - min_size: Annotated[int, "Minimum cell size"] = 15, - gpu: Annotated[bool, "Whether to use GPU for processing"] = False, - normalize: Annotated[bool, "Whether to normalize image intensities"] = True, - norm_range_low: Annotated[float, "Lower percentile for normalization"] = 1.0, - norm_range_high: Annotated[float, "Upper percentile for normalization"] = 99.8, - save_path: Annotated[ - str | None, - 'Path to save segmentation results (if None, uses original image directory with _cellpose_segmented suffix). Path end must with "/"', - ] = None, -) -> dict[str, str]: - try: - from cellpose import io as cellpose_io - from cellpose import models - - if image_path is None: - return "No image path provided, please provide an image path" - - # Handle directory vs file paths using cellpose's own functions - path_obj = Path(image_path) - if path_obj.is_dir(): - logger.info(f"Directory provided: {image_path}") - # Use cellpose's function to get image files from directory - image_files = await asyncio.to_thread(cellpose_io.get_image_files, image_path, mask_filter="") - if not image_files: - raise FileNotFoundError(f"No image files found in directory: {image_path}") - - # Use the first image file found - actual_image_path = image_files[0] - logger.info(f"Using first image from directory: {actual_image_path}") - else: - actual_image_path = image_path - - # Use cellpose's native image loading function - logger.info(f"Loading image using cellpose.io.imread: {actual_image_path}") - image = await asyncio.to_thread(cellpose_io.imread, actual_image_path) - - if save_path is None: - actual_path = Path(actual_image_path) - save_dir = actual_path.parent - base_name = actual_path.stem - save_path = str(save_dir / f"{base_name}_cellpose_segmented.png") - - # Validate image - if not _validate_image(image): - raise ValueError("Invalid input image") - - # Apply normalization if requested - if normalize: - logger.info(f"Normalizing image using {norm_range_low}-{norm_range_high} percentile range") - image = await asyncio.to_thread(_normalize_image, image, norm_range_low, norm_range_high) - - # Run Cellpose - logger.info(f"Running Cellpose model: {model_type}") - model = models.CellposeModel(gpu=gpu, model_type=model_type) - - # Run model evaluation in a thread pool to avoid blocking - masks, flows, styles = await asyncio.to_thread( - model.eval, - image, - diameter=diameter, - channels=channels, - flow_threshold=flow_threshold, - cellprob_threshold=cellprob_threshold, - min_size=min_size, - ) +def make_cellpose_segmentation(cfg: Config): + """Factory: return a ``cellpose_segmentation`` callable bound to *cfg*.""" + + async def cellpose_segmentation( + image_path: Annotated[str | None, "Path to the input image or directory (use forward slashes)"] = None, + model_type: Annotated[str, "Type of model to use (nuclei, cyto, cyto2)"] = "nuclei", + diameter: Annotated[int | None, "Expected cell diameter (if None, will be estimated)"] = None, + channels: Annotated[list[int], "List of channels to use [0,0] for grayscale"] = [0, 0], + flow_threshold: Annotated[float, "Flow error threshold"] = 0.4, + cellprob_threshold: Annotated[float, "Cell probability threshold"] = 0.0, + min_size: Annotated[int, "Minimum cell size"] = 15, + gpu: Annotated[bool, "Whether to use GPU for processing"] = False, + normalize: Annotated[bool, "Whether to normalize image intensities"] = True, + norm_range_low: Annotated[float, "Lower percentile for normalization"] = 1.0, + norm_range_high: Annotated[float, "Upper percentile for normalization"] = 99.8, + save_path: Annotated[ + str | None, + 'Path to save segmentation results (if None, uses original image directory with _cellpose_segmented suffix). Path end must with "/"', + ] = None, + ) -> dict[str, str]: + try: + from cellpose import io as cellpose_io + from cellpose import models - save_path_obj = Path(save_path) + if image_path is None: + return "No image path provided, please provide an image path" - # Determine if save_path is a directory or file path - # Consider it a directory if: ends with /, has no suffix, or is an existing directory - is_directory = str(save_path).endswith(("/", "\\")) or not save_path_obj.suffix or save_path_obj.is_dir() + # Handle directory vs file paths using cellpose's own functions + path_obj = Path(image_path) + if path_obj.is_dir(): + logger.info(f"Directory provided: {image_path}") + # Use cellpose's function to get image files from directory + image_files = await asyncio.to_thread(cellpose_io.get_image_files, image_path, mask_filter="") + if not image_files: + raise FileNotFoundError(f"No image files found in directory: {image_path}") + + # Use the first image file found + actual_image_path = image_files[0] + logger.info(f"Using first image from directory: {actual_image_path}") + else: + actual_image_path = image_path + + # Use cellpose's native image loading function + logger.info(f"Loading image using cellpose.io.imread: {actual_image_path}") + image = await asyncio.to_thread(cellpose_io.imread, actual_image_path) + + if save_path is None: + actual_path = Path(actual_image_path) + save_dir = actual_path.parent + base_name = actual_path.stem + save_path = str(save_dir / f"{base_name}_cellpose_segmented.png") + + # Validate image + if not _validate_image(image): + raise ValueError("Invalid input image") + + # Apply normalization if requested + if normalize: + logger.info(f"Normalizing image using {norm_range_low}-{norm_range_high} percentile range") + image = await asyncio.to_thread(_normalize_image, image, norm_range_low, norm_range_high) + + # Run Cellpose + logger.info(f"Running Cellpose model: {model_type}") + # cellpose downloads pretrained weights via raw urllib (no proxy param) on first + # use, so temporarily expose CIJ_PROXY through env vars for this call only. + with temporary_proxy(cfg): + model = models.CellposeModel(gpu=gpu, model_type=model_type) + + # Run model evaluation in a thread pool to avoid blocking + masks, flows, styles = await asyncio.to_thread( + model.eval, + image, + diameter=diameter, + channels=channels, + flow_threshold=flow_threshold, + cellprob_threshold=cellprob_threshold, + min_size=min_size, + ) - if is_directory: - # save_path is a directory - save_dir = save_path_obj - save_dir.mkdir(parents=True, exist_ok=True) - # Use image filename as base for output files - base_name = Path(actual_image_path).stem - final_save_path = save_dir / f"{base_name}_cellpose_segmented.png" - else: - # save_path is a file path - save_dir = save_path_obj.parent - save_dir.mkdir(parents=True, exist_ok=True) - base_name = save_path_obj.stem - final_save_path = save_path_obj + save_path_obj = Path(save_path) - logger.info(f"Save directory: {save_dir}") + # Determine if save_path is a directory or file path + # Consider it a directory if: ends with /, has no suffix, or is an existing directory + is_directory = str(save_path).endswith(("/", "\\")) or not save_path_obj.suffix or save_path_obj.is_dir() - try: - # Save original image - original_path = save_dir / f"{base_name}_original.png" - await asyncio.to_thread(io.imsave, str(original_path), img_as_ubyte(image)) - logger.info(f"Saved original image to {original_path}") + if is_directory: + # save_path is a directory + save_dir = save_path_obj + save_dir.mkdir(parents=True, exist_ok=True) + # Use image filename as base for output files + base_name = Path(actual_image_path).stem + final_save_path = save_dir / f"{base_name}_cellpose_segmented.png" + else: + # save_path is a file path + save_dir = save_path_obj.parent + save_dir.mkdir(parents=True, exist_ok=True) + base_name = save_path_obj.stem + final_save_path = save_path_obj - # Create and save colored segmentation result - colored_masks = await asyncio.to_thread(create_colored_masks, masks) - await asyncio.to_thread(io.imsave, str(final_save_path), colored_masks) - logger.info(f"Saved colored segmentation to {final_save_path}") + logger.info(f"Save directory: {save_dir}") - # Save ROIs - rois_save_path = save_dir / f"{base_name}_rois.zip" - await asyncio.to_thread(cellpose_io.save_rois, masks, str(rois_save_path)) + try: + # Save original image + original_path = save_dir / f"{base_name}_original.png" + await asyncio.to_thread(io.imsave, str(original_path), img_as_ubyte(image)) + logger.info(f"Saved original image to {original_path}") + + # Create and save colored segmentation result + colored_masks = await asyncio.to_thread(create_colored_masks, masks) + await asyncio.to_thread(io.imsave, str(final_save_path), colored_masks) + logger.info(f"Saved colored segmentation to {final_save_path}") + + # Save ROIs + rois_save_path = save_dir / f"{base_name}_rois.zip" + await asyncio.to_thread(cellpose_io.save_rois, masks, str(rois_save_path)) + + return { + "original_image": str(original_path), + "segmentation_image": str(final_save_path), + "rois_path": str(rois_save_path), + "num_cells": int(masks.max()), + "save_directory": str(save_dir.resolve()), + } + except Exception as e: + logger.error(f"Error saving results: {str(e)}") + raise - return { - "original_image": str(original_path), - "segmentation_image": str(final_save_path), - "rois_path": str(rois_save_path), - "num_cells": int(masks.max()), - "save_directory": str(save_dir.resolve()), - } except Exception as e: - logger.error(f"Error saving results: {str(e)}") + logger.error(f"Error in cellpose_segmentation: {str(e)}") raise - except Exception as e: - logger.error(f"Error in cellpose_segmentation: {str(e)}") - raise - - -async def stardist_segmentation( - image_path: Annotated[str | None, "Path to the input image (use forward slashes)"] = None, - image_array: Annotated[Any | None, "Optional in-memory image; overrides image_path if provided"] = None, - image_type: Annotated[str, "fluorescence | he | tissue | brightfield | phase | dapi | unknown"] = "fluorescence", - model: Annotated[ - str | None, - "Override pretrained model name for 2D (e.g., '2D_versatile_fluo', '2D_versatile_he', '2D_demo')", - ] = None, - p_low: Annotated[float, "Lower percentile for normalization"] = 1.0, - p_high: Annotated[float, "Upper percentile for normalization"] = 99.8, - prob_thresh: Annotated[float | None, "StarDist probability threshold override"] = None, - nms_thresh: Annotated[float | None, "StarDist NMS threshold override"] = None, - save_path: Annotated[ - str | None, "Output PNG path for colored labels; default saves near input or temp/stardist_results" - ] = None, -) -> dict[str, Any]: - try: - if image_array is not None: - img = image_array - inferred_dir = get_project_temp_dir("stardist_segmentation_results") - inferred_name = "in_memory" - # Convert RGB to grayscale if needed - if img.ndim == 3 and img.shape[-1] in (3, 4): - rgb = img[..., :3].astype(np.float32) - img = 0.2126 * rgb[..., 0] + 0.7152 * rgb[..., 1] + 0.0722 * rgb[..., 2] - else: - if image_path is None: - return "No image path provided, please provide an image path" - else: - img = await asyncio.to_thread(load_image, image_path, to_gray=True) - p = Path(image_path) + return cellpose_segmentation + + +def make_stardist_segmentation(cfg: Config): + """Factory: return a ``stardist_segmentation`` callable bound to *cfg*.""" + + async def stardist_segmentation( + image_path: Annotated[str | None, "Path to the input image (use forward slashes)"] = None, + image_array: Annotated[Any | None, "Optional in-memory image; overrides image_path if provided"] = None, + image_type: Annotated[ + str, "fluorescence | he | tissue | brightfield | phase | dapi | unknown" + ] = "fluorescence", + model: Annotated[ + str | None, + "Override pretrained model name for 2D (e.g., '2D_versatile_fluo', '2D_versatile_he', '2D_demo')", + ] = None, + p_low: Annotated[float, "Lower percentile for normalization"] = 1.0, + p_high: Annotated[float, "Upper percentile for normalization"] = 99.8, + prob_thresh: Annotated[float | None, "StarDist probability threshold override"] = None, + nms_thresh: Annotated[float | None, "StarDist NMS threshold override"] = None, + save_path: Annotated[ + str | None, "Output PNG path for colored labels; default saves near input or temp/stardist_results" + ] = None, + ) -> dict[str, Any]: + try: + if image_array is not None: + img = image_array inferred_dir = get_project_temp_dir("stardist_segmentation_results") - inferred_name = p.stem - - if not _validate_image(img): - raise ValueError("Invalid input image for StarDist") - - ndim = img.ndim - if ndim != 2: - raise ValueError(f"Unsupported image ndim={ndim}; only 2D images are supported") - - img_norm = normalize(img.astype(np.float32, copy=False), p_low, p_high) + inferred_name = "in_memory" + # Convert RGB to grayscale if needed + if img.ndim == 3 and img.shape[-1] in (3, 4): + rgb = img[..., :3].astype(np.float32) + img = 0.2126 * rgb[..., 0] + 0.7152 * rgb[..., 1] + 0.0722 * rgb[..., 2] + else: + if image_path is None: + return "No image path provided, please provide an image path" + else: + img = await asyncio.to_thread(load_image, image_path, to_gray=True) + p = Path(image_path) + inferred_dir = get_project_temp_dir("stardist_segmentation_results") + inferred_name = p.stem + + if not _validate_image(img): + raise ValueError("Invalid input image for StarDist") + + ndim = img.ndim + if ndim != 2: + raise ValueError(f"Unsupported image ndim={ndim}; only 2D images are supported") + + img_norm = normalize(img.astype(np.float32, copy=False), p_low, p_high) + + if model is None: + key = (image_type or "fluorescence").lower() + model_name = "2D_versatile_he" if key in {"he", "tissue", "brightfield"} else "2D_versatile_fluo" + else: + model_name = model + # Lazy: download the standard pretrained model on first use and cache it + # under ~/.stardist/models/ (mirrors how cellpose fetches its + # weights). Keeps the plugin JAR free of bundled model weights. + # stardist/csbdeep fetch weights via raw urllib (no proxy param); expose + # CIJ_PROXY through env vars for the model load only. + with temporary_proxy(cfg): + model_instance = StarDist2D.from_pretrained(model_name) - if model is None: - key = (image_type or "fluorescence").lower() - model_name = "2D_versatile_he" if key in {"he", "tissue", "brightfield"} else "2D_versatile_fluo" - else: - model_name = model - # Lazy: download the standard pretrained model on first use and cache it - # under ~/.stardist/models/ (mirrors how cellpose fetches its - # weights). Keeps the plugin JAR free of bundled model weights. - model_instance = StarDist2D.from_pretrained(model_name) + kwargs = {} + if prob_thresh is not None: + kwargs["prob_thresh"] = prob_thresh + if nms_thresh is not None: + kwargs["nms_thresh"] = nms_thresh - kwargs = {} - if prob_thresh is not None: - kwargs["prob_thresh"] = prob_thresh - if nms_thresh is not None: - kwargs["nms_thresh"] = nms_thresh + labels, details = await asyncio.to_thread(model_instance.predict_instances, img_norm, **kwargs) - labels, details = await asyncio.to_thread(model_instance.predict_instances, img_norm, **kwargs) + out_dir = inferred_dir if save_path is None else Path(save_path).parent + out_dir = Path(out_dir) + out_dir.mkdir(parents=True, exist_ok=True) - out_dir = inferred_dir if save_path is None else Path(save_path).parent - out_dir = Path(out_dir) - out_dir.mkdir(parents=True, exist_ok=True) + original_path = out_dir / f"{inferred_name}_stardist_input.png" + await asyncio.to_thread(io.imsave, original_path, (img_norm * 255).astype(np.uint8)) - original_path = out_dir / f"{inferred_name}_stardist_input.png" - await asyncio.to_thread(io.imsave, original_path, (img_norm * 255).astype(np.uint8)) + colored = await asyncio.to_thread(create_colored_masks, labels.astype(np.int32)) + seg_png = out_dir / f"{inferred_name}_stardist_labels.png" if save_path is None else Path(save_path) + await asyncio.to_thread(io.imsave, seg_png, colored) - colored = await asyncio.to_thread(create_colored_masks, labels.astype(np.int32)) - seg_png = out_dir / f"{inferred_name}_stardist_labels.png" if save_path is None else Path(save_path) - await asyncio.to_thread(io.imsave, seg_png, colored) + labels_tif = out_dir / f"{inferred_name}_stardist_labels.tif" + await asyncio.to_thread(io.imsave, labels_tif, labels.astype(np.uint16)) - labels_tif = out_dir / f"{inferred_name}_stardist_labels.tif" - await asyncio.to_thread(io.imsave, labels_tif, labels.astype(np.uint16)) + return { + "original_image": str(original_path), + "segmentation_image": str(seg_png), + "labels_path": str(labels_tif), + "num_instances": int(labels.max()), + "model_name": model_name, + } - return { - "original_image": str(original_path), - "segmentation_image": str(seg_png), - "labels_path": str(labels_tif), - "num_instances": int(labels.max()), - "model_name": model_name, - } + except Exception as e: + logger.error(f"Error in stardist_segmentation: {e}") + raise - except Exception as e: - logger.error(f"Error in stardist_segmentation: {e}") - raise + return stardist_segmentation # @@ -1466,4 +1485,4 @@ def create_colored_masks(masks: np.ndarray) -> np.ndarray: if __name__ == "__main__": - asyncio.run(cellpose_segmentation()) + asyncio.run(make_cellpose_segmentation(load_config())()) diff --git a/copilotj/multiagent/research_tools.py b/copilotj/multiagent/research_tools.py index 9c6c6b16..3000fa58 100644 --- a/copilotj/multiagent/research_tools.py +++ b/copilotj/multiagent/research_tools.py @@ -21,7 +21,7 @@ from sklearn.metrics.pairwise import cosine_similarity from tavily import TavilyClient -from copilotj.core.config import Config +from copilotj.core.config import Config, load_config from copilotj.core.embedding import get_embeddings from copilotj.core.kb import ( DEFAULT_INDEX_DIR, @@ -30,9 +30,10 @@ ) from copilotj.multiagent.py_tools import get_project_temp_dir from copilotj.multiagent.tools import execute_python_script +from copilotj.util import proxy_dict __all__ = [ - "ddg_search", + "make_ddg_search", "wikipedia_search", "imagesc_search", "biii_search", @@ -107,6 +108,7 @@ def _fetch_collection( ttl_seconds: int, force_refresh: bool = False, session: requests.Session | None = None, + proxies: dict[str, str] | None = None, ) -> list[Mapping]: """Return the BioImage Model Zoo collection as a list of resource dicts.""" @@ -127,6 +129,8 @@ def _fetch_collection( last_error: Exception | None = None sess = session or requests.Session() + if proxies: + sess.proxies.update(proxies) for url in urls: try: resp = sess.get(url, timeout=30) @@ -207,51 +211,61 @@ def _extract_download_urls(entry: Mapping) -> list[str]: # search tool -def ddg_search( - query: Annotated[str, "Search query string describing what information you need"], - *, - max_results: Annotated[int, "Maximum number of results to return"] = 3, - timeout: Annotated[int, "Timeout for search request (s)"] = 10, - retries: Annotated[int, "Number of retry attempts"] = 2, - images: Annotated[bool, "Set True to return image results instead of text"] = False, - public_only: Annotated[bool, "Bias towards Wikimedia/Flickr/Unsplash if True"] = False, - safesearch: Annotated[str, "DuckDuckGo safesearch: off | moderate | strict"] = "moderate", -) -> str: - attempt = 0 - while attempt <= retries: - try: - with DDGS(timeout=timeout) as ddgs: - if images: - sites = [ - "site:commons.wikimedia.org", - "site:upload.wikimedia.org", - "site:wikimedia.org", - "site:flickr.com", - "site:unsplash.com", - "site:staticflickr.com", - ] - q = f"{query} (photo OR image) {' '.join(sites)}" if public_only else f"{query} (photo OR image)" - items = list(ddgs.images(q, max_results=max_results, safesearch=safesearch)) - if not items: - return "No image results." - out = [] - for i, it in enumerate(items, 1): - img = it.get("image") or it.get("thumbnail") - out.append(f"{i}. {it.get('title')}\nPage: {it.get('url')}\nImage: {img}") - return "\n\n".join(out) - else: - results = ddgs.text(query, max_results=max_results) - if not results: - return "No text results." - out = [] - for i, r in enumerate(results, 1): - out.append(f"{i}. {r.get('title')}\n{r.get('body')}\nSource: {r.get('href')}") - return "\n\n".join(out) - except Exception as e: - attempt += 1 - if attempt > retries: - return f"DDG search failed: {e}" - time.sleep(1 * attempt) +def make_ddg_search(cfg: Config): + """Factory: return a ``ddg_search`` callable bound to *cfg*.""" + proxy = cfg.cij_proxy + + def ddg_search( + query: Annotated[str, "Search query string describing what information you need"], + *, + max_results: Annotated[int, "Maximum number of results to return"] = 3, + timeout: Annotated[int, "Timeout for search request (s)"] = 10, + retries: Annotated[int, "Number of retry attempts"] = 2, + images: Annotated[bool, "Set True to return image results instead of text"] = False, + public_only: Annotated[bool, "Bias towards Wikimedia/Flickr/Unsplash if True"] = False, + safesearch: Annotated[str, "DuckDuckGo safesearch: off | moderate | strict"] = "moderate", + ) -> str: + attempt = 0 + while attempt <= retries: + try: + with DDGS(proxy=proxy, timeout=timeout) as ddgs: + if images: + sites = [ + "site:commons.wikimedia.org", + "site:upload.wikimedia.org", + "site:wikimedia.org", + "site:flickr.com", + "site:unsplash.com", + "site:staticflickr.com", + ] + q = ( + f"{query} (photo OR image) {' '.join(sites)}" + if public_only + else f"{query} (photo OR image)" + ) + items = list(ddgs.images(q, max_results=max_results, safesearch=safesearch)) + if not items: + return "No image results." + out = [] + for i, it in enumerate(items, 1): + img = it.get("image") or it.get("thumbnail") + out.append(f"{i}. {it.get('title')}\nPage: {it.get('url')}\nImage: {img}") + return "\n\n".join(out) + else: + results = ddgs.text(query, max_results=max_results) + if not results: + return "No text results." + out = [] + for i, r in enumerate(results, 1): + out.append(f"{i}. {r.get('title')}\n{r.get('body')}\nSource: {r.get('href')}") + return "\n\n".join(out) + except Exception as e: + attempt += 1 + if attempt > retries: + return f"DDG search failed: {e}" + time.sleep(1 * attempt) + + return ddg_search def make_tavily_search(cfg: Config): @@ -268,7 +282,7 @@ def tavily_search( if not cfg.tavily_api_key: return "Tavily API key not found. Please set COPILOTJ_TAVILY_API_KEY environment variable." - client = TavilyClient(api_key=cfg.tavily_api_key) + client = TavilyClient(api_key=cfg.tavily_api_key, proxies=proxy_dict(cfg)) response = client.search( query=query, @@ -592,6 +606,7 @@ async def imagej_retriever(query: Annotated[str, "Search query string describing def make_deep_research(cfg: Config): """Factory: return a ``deep_research`` callable bound to *cfg*.""" _tavily_search = make_tavily_search(cfg) + _ddg_search = make_ddg_search(cfg) async def deep_research(query: Annotated[str, "The research question to investigate thoroughly"]) -> str: research_report = { @@ -620,7 +635,7 @@ async def deep_research(query: Annotated[str, "The research question to investig research_report["sources_consulted"].append("Wikipedia") research_report["findings"]["reference"] = wiki_results - ddg_results = ddg_search(query, max_results=5) + ddg_results = _ddg_search(query, max_results=5) research_report["sources_consulted"].append("DuckDuckGo Search") research_report["findings"]["web_alternative"] = ddg_results @@ -714,6 +729,7 @@ def bioimage_search_models( cache_dir=Path(_cfg.bioimage_model_zoo_cache).resolve(), ttl_seconds=_cfg.bioimage_model_zoo_cache_ttl, force_refresh=False, + proxies=proxy_dict(_cfg), ) query_text = query.lower().strip() if query else None @@ -843,6 +859,7 @@ def bioimage_download_model( cache_dir=Path(cfg.bioimage_model_zoo_cache).resolve(), ttl_seconds=cfg.bioimage_model_zoo_cache_ttl, force_refresh=False, + proxies=proxy_dict(cfg), ) entry = _get_model(models, model_id) @@ -858,6 +875,9 @@ def bioimage_download_model( result_path = dest_path / filename_guess sess = requests.Session() + _proxies = proxy_dict(cfg) + if _proxies: + sess.proxies.update(_proxies) with sess.get(chosen, stream=True, timeout=60) as resp: resp.raise_for_status() with result_path.open("wb") as handle: @@ -876,7 +896,7 @@ def bioimage_download_model( if __name__ == "__main__": from copilotj.core import load_config - load_config() + cfg = load_config() - result = ddg_search("images of albert einstein", images=True) + result = make_ddg_search(cfg)("images of albert einstein", images=True) print(result) diff --git a/copilotj/server/server.py b/copilotj/server/server.py index b4b2ecf6..2093eb9a 100644 --- a/copilotj/server/server.py +++ b/copilotj/server/server.py @@ -12,6 +12,8 @@ from copilotj.core.config import Config from copilotj.core.kb import ensure_faiss_index_async from copilotj.core.lifecycle import run_cleanup +from copilotj.core.model_info import get_model_capabilities +from copilotj.core.model_listing import list_provider_models from copilotj.server.bridge import Bridge from copilotj.server.threads import Threads @@ -114,6 +116,9 @@ async def on_shutdown(app: web.Application) -> None: app.on_shutdown.append(on_shutdown) async def _ensure_kb(app: web.Application): + from copilotj.core.embedding import configure_download_proxy + + configure_download_proxy(self._cfg) asyncio.ensure_future(ensure_faiss_index_async()) yield @@ -124,7 +129,7 @@ async def _prepare_model_db(app: web.Application): from copilotj.core.model_info import ensure_model_db_async async def _download_and_resolve(): - await ensure_model_db_async() + await ensure_model_db_async(self._cfg) new_cfg = resolve_vision_config(self._cfg) self._cfg = new_cfg self._threads.update_cfg(new_cfg) @@ -180,12 +185,11 @@ async def _on_model_capabilities(self, request: web.Request) -> web.Response: model name. Without the parameter, returns info for the server's configured LLM and VLM. """ - from copilotj.core.model_info import get_model_capabilities # Check arbitrary model via query parameter model_param = request.query.get("model") if model_param: - caps = await asyncio.to_thread(get_model_capabilities, model_param) + caps = await asyncio.to_thread(get_model_capabilities, model_param, self._cfg) return web.json_response( { "model": model_param, @@ -200,7 +204,7 @@ async def _on_model_capabilities(self, request: web.Request) -> web.Response: def _caps_dict(model: str) -> dict | None: if not model: return None - caps = get_model_capabilities(model) + caps = get_model_capabilities(model, cfg) return { "supports_vision": caps.supports_vision, "supports_function_calling": caps.supports_function_calling, @@ -237,20 +241,19 @@ async def _on_list_models(self, request: web.Request) -> web.Response: live. Never returns 5xx — Ollama being unreachable surfaces as ``source: "unreachable"`` with an empty model list. """ - from copilotj.core.model_listing import list_provider_models provider = request.query.get("provider") base_url = request.query.get("base_url") if provider: - result = await list_provider_models(provider, base_url=base_url) + result = await list_provider_models(provider, base_url=base_url, cfg=self._cfg) return web.json_response(result) # Grouped: resolve all supported providers concurrently. Catalog-backed # cloud providers (incl. DeepSeek / OpenRouter) are listed so the model # picker can offer autocomplete for them; Ollama is queried live. providers = ["openai", "anthropic", "gemini", "deepseek", "openrouter", "ollama"] - results = await asyncio.gather(*(list_provider_models(p, base_url=base_url) for p in providers)) + results = await asyncio.gather(*(list_provider_models(p, base_url=base_url, cfg=self._cfg) for p in providers)) return web.json_response({"providers": {r["provider"]: r for r in results}}) diff --git a/copilotj/test/core/test_proxy.py b/copilotj/test/core/test_proxy.py new file mode 100644 index 00000000..8bd20398 --- /dev/null +++ b/copilotj/test/core/test_proxy.py @@ -0,0 +1,115 @@ +# SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project. +# +# SPDX-License-Identifier: Apache-2.0 + +"""Tests for the ``CIJ_PROXY`` download-proxy behavior in :mod:`copilotj.core`. + +Covers :func:`Config.cij_proxy` loading (with no ``os.environ`` mutation) and the +LLM/VLM client proxy precedence, including the ``vlm_proxy`` regression +(``new_vlm_model_client`` previously ignored ``cfg.vlm_proxy``). +""" + +from __future__ import annotations + +import os + +from copilotj.core.config import Config, load_config +from copilotj.core.model_client import new_model_client, new_vlm_model_client + +_PROXY_ENV_KEYS = ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "http_proxy", "https_proxy", "all_proxy") + + +# --------------------------------------------------------------------------- # +# Config loading +# --------------------------------------------------------------------------- # + + +def test_cij_proxy_loaded_from_env(monkeypatch, tmp_path): + monkeypatch.setenv("COPILOTJ_HOME", str(tmp_path)) # isolate from any real .env + monkeypatch.setenv("CIJ_PROXY", "http://127.0.0.1:8080") + cfg = load_config() + assert cfg.cij_proxy == "http://127.0.0.1:8080" + + +def test_cij_proxy_unset_defaults_none(monkeypatch, tmp_path): + monkeypatch.setenv("COPILOTJ_HOME", str(tmp_path)) + monkeypatch.delenv("CIJ_PROXY", raising=False) + cfg = load_config() + assert cfg.cij_proxy is None + + +def test_load_config_does_not_mutate_proxy_env(monkeypatch, tmp_path): + """The explicit approach must never write proxy env vars.""" + for key in _PROXY_ENV_KEYS: + monkeypatch.delenv(key, raising=False) + monkeypatch.setenv("COPILOTJ_HOME", str(tmp_path)) + monkeypatch.setenv("CIJ_PROXY", "http://127.0.0.1:8080") + load_config() + for key in _PROXY_ENV_KEYS: + assert key not in os.environ + + +# --------------------------------------------------------------------------- # +# LLM / VLM client proxy precedence +# --------------------------------------------------------------------------- # + + +def _capture_resolve(monkeypatch, captured: dict): + """Monkeypatch ``_resolve_client`` to record the proxy kwarg and return a dummy.""" + + def fake_resolve(provider, model, api_key, *, proxy, base_url): # noqa: ARG001 + captured["proxy"] = proxy + captured["provider"] = provider + return object() + + monkeypatch.setattr("copilotj.core.model_client._resolve_client", fake_resolve) + + +def test_llm_client_prefers_llm_proxy(monkeypatch): + captured: dict = {} + _capture_resolve(monkeypatch, captured) + cfg = Config(llm_model="gpt-4o", llm_provider="openai", llm_proxy="http://llm:1", cij_proxy="http://cij:1") + new_model_client(cfg) + assert captured["proxy"] == "http://llm:1" + + +def test_llm_client_falls_back_to_cij_proxy(monkeypatch): + captured: dict = {} + _capture_resolve(monkeypatch, captured) + cfg = Config(llm_model="gpt-4o", llm_provider="openai", cij_proxy="http://cij:1") + new_model_client(cfg) + assert captured["proxy"] == "http://cij:1" + + +def test_vlm_client_prefers_vlm_proxy(monkeypatch): + """Regression: ``cfg.vlm_proxy`` must win (previously ignored — fell back to llm_proxy).""" + captured: dict = {} + _capture_resolve(monkeypatch, captured) + cfg = Config( + llm_model="llm", + vlm_model="gpt-4o", + vlm_provider="openai", + vlm_proxy="http://vlm:1", + llm_proxy="http://llm:1", + cij_proxy="http://cij:1", + ) + new_vlm_model_client(cfg) + assert captured["proxy"] == "http://vlm:1" + + +def test_vlm_client_excludes_llm_proxy(monkeypatch): + """VLM proxy precedence is ``vlm_proxy > cij_proxy`` — ``llm_proxy`` is never used.""" + captured: dict = {} + _capture_resolve(monkeypatch, captured) + # vlm_proxy unset, both llm_proxy and cij_proxy set → cij_proxy wins + cfg = Config( + llm_model="llm", vlm_model="gpt-4o", vlm_provider="openai", llm_proxy="http://llm:1", cij_proxy="http://cij:1" + ) + new_vlm_model_client(cfg) + assert captured["proxy"] == "http://cij:1" + + captured.clear() + # vlm_proxy and cij_proxy unset, llm_proxy set → no proxy (llm_proxy excluded) + cfg = Config(llm_model="llm", vlm_model="gpt-4o", vlm_provider="openai", llm_proxy="http://llm:1") + new_vlm_model_client(cfg) + assert captured["proxy"] is None diff --git a/copilotj/test/multiagent/test_leader_syntax_reset.py b/copilotj/test/multiagent/test_leader_syntax_reset.py new file mode 100644 index 00000000..988e51b2 --- /dev/null +++ b/copilotj/test/multiagent/test_leader_syntax_reset.py @@ -0,0 +1,155 @@ +# SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project. +# +# SPDX-License-Identifier: Apache-2.0 + +"""Tests for LeaderDriven.run() syntax-error counter reset. + +`syntax_error_counter` must track CONSECUTIVE ReAct syntax errors, not +cumulative ones: a model that recovers (produces a valid response) between +errors must have its counter reset, mirroring `tool_retry_counter` at L850. + +Control-flow fact these tests rely on (verified by trace + outside-voice +review): every ModelSyntaxError increment in run() leaves `agent_resp = None`, +which routes recovery through the single `send_correction` at ~L782. That site +is the only place a reset is load-bearing. +""" + +import asyncio + +from copilotj.core import ModelSyntaxError +from copilotj.multiagent.leader_multiagent import LeaderDriven + +_MAX_SYNTAX_ERRORS = 3 + + +class _Args: + def model_dump(self) -> dict: + return {} + + +class _Tool: + def __init__(self, name: str) -> None: + self.name = name + + +class _ToolCall: + def __init__(self, name: str) -> None: + self.tool = _Tool(name) + self.args = _Args() + + +class _Resp: + """Minimal stand-in for ModelResponse exercised by run().""" + + def __init__(self, *, content=None, reasoning_content=None, tool_calls=None) -> None: + self.content = content + self.reasoning_content = reasoning_content + self.tool_calls = tool_calls + + +def _syntax_err() -> ModelSyntaxError: + return ModelSyntaxError("bad ReAct") + + +def _action() -> _Resp: + """A valid parsed response that carries a tool call (no final answer).""" + return _Resp(reasoning_content="thought", tool_calls=[_ToolCall("some_tool")]) + + +def _final() -> _Resp: + return _Resp(content="final answer") + + +class _ScriptedLeader: + """LeaderAgent stub whose dialog methods pop a scripted queue. + + Each queue holds either a _Resp (returned) or a ModelSyntaxError (raised). + """ + + def __init__(self) -> None: + self.begin_dialog_q: list = [] + self.send_correction_q: list = [] + self.continue_dialog_q: list = [] + self.call_tool_q: list = [] + + @staticmethod + def _pop(queue: list): + item = queue.pop(0) + if isinstance(item, Exception): + raise item + return item + + async def begin_dialog(self, task, trace_ctx=None): + return self._pop(self.begin_dialog_q) + + async def send_correction(self, message, trace_ctx=None): + return self._pop(self.send_correction_q) + + async def continue_dialog(self, prior_response, observation, trace_ctx=None): + return self._pop(self.continue_dialog_q) + + async def _call_tool(self, tool_call): + return self._pop(self.call_tool_q) + + +async def _noop_dialog_changed(*args, **kwargs) -> None: + pass + + +async def _drive(leader: _ScriptedLeader) -> str: + """Run one LeaderDriven.run() dialog and return its final status. + + Bypasses LeaderDriven.__init__ (which needs cfg/apis/model_client); run() + only touches the attributes wired up here. + """ + pattern = LeaderDriven.__new__(LeaderDriven) + pattern.dialog_counter = 1 + pattern._summarize_task = None # type: ignore[attr-defined] + pattern.log_info = lambda *a, **k: None # type: ignore[assignment] + pattern.log_error = lambda *a, **k: None # type: ignore[assignment] + pattern.dialog_changed = _noop_dialog_changed # type: ignore[assignment] + pattern.leader_agent = leader # type: ignore[assignment] + + captured: dict = {} + + async def _record(*args) -> None: + # _background_summarize_and_store(dialog_id, task, dialog_context) + captured["ctx"] = args[-1] + + pattern._background_summarize_and_store = _record # type: ignore[assignment] + + await pattern.run("do the thing") + await pattern._summarize_task # type: ignore[attr-defined] + return captured["ctx"]["status"] + + +def test_cumulative_errors_with_recovery_complete(): + """>=3 total syntax errors, each recovered from, must NOT abort. + + Under the old cumulative counting this sequence aborted at the third error + (status "failed"); with the reset-on-recovery it completes. + """ + leader = _ScriptedLeader() + leader.begin_dialog_q = [_syntax_err()] + # Three recoveries at site (a), each producing a valid tool call: + leader.send_correction_q = [_action(), _action(), _action()] + leader.call_tool_q = ["ok", "ok", "ok"] + # Two errors interleaved, then a final answer on the third continue: + leader.continue_dialog_q = [_syntax_err(), _syntax_err(), _final()] + + status = asyncio.run(_drive(leader)) + + assert status == "completed" + + +def test_consecutive_errors_still_abort(): + """Three consecutive syntax errors (no recovery) must still abort.""" + leader = _ScriptedLeader() + leader.begin_dialog_q = [_syntax_err()] + leader.send_correction_q = [_syntax_err(), _syntax_err()] + + status = asyncio.run(_drive(leader)) + + assert status == "failed" + # Sanity: we exercised the abort threshold, not some other failure path. + assert len(leader.send_correction_q) == 0 diff --git a/copilotj/test/util/test_proxy.py b/copilotj/test/util/test_proxy.py new file mode 100644 index 00000000..017f67cf --- /dev/null +++ b/copilotj/test/util/test_proxy.py @@ -0,0 +1,73 @@ +# SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project. +# +# SPDX-License-Identifier: Apache-2.0 + +"""Tests for the proxy helpers in :mod:`copilotj.util.env`. + +Covers :func:`proxy_dict` and the scoped :func:`temporary_proxy` context manager +(the mechanism used for the env-only cellpose/stardist weight downloads). +""" + +from __future__ import annotations + +import os + +import pytest + +from copilotj.core.config import Config +from copilotj.util import proxy_dict, temporary_proxy + +_PROXY_ENV_KEYS = ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "http_proxy", "https_proxy", "all_proxy") + + +@pytest.fixture +def clean_proxy_env(monkeypatch): + """Remove any pre-existing proxy env vars so tests start from a clean baseline.""" + for key in _PROXY_ENV_KEYS: + monkeypatch.delenv(key, raising=False) + + +# --------------------------------------------------------------------------- # +# proxy_dict +# --------------------------------------------------------------------------- # + + +def test_proxy_dict_returns_dict_when_set(): + proxies = proxy_dict(Config(cij_proxy="http://127.0.0.1:8080")) + assert proxies == {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"} + + +def test_proxy_dict_none_when_unset(): + assert proxy_dict(Config()) is None + + +# --------------------------------------------------------------------------- # +# temporary_proxy (scoped env — the cellpose/stardist path) +# --------------------------------------------------------------------------- # + + +def test_temporary_proxy_sets_env_from_cij(monkeypatch, clean_proxy_env): + cfg = Config(cij_proxy="http://127.0.0.1:8080") + with temporary_proxy(cfg): + assert os.environ["HTTP_PROXY"] == "http://127.0.0.1:8080" + assert os.environ["HTTPS_PROXY"] == "http://127.0.0.1:8080" + assert os.environ["ALL_PROXY"] == "http://127.0.0.1:8080" + # Restored on exit + for key in _PROXY_ENV_KEYS: + assert key not in os.environ + + +def test_temporary_proxy_restores_prior_value(monkeypatch, clean_proxy_env): + monkeypatch.setenv("HTTP_PROXY", "http://pre-existing:1") + monkeypatch.setenv("HTTPS_PROXY", "http://pre-existing:1") + cfg = Config(cij_proxy="http://127.0.0.1:8080") + with temporary_proxy(cfg): + assert os.environ["HTTP_PROXY"] == "http://127.0.0.1:8080" + assert os.environ["HTTP_PROXY"] == "http://pre-existing:1" + + +def test_temporary_proxy_noop_without_proxy(clean_proxy_env): + with temporary_proxy(Config()): + pass + for key in _PROXY_ENV_KEYS: + assert key not in os.environ diff --git a/copilotj/util/env.py b/copilotj/util/env.py index 6fd6a9f2..ec767199 100644 --- a/copilotj/util/env.py +++ b/copilotj/util/env.py @@ -7,7 +7,23 @@ from copilotj.core.config import Config -__all__ = ["temporary_proxy"] +__all__ = ["proxy_dict", "temporary_proxy"] + + +def proxy_dict(cfg: Config) -> dict[str, str] | None: + """Return an explicit ``{"http":..,"https":..}`` proxies dict from ``cfg.cij_proxy``. + + Returns ``None`` when no download proxy is configured, so callers can pass the + result directly to clients that accept ``proxies=None`` (e.g. ``requests``, + ``TavilyClient``). + + >>> from copilotj.core.config import Config + >>> proxy_dict(Config(cij_proxy="http://127.0.0.1:8080")) + {'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'} + >>> proxy_dict(Config()) is None + True + """ + return {"http": cfg.cij_proxy, "https": cfg.cij_proxy} if cfg.cij_proxy else None @contextlib.contextmanager @@ -16,7 +32,7 @@ def temporary_proxy(cfg: Config, default_value: str | None = None): Notes: not thread-safe, use with caution in multi-threaded environments. """ - proxy = default_value or cfg.llm_proxy + proxy = default_value or cfg.llm_proxy or cfg.cij_proxy keys = ["HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"] old_env = {k: os.environ.get(k) for k in keys + [k.lower() for k in keys]} diff --git a/flake.nix b/flake.nix index 2404d9c5..90a10901 100644 --- a/flake.nix +++ b/flake.nix @@ -153,7 +153,6 @@ UV_PYTHON = python.interpreter; # JDK configuration JAVA_HOME = pkgs.jdk21.home; - JAVA8_HOME = pkgs.jdk8.home; } // lib.optionalAttrs pkgs.stdenv.isLinux { # Python libraries often load native shared objects using dlopen(3). @@ -206,7 +205,6 @@ # JDK configuration JAVA_HOME = pkgs.jdk21.home; - JAVA8_HOME = pkgs.jdk8.home; }; shellHook = '' diff --git a/justfile b/justfile index 1c5529f0..e76f032a 100644 --- a/justfile +++ b/justfile @@ -2,7 +2,6 @@ # # JDK configuration (set via flake.nix env vars): # JAVA_HOME -> JDK 21 (builds the single JAR: core -> Java 8 bytecode, MCP -> Java 17) -# JAVA8_HOME -> JDK 8 (only for ad-hoc Java 8 downgrade checks; not required to build) default: @just --list diff --git a/plugin/pom.xml b/plugin/pom.xml index d5387872..3bf4d918 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -5,7 +5,7 @@ org.scijava pom-scijava - 38.0.1 + 40.0.0 diff --git a/scripts/rag_builder.py b/scripts/rag_builder.py index 0eb3c391..4186ed59 100644 --- a/scripts/rag_builder.py +++ b/scripts/rag_builder.py @@ -111,6 +111,10 @@ def build_rag(data_dir: Path = DEFAULT_DATA_DIR, index_dir: Path = DEFAULT_INDEX raise ValueError(f"No text chunks created from {data_dir}") index_dir.mkdir(parents=True, exist_ok=True) + from copilotj.core.config import load_config + from copilotj.core.embedding import configure_download_proxy + + configure_download_proxy(load_config()) vector_store = FAISS.from_documents(chunks, get_embeddings()) vector_store.save_local(str(index_dir), index_name=INDEX_NAME) jsonl_path = index_dir / f"{INDEX_NAME}.jsonl.gz" diff --git a/uv.lock b/uv.lock index 485f57c9..2d01cbf6 100644 --- a/uv.lock +++ b/uv.lock @@ -4206,11 +4206,11 @@ wheels = [ [[package]] name = "pypdf" -version = "6.12.2" +version = "6.13.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/6d/20879428577c1e57ecd41b69dc86beabf43db9287ad2e702207f8b48c751/pypdf-6.12.2.tar.gz", hash = "sha256:111669eb6680c04495ae0c113a1476e3bf93a95761d23c7406b591c80a6490b1", size = 6468184, upload-time = "2026-05-26T13:31:26.911Z" } +sdist = { url = "https://files.pythonhosted.org/packages/17/18/9947cc201af9ccf76720fd3347bf4f70eb882ce3fcf4cb05f7443e4cf871/pypdf-6.13.3.tar.gz", hash = "sha256:f3cb822769725f1bac658c406cfc9460399043f3750c2d3e4650e0a85eacabd7", size = 6484063, upload-time = "2026-06-17T15:22:00.898Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/44/fee070a16639d9869bb6a7e0f3a1b3946da1d66f32b9260b4d19cb90d7b2/pypdf-6.12.2-py3-none-any.whl", hash = "sha256:67b2699357a1f3f4c945940ea80826349ee507c9e2577724a14b4941982c104d", size = 343865, upload-time = "2026-05-26T13:31:25.068Z" }, + { url = "https://files.pythonhosted.org/packages/94/56/2967e621598987905fb8cdfadd8f8de6b5c68c9351f0523c4df8409f28f1/pypdf-6.13.3-py3-none-any.whl", hash = "sha256:c6e3f86afb625791510b02ad5480e94b63970bb957df75d44657c282ecc52224", size = 347288, upload-time = "2026-06-17T15:21:59.512Z" }, ] [[package]] diff --git a/web/package.json b/web/package.json index 06532830..13109875 100644 --- a/web/package.json +++ b/web/package.json @@ -37,7 +37,7 @@ "prettier-plugin-organize-imports": "^4.3.0", "typescript": "~5.9.3", "unplugin-vue-components": "^32.1.0", - "vite": "^6.4.3", + "vite": "^8.0.12", "vue-tsc": "^3.3.5" }, "prettier": { diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 128607e0..8e719b7a 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -16,16 +16,16 @@ importers: version: 2.0.3 '@tabler/icons-vue': specifier: ^3.44.0 - version: 3.44.0(vue@3.5.38(typescript@5.9.3)) + version: 3.44.0(vue@3.5.39(typescript@5.9.3)) '@tailwindcss/typography': specifier: ^0.5.20 version: 0.5.20(tailwindcss@4.3.1) '@tailwindcss/vite': specifier: ^4.3.1 - version: 4.3.1(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)) + version: 4.3.1(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0)) '@vueuse/core': specifier: ^14.3.0 - version: 14.3.0(vue@3.5.38(typescript@5.9.3)) + version: 14.3.0(vue@3.5.39(typescript@5.9.3)) date-fns: specifier: ^4.4.0 version: 4.4.0 @@ -43,10 +43,10 @@ importers: version: 2.2.4(marked@18.0.5) pinia: specifier: ^3.0.4 - version: 3.0.4(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3)) + version: 3.0.4(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)) primevue: specifier: ^4.5.5 - version: 4.5.5(vue@3.5.38(typescript@5.9.3)) + version: 4.5.5(vue@3.5.39(typescript@5.9.3)) tailwind-merge: specifier: ^3.6.0 version: 3.6.0 @@ -61,26 +61,26 @@ importers: version: 14.0.0 vue: specifier: ^3.5.38 - version: 3.5.38(typescript@5.9.3) + version: 3.5.39(typescript@5.9.3) vue-router: specifier: ^5.1.0 - version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3)))(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + version: 5.1.0(@vue/compiler-sfc@3.5.39)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)))(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@5.9.3)) devDependencies: '@primevue/auto-import-resolver': specifier: ^4.5.5 version: 4.5.5 '@types/node': specifier: ^26.0.0 - version: 26.0.0 + version: 26.0.1 '@types/uuid': specifier: ^11.0.0 version: 11.0.0 '@vitejs/plugin-vue': specifier: ^6.0.7 - version: 6.0.7(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + version: 6.0.7(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@5.9.3)) '@vue/tsconfig': specifier: ^0.9.1 - version: 0.9.1(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3)) + version: 0.9.1(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)) prettier-plugin-organize-imports: specifier: ^4.3.0 version: 4.3.0(prettier@3.6.0)(typescript@5.9.3)(vue-tsc@3.3.5(typescript@5.9.3)) @@ -89,10 +89,10 @@ importers: version: 5.9.3 unplugin-vue-components: specifier: ^32.1.0 - version: 32.1.0(vue@3.5.38(typescript@5.9.3)) + version: 32.1.0(vue@3.5.39(typescript@5.9.3)) vite: - specifier: ^6.4.3 - version: 6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) + specifier: ^8.0.12 + version: 8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0) vue-tsc: specifier: ^3.3.5 version: 3.3.5(typescript@5.9.3) @@ -137,161 +137,14 @@ packages: resolution: {integrity: sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==} engines: {node: ^22.18.0 || >=24.11.0} - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -309,6 +162,15 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@oxc-project/types@0.133.0': + resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + '@primeuix/styled@0.7.4': resolution: {integrity: sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ==} engines: {node: '>=12.11.0'} @@ -341,133 +203,103 @@ packages: resolution: {integrity: sha512-ZYLu9m3Nm5BmL0woqCJX84EZfLBepJn+T19v5oj3PlsMpUVNnDAsm2jgYdT6/b3RkdOuccxk4Pg/0EvlATOAhA==} engines: {node: '>=12.11.0'} - '@rolldown/pluginutils@1.0.1': - resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - - '@rollup/rollup-android-arm-eabi@4.62.0': - resolution: {integrity: sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.62.0': - resolution: {integrity: sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==} + '@rolldown/binding-android-arm64@1.0.3': + resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.62.0': - resolution: {integrity: sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==} + '@rolldown/binding-darwin-arm64@1.0.3': + resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.62.0': - resolution: {integrity: sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==} + '@rolldown/binding-darwin-x64@1.0.3': + resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.62.0': - resolution: {integrity: sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.62.0': - resolution: {integrity: sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==} + '@rolldown/binding-freebsd-x64@1.0.3': + resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.62.0': - resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.62.0': - resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.62.0': - resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==} + '@rolldown/binding-linux-arm64-gnu@1.0.3': + resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.62.0': - resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==} + '@rolldown/binding-linux-arm64-musl@1.0.3': + resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.62.0': - resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-loong64-musl@4.62.0': - resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-ppc64-gnu@4.62.0': - resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-ppc64-musl@4.62.0': - resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==} + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.62.0': - resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-riscv64-musl@4.62.0': - resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.62.0': - resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==} + '@rolldown/binding-linux-s390x-gnu@1.0.3': + resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.62.0': - resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==} + '@rolldown/binding-linux-x64-gnu@1.0.3': + resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.62.0': - resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==} + '@rolldown/binding-linux-x64-musl@1.0.3': + resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] - '@rollup/rollup-openbsd-x64@4.62.0': - resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.62.0': - resolution: {integrity: sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==} + '@rolldown/binding-openharmony-arm64@1.0.3': + resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.62.0': - resolution: {integrity: sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==} - cpu: [arm64] - os: [win32] + '@rolldown/binding-wasm32-wasi@1.0.3': + resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] - '@rollup/rollup-win32-ia32-msvc@4.62.0': - resolution: {integrity: sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==} - cpu: [ia32] + '@rolldown/binding-win32-arm64-msvc@1.0.3': + resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.62.0': - resolution: {integrity: sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==} + '@rolldown/binding-win32-x64-msvc@1.0.3': + resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.62.0': - resolution: {integrity: sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==} - cpu: [x64] - os: [win32] + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} '@tabler/icons-vue@3.44.0': resolution: {integrity: sha512-mABxdhq3SWo2ZI77w/t0reiOGNim/SEDSlfMT5PeiWA3cZwnZoQUYRiq/X6SgeTaA7LzCTX0IuvQWVf4RWOvsg==} @@ -515,24 +347,28 @@ packages: engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.3.1': resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.3.1': resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.3.1': resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.3.1': resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} @@ -572,14 +408,14 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 - '@types/estree@1.0.9': - resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} '@types/jsesc@2.5.1': resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} - '@types/node@26.0.0': - resolution: {integrity: sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==} + '@types/node@26.0.1': + resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==} '@types/uuid@11.0.0': resolution: {integrity: sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==} @@ -613,56 +449,80 @@ packages: vue: optional: true + '@vue/compiler-core@3.5.30': + resolution: {integrity: sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==} + '@vue/compiler-core@3.5.38': resolution: {integrity: sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==} + '@vue/compiler-core@3.5.39': + resolution: {integrity: sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==} + + '@vue/compiler-dom@3.5.30': + resolution: {integrity: sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==} + '@vue/compiler-dom@3.5.38': resolution: {integrity: sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==} - '@vue/compiler-sfc@3.5.38': - resolution: {integrity: sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==} + '@vue/compiler-dom@3.5.39': + resolution: {integrity: sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==} + + '@vue/compiler-sfc@3.5.30': + resolution: {integrity: sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==} - '@vue/compiler-ssr@3.5.38': - resolution: {integrity: sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==} + '@vue/compiler-sfc@3.5.39': + resolution: {integrity: sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==} + + '@vue/compiler-ssr@3.5.30': + resolution: {integrity: sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==} + + '@vue/compiler-ssr@3.5.39': + resolution: {integrity: sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==} '@vue/devtools-api@7.7.9': resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} - '@vue/devtools-api@8.1.3': - resolution: {integrity: sha512-73NMCvxXh8Hyozc/jiwqTFWVcCMyi11U1zmrq4DoukQJnuo8JHt6FsNu4HdeUDa8SpIp5vb7Q22GWgIq0efsXg==} + '@vue/devtools-api@8.1.5': + resolution: {integrity: sha512-YJipMVAKe5wT5CWf5kTYCaNV7NMNjFVxJkIkJaJ4W/nCxEBzlZzrOsYKeCymdCrFZmBS/+wTWFoUs3Jf/Q6XSQ==} '@vue/devtools-kit@7.7.9': resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} - '@vue/devtools-kit@8.1.3': - resolution: {integrity: sha512-cRn7GXiCQkMYU2Z3h3pM4YO/ndbx9FY1yLDAqIqPLcmIq4H6zAOJHein6tvZU3AfPwgrodqLiPBEF+YQaS8AxA==} + '@vue/devtools-kit@8.1.5': + resolution: {integrity: sha512-FcSAxsi4eWuXLCB7Rv9lj0aIVHHPNVQ2BazGf4RJTc2JCqb4BQg0hk87ZFhminCfl+mD5OUI0rX2cgyu4kJOGA==} '@vue/devtools-shared@7.7.9': resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} - '@vue/devtools-shared@8.1.3': - resolution: {integrity: sha512-CM3uIPL+v+lrJUk33+pxspYo0MhuMWlCvf7zC9fybifvCPyM2jUbYRPwoYEJgYbwRqPikm5HozbUhp60MF2QuA==} + '@vue/devtools-shared@8.1.5': + resolution: {integrity: sha512-mhT4zcPFhF+Xk1O4BfhhrbXzpmfqY03fS6xGpcllbQG7lDjhQf8pQHcTIhqQIYx1hfwtHmk/6jM96ele0UxPqQ==} '@vue/language-core@3.3.5': resolution: {integrity: sha512-UkKu5nhX89fg4VhlG/FOeI10G3cj/7radKT/cy9BT4Q9qJmJlSTAc/dP63Xqs29aypN4f39xUV6PsLNk/dcD6g==} - '@vue/reactivity@3.5.38': - resolution: {integrity: sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==} + '@vue/reactivity@3.5.39': + resolution: {integrity: sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==} - '@vue/runtime-core@3.5.38': - resolution: {integrity: sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==} + '@vue/runtime-core@3.5.39': + resolution: {integrity: sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==} - '@vue/runtime-dom@3.5.38': - resolution: {integrity: sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==} + '@vue/runtime-dom@3.5.39': + resolution: {integrity: sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==} - '@vue/server-renderer@3.5.38': - resolution: {integrity: sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==} + '@vue/server-renderer@3.5.39': + resolution: {integrity: sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==} peerDependencies: - vue: 3.5.38 + vue: 3.5.39 + + '@vue/shared@3.5.30': + resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==} '@vue/shared@3.5.38': resolution: {integrity: sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==} + '@vue/shared@3.5.39': + resolution: {integrity: sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==} + '@vue/tsconfig@0.9.1': resolution: {integrity: sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==} peerDependencies: @@ -687,8 +547,8 @@ packages: peerDependencies: vue: ^3.5.0 - acorn@8.17.0: - resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} hasBin: true @@ -746,11 +606,6 @@ packages: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} - engines: {node: '>=18'} - hasBin: true - estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -834,24 +689,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -869,6 +728,10 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + local-pkg@1.1.2: + resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} + engines: {node: '>=14'} + local-pkg@1.2.1: resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} engines: {node: '>=14'} @@ -893,6 +756,9 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + mlly@1.8.1: + resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==} + mlly@1.8.2: resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} @@ -939,8 +805,8 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - pkg-types@2.3.1: - resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + pkg-types@2.3.0: + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} postcss-selector-parser@6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} @@ -979,9 +845,9 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.62.0: - resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + rolldown@1.0.3: + resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true scule@1.3.0: @@ -1018,13 +884,16 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true - ufo@1.6.4: - resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + ufo@1.6.3: + resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} @@ -1054,31 +923,34 @@ packages: resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} hasBin: true - vite@6.4.3: - resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite@8.0.16: + resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -1121,8 +993,8 @@ packages: peerDependencies: typescript: '>=5.0.0' - vue@3.5.38: - resolution: {integrity: sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==} + vue@3.5.39: + resolution: {integrity: sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -1174,82 +1046,20 @@ snapshots: '@babel/helper-string-parser': 8.0.0 '@babel/helper-validator-identifier': 8.0.2 - '@esbuild/aix-ppc64@0.25.12': - optional: true - - '@esbuild/android-arm64@0.25.12': - optional: true - - '@esbuild/android-arm@0.25.12': - optional: true - - '@esbuild/android-x64@0.25.12': - optional: true - - '@esbuild/darwin-arm64@0.25.12': - optional: true - - '@esbuild/darwin-x64@0.25.12': - optional: true - - '@esbuild/freebsd-arm64@0.25.12': - optional: true - - '@esbuild/freebsd-x64@0.25.12': - optional: true - - '@esbuild/linux-arm64@0.25.12': - optional: true - - '@esbuild/linux-arm@0.25.12': - optional: true - - '@esbuild/linux-ia32@0.25.12': - optional: true - - '@esbuild/linux-loong64@0.25.12': - optional: true - - '@esbuild/linux-mips64el@0.25.12': - optional: true - - '@esbuild/linux-ppc64@0.25.12': - optional: true - - '@esbuild/linux-riscv64@0.25.12': - optional: true - - '@esbuild/linux-s390x@0.25.12': - optional: true - - '@esbuild/linux-x64@0.25.12': - optional: true - - '@esbuild/netbsd-arm64@0.25.12': - optional: true - - '@esbuild/netbsd-x64@0.25.12': - optional: true - - '@esbuild/openbsd-arm64@0.25.12': - optional: true - - '@esbuild/openbsd-x64@0.25.12': - optional: true - - '@esbuild/openharmony-arm64@0.25.12': - optional: true - - '@esbuild/sunos-x64@0.25.12': - optional: true - - '@esbuild/win32-arm64@0.25.12': + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 optional: true - '@esbuild/win32-ia32@0.25.12': + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 optional: true - '@esbuild/win32-x64@0.25.12': + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 optional: true '@jridgewell/gen-mapping@0.3.13': @@ -1271,6 +1081,15 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@oxc-project/types@0.133.0': {} + '@primeuix/styled@0.7.4': dependencies: '@primeuix/utils': 0.6.4 @@ -1289,102 +1108,76 @@ snapshots: dependencies: '@primevue/metadata': 4.5.5 - '@primevue/core@4.5.5(vue@3.5.38(typescript@5.9.3))': + '@primevue/core@4.5.5(vue@3.5.39(typescript@5.9.3))': dependencies: '@primeuix/styled': 0.7.4 '@primeuix/utils': 0.6.4 - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.39(typescript@5.9.3) - '@primevue/icons@4.5.5(vue@3.5.38(typescript@5.9.3))': + '@primevue/icons@4.5.5(vue@3.5.39(typescript@5.9.3))': dependencies: '@primeuix/utils': 0.6.4 - '@primevue/core': 4.5.5(vue@3.5.38(typescript@5.9.3)) + '@primevue/core': 4.5.5(vue@3.5.39(typescript@5.9.3)) transitivePeerDependencies: - vue '@primevue/metadata@4.5.5': {} - '@rolldown/pluginutils@1.0.1': {} - - '@rollup/rollup-android-arm-eabi@4.62.0': - optional: true - - '@rollup/rollup-android-arm64@4.62.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.62.0': - optional: true - - '@rollup/rollup-darwin-x64@4.62.0': - optional: true - - '@rollup/rollup-freebsd-arm64@4.62.0': - optional: true - - '@rollup/rollup-freebsd-x64@4.62.0': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.62.0': + '@rolldown/binding-android-arm64@1.0.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.62.0': + '@rolldown/binding-darwin-arm64@1.0.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.62.0': + '@rolldown/binding-darwin-x64@1.0.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.62.0': + '@rolldown/binding-freebsd-x64@1.0.3': optional: true - '@rollup/rollup-linux-loong64-gnu@4.62.0': + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': optional: true - '@rollup/rollup-linux-loong64-musl@4.62.0': + '@rolldown/binding-linux-arm64-gnu@1.0.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.62.0': + '@rolldown/binding-linux-arm64-musl@1.0.3': optional: true - '@rollup/rollup-linux-ppc64-musl@4.62.0': + '@rolldown/binding-linux-ppc64-gnu@1.0.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.62.0': + '@rolldown/binding-linux-s390x-gnu@1.0.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.62.0': + '@rolldown/binding-linux-x64-gnu@1.0.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.62.0': + '@rolldown/binding-linux-x64-musl@1.0.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.62.0': + '@rolldown/binding-openharmony-arm64@1.0.3': optional: true - '@rollup/rollup-linux-x64-musl@4.62.0': - optional: true - - '@rollup/rollup-openbsd-x64@4.62.0': - optional: true - - '@rollup/rollup-openharmony-arm64@4.62.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.62.0': + '@rolldown/binding-wasm32-wasi@1.0.3': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rollup/rollup-win32-ia32-msvc@4.62.0': + '@rolldown/binding-win32-arm64-msvc@1.0.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.62.0': + '@rolldown/binding-win32-x64-msvc@1.0.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.62.0': - optional: true + '@rolldown/pluginutils@1.0.1': {} - '@tabler/icons-vue@3.44.0(vue@3.5.38(typescript@5.9.3))': + '@tabler/icons-vue@3.44.0(vue@3.5.39(typescript@5.9.3))': dependencies: '@tabler/icons': 3.44.0 - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.39(typescript@5.9.3) '@tabler/icons@3.44.0': {} @@ -1454,18 +1247,21 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.3.1 - '@tailwindcss/vite@4.3.1(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.1(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.1 '@tailwindcss/oxide': 4.3.1 tailwindcss: 4.3.1 - vite: 6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) + vite: 8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0) - '@types/estree@1.0.9': {} + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true '@types/jsesc@2.5.1': {} - '@types/node@26.0.0': + '@types/node@26.0.1': dependencies: undici-types: 8.3.0 @@ -1475,11 +1271,11 @@ snapshots: '@types/web-bluetooth@0.0.21': {} - '@vitejs/plugin-vue@6.0.7(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) - vue: 3.5.38(typescript@5.9.3) + vite: 8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0) + vue: 3.5.39(typescript@5.9.3) '@volar/language-core@2.4.28': dependencies: @@ -1493,15 +1289,23 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue-macros/common@3.1.2(vue@3.5.38(typescript@5.9.3))': + '@vue-macros/common@3.1.2(vue@3.5.39(typescript@5.9.3))': dependencies: - '@vue/compiler-sfc': 3.5.38 + '@vue/compiler-sfc': 3.5.30 ast-kit: 2.2.0 - local-pkg: 1.2.1 + local-pkg: 1.1.2 magic-string-ast: 1.0.3 unplugin-utils: 0.3.1 optionalDependencies: - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.39(typescript@5.9.3) + + '@vue/compiler-core@3.5.30': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.30 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 '@vue/compiler-core@3.5.38': dependencies: @@ -1511,35 +1315,70 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 + '@vue/compiler-core@3.5.39': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.39 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.30': + dependencies: + '@vue/compiler-core': 3.5.30 + '@vue/shared': 3.5.30 + '@vue/compiler-dom@3.5.38': dependencies: '@vue/compiler-core': 3.5.38 '@vue/shared': 3.5.38 - '@vue/compiler-sfc@3.5.38': + '@vue/compiler-dom@3.5.39': + dependencies: + '@vue/compiler-core': 3.5.39 + '@vue/shared': 3.5.39 + + '@vue/compiler-sfc@3.5.30': dependencies: '@babel/parser': 7.29.7 - '@vue/compiler-core': 3.5.38 - '@vue/compiler-dom': 3.5.38 - '@vue/compiler-ssr': 3.5.38 - '@vue/shared': 3.5.38 + '@vue/compiler-core': 3.5.30 + '@vue/compiler-dom': 3.5.30 + '@vue/compiler-ssr': 3.5.30 + '@vue/shared': 3.5.30 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.15 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.38': + '@vue/compiler-sfc@3.5.39': dependencies: - '@vue/compiler-dom': 3.5.38 - '@vue/shared': 3.5.38 + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.39 + '@vue/compiler-dom': 3.5.39 + '@vue/compiler-ssr': 3.5.39 + '@vue/shared': 3.5.39 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.15 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.30': + dependencies: + '@vue/compiler-dom': 3.5.30 + '@vue/shared': 3.5.30 + + '@vue/compiler-ssr@3.5.39': + dependencies: + '@vue/compiler-dom': 3.5.39 + '@vue/shared': 3.5.39 '@vue/devtools-api@7.7.9': dependencies: '@vue/devtools-kit': 7.7.9 - '@vue/devtools-api@8.1.3': + '@vue/devtools-api@8.1.5': dependencies: - '@vue/devtools-kit': 8.1.3 + '@vue/devtools-kit': 8.1.5 '@vue/devtools-kit@7.7.9': dependencies: @@ -1551,9 +1390,9 @@ snapshots: speakingurl: 14.0.1 superjson: 2.2.6 - '@vue/devtools-kit@8.1.3': + '@vue/devtools-kit@8.1.5': dependencies: - '@vue/devtools-shared': 8.1.3 + '@vue/devtools-shared': 8.1.5 birpc: 2.9.0 hookable: 5.5.3 perfect-debounce: 2.1.0 @@ -1562,7 +1401,7 @@ snapshots: dependencies: rfdc: 1.4.1 - '@vue/devtools-shared@8.1.3': {} + '@vue/devtools-shared@8.1.5': {} '@vue/language-core@3.3.5': dependencies: @@ -1574,49 +1413,53 @@ snapshots: path-browserify: 1.0.1 picomatch: 4.0.4 - '@vue/reactivity@3.5.38': + '@vue/reactivity@3.5.39': dependencies: - '@vue/shared': 3.5.38 + '@vue/shared': 3.5.39 - '@vue/runtime-core@3.5.38': + '@vue/runtime-core@3.5.39': dependencies: - '@vue/reactivity': 3.5.38 - '@vue/shared': 3.5.38 + '@vue/reactivity': 3.5.39 + '@vue/shared': 3.5.39 - '@vue/runtime-dom@3.5.38': + '@vue/runtime-dom@3.5.39': dependencies: - '@vue/reactivity': 3.5.38 - '@vue/runtime-core': 3.5.38 - '@vue/shared': 3.5.38 + '@vue/reactivity': 3.5.39 + '@vue/runtime-core': 3.5.39 + '@vue/shared': 3.5.39 csstype: 3.2.3 - '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@5.9.3))': + '@vue/server-renderer@3.5.39(vue@3.5.39(typescript@5.9.3))': dependencies: - '@vue/compiler-ssr': 3.5.38 - '@vue/shared': 3.5.38 - vue: 3.5.38(typescript@5.9.3) + '@vue/compiler-ssr': 3.5.39 + '@vue/shared': 3.5.39 + vue: 3.5.39(typescript@5.9.3) + + '@vue/shared@3.5.30': {} '@vue/shared@3.5.38': {} - '@vue/tsconfig@0.9.1(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))': + '@vue/shared@3.5.39': {} + + '@vue/tsconfig@0.9.1(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))': optionalDependencies: typescript: 5.9.3 - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.39(typescript@5.9.3) - '@vueuse/core@14.3.0(vue@3.5.38(typescript@5.9.3))': + '@vueuse/core@14.3.0(vue@3.5.39(typescript@5.9.3))': dependencies: '@types/web-bluetooth': 0.0.21 '@vueuse/metadata': 14.3.0 - '@vueuse/shared': 14.3.0(vue@3.5.38(typescript@5.9.3)) - vue: 3.5.38(typescript@5.9.3) + '@vueuse/shared': 14.3.0(vue@3.5.39(typescript@5.9.3)) + vue: 3.5.39(typescript@5.9.3) '@vueuse/metadata@14.3.0': {} - '@vueuse/shared@14.3.0(vue@3.5.38(typescript@5.9.3))': + '@vueuse/shared@14.3.0(vue@3.5.39(typescript@5.9.3))': dependencies: - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.39(typescript@5.9.3) - acorn@8.17.0: {} + acorn@8.16.0: {} alien-signals@3.2.1: {} @@ -1662,35 +1505,6 @@ snapshots: entities@7.0.1: {} - esbuild@0.25.12: - 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 - estree-walker@2.0.2: {} exsolve@1.0.8: {} @@ -1765,10 +1579,16 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + local-pkg@1.1.2: + dependencies: + mlly: 1.8.1 + pkg-types: 2.3.0 + quansync: 0.2.11 + local-pkg@1.2.1: dependencies: - mlly: 1.8.2 - pkg-types: 2.3.1 + mlly: 1.8.1 + pkg-types: 2.3.0 quansync: 0.2.11 magic-string-ast@1.0.3: @@ -1787,12 +1607,19 @@ snapshots: mitt@3.0.1: {} + mlly@1.8.1: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.3 + mlly@1.8.2: dependencies: - acorn: 8.17.0 + acorn: 8.16.0 pathe: 2.0.3 pkg-types: 1.3.1 - ufo: 1.6.4 + ufo: 1.6.3 muggle-string@0.4.1: {} @@ -1812,20 +1639,20 @@ snapshots: picomatch@4.0.4: {} - pinia@3.0.4(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3)): + pinia@3.0.4(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)): dependencies: '@vue/devtools-api': 7.7.9 - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.39(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 pkg-types@1.3.1: dependencies: confbox: 0.1.8 - mlly: 1.8.2 + mlly: 1.8.1 pathe: 2.0.3 - pkg-types@2.3.1: + pkg-types@2.3.0: dependencies: confbox: 0.2.4 exsolve: 1.0.8 @@ -1851,13 +1678,13 @@ snapshots: prettier@3.6.0: {} - primevue@4.5.5(vue@3.5.38(typescript@5.9.3)): + primevue@4.5.5(vue@3.5.39(typescript@5.9.3)): dependencies: '@primeuix/styled': 0.7.4 '@primeuix/styles': 2.0.3 '@primeuix/utils': 0.6.4 - '@primevue/core': 4.5.5(vue@3.5.38(typescript@5.9.3)) - '@primevue/icons': 4.5.5(vue@3.5.38(typescript@5.9.3)) + '@primevue/core': 4.5.5(vue@3.5.39(typescript@5.9.3)) + '@primevue/icons': 4.5.5(vue@3.5.39(typescript@5.9.3)) transitivePeerDependencies: - vue @@ -1867,36 +1694,26 @@ snapshots: rfdc@1.4.1: {} - rollup@4.62.0: + rolldown@1.0.3: dependencies: - '@types/estree': 1.0.9 + '@oxc-project/types': 0.133.0 + '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.62.0 - '@rollup/rollup-android-arm64': 4.62.0 - '@rollup/rollup-darwin-arm64': 4.62.0 - '@rollup/rollup-darwin-x64': 4.62.0 - '@rollup/rollup-freebsd-arm64': 4.62.0 - '@rollup/rollup-freebsd-x64': 4.62.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.62.0 - '@rollup/rollup-linux-arm-musleabihf': 4.62.0 - '@rollup/rollup-linux-arm64-gnu': 4.62.0 - '@rollup/rollup-linux-arm64-musl': 4.62.0 - '@rollup/rollup-linux-loong64-gnu': 4.62.0 - '@rollup/rollup-linux-loong64-musl': 4.62.0 - '@rollup/rollup-linux-ppc64-gnu': 4.62.0 - '@rollup/rollup-linux-ppc64-musl': 4.62.0 - '@rollup/rollup-linux-riscv64-gnu': 4.62.0 - '@rollup/rollup-linux-riscv64-musl': 4.62.0 - '@rollup/rollup-linux-s390x-gnu': 4.62.0 - '@rollup/rollup-linux-x64-gnu': 4.62.0 - '@rollup/rollup-linux-x64-musl': 4.62.0 - '@rollup/rollup-openbsd-x64': 4.62.0 - '@rollup/rollup-openharmony-arm64': 4.62.0 - '@rollup/rollup-win32-arm64-msvc': 4.62.0 - '@rollup/rollup-win32-ia32-msvc': 4.62.0 - '@rollup/rollup-win32-x64-gnu': 4.62.0 - '@rollup/rollup-win32-x64-msvc': 4.62.0 - fsevents: 2.3.3 + '@rolldown/binding-android-arm64': 1.0.3 + '@rolldown/binding-darwin-arm64': 1.0.3 + '@rolldown/binding-darwin-x64': 1.0.3 + '@rolldown/binding-freebsd-x64': 1.0.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 + '@rolldown/binding-linux-arm64-gnu': 1.0.3 + '@rolldown/binding-linux-arm64-musl': 1.0.3 + '@rolldown/binding-linux-ppc64-gnu': 1.0.3 + '@rolldown/binding-linux-s390x-gnu': 1.0.3 + '@rolldown/binding-linux-x64-gnu': 1.0.3 + '@rolldown/binding-linux-x64-musl': 1.0.3 + '@rolldown/binding-openharmony-arm64': 1.0.3 + '@rolldown/binding-wasm32-wasi': 1.0.3 + '@rolldown/binding-win32-arm64-msvc': 1.0.3 + '@rolldown/binding-win32-x64-msvc': 1.0.3 scule@1.3.0: {} @@ -1923,9 +1740,12 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 + tslib@2.8.1: + optional: true + typescript@5.9.3: {} - ufo@1.6.4: {} + ufo@1.6.3: {} undici-types@8.3.0: {} @@ -1934,7 +1754,7 @@ snapshots: pathe: 2.0.3 picomatch: 4.0.4 - unplugin-vue-components@32.1.0(vue@3.5.38(typescript@5.9.3)): + unplugin-vue-components@32.1.0(vue@3.5.39(typescript@5.9.3)): dependencies: chokidar: 5.0.0 local-pkg: 1.2.1 @@ -1945,7 +1765,7 @@ snapshots: tinyglobby: 0.2.17 unplugin: 3.0.0 unplugin-utils: 0.3.1 - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.39(typescript@5.9.3) unplugin@3.0.0: dependencies: @@ -1957,32 +1777,30 @@ snapshots: uuid@14.0.0: {} - vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0): + vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0): dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.4) + lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.15 - rollup: 4.62.0 + rolldown: 1.0.3 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.0.0 + '@types/node': 26.0.1 fsevents: 2.3.3 jiti: 2.7.0 - lightningcss: 1.32.0 yaml: 2.9.0 vscode-uri@3.1.0: {} - vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3)))(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)): + vue-router@5.1.0(@vue/compiler-sfc@3.5.39)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)))(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@5.9.3)): dependencies: '@babel/generator': 8.0.0 - '@vue-macros/common': 3.1.2(vue@3.5.38(typescript@5.9.3)) - '@vue/devtools-api': 8.1.3 + '@vue-macros/common': 3.1.2(vue@3.5.39(typescript@5.9.3)) + '@vue/devtools-api': 8.1.5 ast-walker-scope: 0.9.0 chokidar: 5.0.0 json5: 2.2.3 - local-pkg: 1.2.1 + local-pkg: 1.1.2 magic-string: 0.30.21 mlly: 1.8.2 muggle-string: 0.4.1 @@ -1992,12 +1810,12 @@ snapshots: tinyglobby: 0.2.17 unplugin: 3.0.0 unplugin-utils: 0.3.1 - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.39(typescript@5.9.3) yaml: 2.9.0 optionalDependencies: - '@vue/compiler-sfc': 3.5.38 - pinia: 3.0.4(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3)) - vite: 6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) + '@vue/compiler-sfc': 3.5.39 + pinia: 3.0.4(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)) + vite: 8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0) vue-tsc@3.3.5(typescript@5.9.3): dependencies: @@ -2005,13 +1823,13 @@ snapshots: '@vue/language-core': 3.3.5 typescript: 5.9.3 - vue@3.5.38(typescript@5.9.3): + vue@3.5.39(typescript@5.9.3): dependencies: - '@vue/compiler-dom': 3.5.38 - '@vue/compiler-sfc': 3.5.38 - '@vue/runtime-dom': 3.5.38 - '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@5.9.3)) - '@vue/shared': 3.5.38 + '@vue/compiler-dom': 3.5.39 + '@vue/compiler-sfc': 3.5.39 + '@vue/runtime-dom': 3.5.39 + '@vue/server-renderer': 3.5.39(vue@3.5.39(typescript@5.9.3)) + '@vue/shared': 3.5.39 optionalDependencies: typescript: 5.9.3