diff --git a/.gitignore b/.gitignore index a24da76e..d7e0d237 100755 --- a/.gitignore +++ b/.gitignore @@ -166,3 +166,4 @@ node_modules/ # 建站模板必须完整入库(覆盖上面的 lib/ 与 AGENTS.md 规则),但依赖树除外 !docker/site-template/** docker/site-template/react-vite/node_modules/ +.jobtest/ diff --git a/src/backend/alembic/versions/ce_0006_job_runtime.py b/src/backend/alembic/versions/ce_0006_job_runtime.py new file mode 100644 index 00000000..a6d6c417 --- /dev/null +++ b/src/backend/alembic/versions/ce_0006_job_runtime.py @@ -0,0 +1,101 @@ +"""CE: add job runtime tables (jobs / job_items / job_calls) + +Revision ID: ce_0006 +Revises: ce_0005 +Create Date: 2026-08-17 + +作业编排运行时的持久化底座。CE 的 agent_factory 一直注册着 `run_job` 工具、 +工作流模式的提示词也在,但这三张表从来没进过 CE 迁移链——作业一提交就会在 +建台账那一步炸掉。这条迁移把 CE 补齐到与主仓一致。 + +台账(job_items)放 DB 而不是沙箱文件:沙箱池化复用会让新 job 读到旧 job 残留的 +账本,以 (job_id, item_key) 作复合主键从结构上避免。 +""" + +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects import postgresql + +revision = "ce_0006" +down_revision = "ce_0005" +branch_labels = None +depends_on = None + +JSONB = postgresql.JSONB(astext_type=sa.Text()).with_variant(sa.JSON(), "sqlite") + + +def upgrade() -> None: + op.create_table( + "jobs", + sa.Column("job_id", sa.String(64), primary_key=True), + sa.Column("user_id", sa.String(64), nullable=False), + sa.Column("chat_id", sa.String(64)), + sa.Column("name", sa.String(255), server_default=""), + sa.Column("status", sa.String(20), nullable=False, server_default="pending"), + sa.Column("script_path", sa.Text(), server_default=""), + sa.Column("script_text", sa.Text(), server_default=""), + sa.Column("sandbox_session_id", sa.String(128)), + sa.Column("budget", JSONB), + sa.Column("usage", JSONB), + sa.Column("metadata", JSONB), + sa.Column("error_message", sa.Text()), + sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.func.now()), + sa.Column("started_at", sa.TIMESTAMP(timezone=True)), + sa.Column("completed_at", sa.TIMESTAMP(timezone=True)), + sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.func.now()), + sa.ForeignKeyConstraint(["user_id"], ["users_shadow.user_id"], ondelete="CASCADE"), + sa.CheckConstraint( + "status IN ('pending','running','paused','completed','failed','cancelled','interrupted')", + name="jobs_status_check", + ), + ) + op.create_index("idx_jobs_user_id", "jobs", ["user_id"]) + op.create_index("idx_jobs_chat_status", "jobs", ["chat_id", "status"]) + op.create_index("idx_jobs_status", "jobs", ["status"]) + + op.create_table( + "job_items", + sa.Column("job_id", sa.String(64), primary_key=True, nullable=False), + sa.Column("item_key", sa.String(128), primary_key=True, nullable=False), + sa.Column("status", sa.String(20), nullable=False, server_default="pending"), + sa.Column("payload", JSONB), + sa.Column("result", JSONB), + sa.Column("review", JSONB), + sa.Column("attempts", sa.Integer(), server_default="0"), + sa.Column("error", sa.Text()), + sa.Column("updated_at", sa.TIMESTAMP(timezone=True), server_default=sa.func.now()), + sa.ForeignKeyConstraint(["job_id"], ["jobs.job_id"], ondelete="CASCADE"), + sa.CheckConstraint( + "status IN ('pending','running','done','not_found','failed','needs_review')", + name="job_items_status_check", + ), + ) + op.create_index("idx_job_items_job_status", "job_items", ["job_id", "status"]) + + op.create_table( + "job_calls", + sa.Column("call_id", sa.String(64), primary_key=True), + sa.Column("job_id", sa.String(64), nullable=False), + sa.Column("item_key", sa.String(128)), + sa.Column("seq", sa.Integer(), server_default="0"), + sa.Column("prompt_hash", sa.String(64)), + sa.Column("model", sa.String(128)), + sa.Column("tokens", JSONB), + sa.Column("duration_ms", sa.BigInteger(), server_default="0"), + sa.Column("status", sa.String(20), server_default="running"), + sa.Column("error", sa.Text()), + sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.func.now()), + sa.ForeignKeyConstraint(["job_id"], ["jobs.job_id"], ondelete="CASCADE"), + ) + op.create_index("idx_job_calls_job_id", "job_calls", ["job_id"]) + + +def downgrade() -> None: + op.drop_index("idx_job_calls_job_id", table_name="job_calls") + op.drop_table("job_calls") + op.drop_index("idx_job_items_job_status", table_name="job_items") + op.drop_table("job_items") + op.drop_index("idx_jobs_status", table_name="jobs") + op.drop_index("idx_jobs_chat_status", table_name="jobs") + op.drop_index("idx_jobs_user_id", table_name="jobs") + op.drop_table("jobs") diff --git a/src/backend/api/app.py b/src/backend/api/app.py index 76763584..80d3c68a 100644 --- a/src/backend/api/app.py +++ b/src/backend/api/app.py @@ -56,6 +56,8 @@ async def lifespan(app: FastAPI): await _startup_local_sidecars() await _startup_recover_chat_runs() await _startup_resume_loops() + await _startup_recover_jobs() + await _startup_orphan_job_reaper() await _startup_stale_run_reaper() await _startup_warm_sandbox_pool() await _startup_idle_session_reaper() @@ -78,6 +80,7 @@ async def lifespan(app: FastAPI): yield # ── shutdown ── await _shutdown_stale_run_reaper() + await _shutdown_orphan_job_reaper() await _shutdown_kb_wiki_worker() await _shutdown_channel_manager() await _shutdown_datasource_sidecar_recovery() @@ -472,6 +475,36 @@ async def _startup_resume_loops(): logger.warning("[startup] autonomous loop resume failed: %s", exc) +async def _startup_recover_jobs(): + """作业编排:进程重启后活跃 job 全是孤儿(进程内没有 driver task),归位 interrupted。 + + 与 chat_run 不同的是,job 的工作项台账在 DB —— 归位后 ``run_job action=resume`` + 可直接断点续跑,已完成的项不会重做。 + """ + try: + from orchestration import job_runtime + + count = await job_runtime.resume_running_jobs() + if count: + logger.info("[startup] orphan jobs recovered: %d", count) + except Exception as exc: + logger.warning("[startup] job orphan recovery failed: %s", exc) + + +async def _startup_orphan_job_reaper(): + """周期性对账失联的批量作业(驱动没了就没人管,护栏跟着一起没)。""" + try: + import asyncio + + from orchestration import job_runtime + + task = asyncio.create_task(job_runtime.run_job_reaper_loop()) + app.state.orphan_job_reaper_task = task + logger.info("[startup] job orphan reaper started") + except Exception as exc: + logger.warning("[startup] job orphan reaper start failed: %s", exc) + + async def _startup_stale_run_reaper(): """Periodically reap zombie chat_runs stuck in running (fallback behind the watchdog).""" try: @@ -497,6 +530,17 @@ async def _shutdown_stale_run_reaper(): await task +async def _shutdown_orphan_job_reaper(): + import asyncio + import contextlib + + task = getattr(app.state, "orphan_job_reaper_task", None) + if task is not None: + task.cancel() + with contextlib.suppress(asyncio.CancelledError): + await task + + async def _shutdown_channel_manager(): try: from core.channels.manager import get_manager diff --git a/src/backend/api/routes/v1/__init__.py b/src/backend/api/routes/v1/__init__.py index a37ddcb5..83e288d8 100644 --- a/src/backend/api/routes/v1/__init__.py +++ b/src/backend/api/routes/v1/__init__.py @@ -32,6 +32,11 @@ ("myspace_folders", "router"), ("batch", "router"), ("internal_batch", "router"), + # 作业编排:沙箱里的作业脚本经 internal_jobs 回调后端(建台账、派子智能体、报终态), + # jobs 是给人看的只读进度视图(输入框上方那条状态条)。两条都必须在——CE 的 + # agent_factory 已经注册了 run_job 工具,少了回调路由,作业会在第一发回调 404 当场死掉。 + ("internal_jobs", "router"), + ("jobs", "router"), ("internal_sites", "router"), ("projects", "router"), ("api_keys", "router"), diff --git a/src/backend/api/routes/v1/chats.py b/src/backend/api/routes/v1/chats.py index 38090e0a..0e1a582d 100644 --- a/src/backend/api/routes/v1/chats.py +++ b/src/backend/api/routes/v1/chats.py @@ -788,6 +788,7 @@ def _build_ctx( "plugin_name": request.plugin_name, "plan_chat": request.plan_chat, "batch_chat": request.batch_chat, + "workflow_chat": request.workflow_chat, "disable_batch_plan": request.disable_batch_plan, **project_ctx, } @@ -803,6 +804,7 @@ def _ensure_chat_session( agent_name: Optional[str] = None, plan_chat: bool = False, batch_chat: bool = False, + workflow_chat: bool = False, project_id: Optional[str] = None, ): extra_data: Dict[str, Any] = {"chat_id": chat_id} @@ -814,6 +816,8 @@ def _ensure_chat_session( extra_data["plan_chat"] = True if batch_chat: extra_data["batch_chat"] = True + if workflow_chat: + extra_data["workflow_chat"] = True # Prefer the edition-aware access resolver before creating a session. pair = chat_service.get_session_with_access(chat_id, user_id) if pair is not None: @@ -850,6 +854,9 @@ def _ensure_chat_session( if batch_chat and not existing_meta.get("batch_chat"): merged["batch_chat"] = True dirty = True + if workflow_chat and not existing_meta.get("workflow_chat"): + merged["workflow_chat"] = True + dirty = True if dirty: chat_service.update_session(chat_id, user_id, {"extra_data": merged}) return session @@ -928,6 +935,7 @@ async def chat_send( agent_name=_agent_name, plan_chat=request.plan_chat, batch_chat=request.batch_chat, + workflow_chat=request.workflow_chat, project_id=request.project_id, ) # Link orphan artifacts (uploaded before session existed) to this chat @@ -1129,6 +1137,7 @@ def _read_messages(): agent_name=_agent_name_stream, plan_chat=request.plan_chat, batch_chat=request.batch_chat, + workflow_chat=request.workflow_chat, project_id=request.project_id, ) diff --git a/src/backend/api/routes/v1/internal_jobs.py b/src/backend/api/routes/v1/internal_jobs.py new file mode 100644 index 00000000..db48f6ed --- /dev/null +++ b/src/backend/api/routes/v1/internal_jobs.py @@ -0,0 +1,462 @@ +"""作业脚本的回调端点 —— 沙箱里的作业脚本经此请求后端。 + +这是「模型凭据不进沙箱」的关键:脚本自己没有任何 key,需要一次模型判断时就带 job token +POST 到这里,由后端代持凭据派出子智能体。token 能做的事只有三件——派子作业、读写本 job +的台账、报进度;换不到模型端点、碰不到别的 job、碰不到任何用户数据接口。 + +端点: + POST /v1/internal/jobs/{job_id}/agent 派一个无历史子智能体,同步返回结构化结果 + POST /v1/internal/jobs/{job_id}/ledger seed / pending / update / stats / budget + POST /v1/internal/jobs/{job_id}/log 进度上报与生命周期(running/completed/failed) +""" + +from __future__ import annotations + +import asyncio +import json +import logging +import re +import time +from typing import Any, Dict, List, Optional, Union + +from fastapi import APIRouter, Header, HTTPException +from pydantic import BaseModel, ConfigDict, Field, field_validator + +from core.db.engine import SessionLocal +from core.infra.responses import success_response +from core.services.job_service import JobService + +logger = logging.getLogger(__name__) + +router = APIRouter(prefix="/v1/internal/jobs", tags=["internal-jobs"]) + +# 每个 job 的在途子作业闸门(进程内):脚本可能一次打出 concurrency 个并发回调, +# 这里按 job 预算收口,全局闸再防单会话吃光后端。 +_job_gates: Dict[str, asyncio.Semaphore] = {} +_GLOBAL_GATE = asyncio.Semaphore(24) + + +def _gate(job_id: str, concurrency: int) -> asyncio.Semaphore: + gate = _job_gates.get(job_id) + if gate is None: + gate = asyncio.Semaphore(max(1, min(int(concurrency or 8), 16))) + _job_gates[job_id] = gate + return gate + + +# ── 请求体 ──────────────────────────────────────────────────────────── + + +class AgentBody(BaseModel): + # 字段名叫 schema_ 是为了避开 BaseModel 的保留名,对外仍是 "schema" + model_config = ConfigDict(populate_by_name=True) + + prompt: str = "" + schema_: Optional[Dict[str, Any]] = Field(default=None, alias="schema") + tools: List[str] = [] + model: Optional[str] = None + max_attempts: int = 2 + # 必须收 int:业务主键十有八九是行号/序号,脚本自然会写 item_key=it["seq"]。 + # Pydantic v2 不做 int→str 强转,只声明 str 会让整轮作业被 422 挡在门外—— + # 事故里 568 项全军覆没、模型一次都没被调用,就是这个类型洁癖的代价。 + item_key: Optional[Union[str, int]] = None + + @field_validator("item_key") + @classmethod + def _key_to_str(cls, v: Optional[Union[str, int]]) -> Optional[str]: + return None if v is None or v == "" else str(v) + + +class LedgerBody(BaseModel): + op: str + items: Optional[List[Dict[str, Any]]] = None + # 同 AgentBody.item_key:主键常常是整数序号,别让类型把回写挡在门外 + key: Optional[Union[str, int]] = None + status: Optional[str] = None + limit: Optional[int] = None + result: Optional[Any] = None + review: Optional[Dict[str, Any]] = None + error: Optional[str] = None + bump_attempts: bool = False + + +class LogBody(BaseModel): + message: Optional[str] = None + lifecycle: Optional[str] = None + error: Optional[str] = None + + +# ── 鉴权 ────────────────────────────────────────────────────────────── + + +def _auth(job_id: str, token: Optional[str]): + """token 校验 → 返回 (user_id, chat_id, budget, allowed_tools, model_params)。 + + 终态 job 一律拒绝:沙箱会被后续会话复用,旧脚本不得借尸还魂。 + """ + if not token: + raise HTTPException(status_code=401, detail="缺少 job token") + with SessionLocal() as db: + job = JobService(db).verify_token(job_id, token) + if job is None: + raise HTTPException(status_code=403, detail="job token 无效或作业已结束") + start_params = dict((job.extra_data or {}).get("start_params") or {}) + return { + "user_id": job.user_id, + "chat_id": job.chat_id, + "budget": dict(job.budget or {}), + "allowed_tools": list(start_params.get("allowed_tools") or []), + "model_name": start_params.get("model_name"), + "model_provider_id": start_params.get("model_provider_id"), + } + + +# ── 结构化输出 ───────────────────────────────────────────────────────── + + +def _json_candidates(text: str): + if not text: + return + yield text.strip() + m = re.search(r"```(?:json)?\s*\n?(.*?)\n?```", text, re.DOTALL) + if m: + yield m.group(1).strip() + s, e = text.find("{"), text.rfind("}") + if s != -1 and e != -1 and e > s: + yield text[s : e + 1] + s, e = text.find("["), text.rfind("]") + if s != -1 and e != -1 and e > s: + yield text[s : e + 1] + + +def _parse_schema(text: str, schema: Dict[str, Any]) -> Optional[Any]: + """宽进严出:先尽力从自由文本里抠出 JSON,再按 schema 校验。""" + for cand in _json_candidates(text): + try: + obj = json.loads(cand) + except (json.JSONDecodeError, ValueError): + continue + try: + import jsonschema + + jsonschema.validate(obj, schema) + except ImportError: + required = list((schema or {}).get("required") or []) + if isinstance(obj, dict) and all(k in obj for k in required): + return obj + continue + except Exception: + continue + return obj + return None + + +def _schema_hint(schema: Dict[str, Any]) -> str: + return ( + "\n\n【输出格式(强制)】只输出一个 JSON 对象,不要任何解释文字、不要代码块以外的内容。" + "必须严格满足以下 JSON Schema:\n" + + json.dumps(schema, ensure_ascii=False)[:2000] + ) + + +# ── 轻量子作业执行 ────────────────────────────────────────────────────── + + +# 工具级基础设施故障的特征串。命中时这一项不能被当成业务结论("查无"), +# 必须作为可重试的失败抛回脚本——否则环境故障会被固化成数据。 +# (实测踩过:搜索配额打爆后 337/354 项被写成"未查询到公开展品信息"并标 done。) +_INFRA_FAILURE_MARKERS = ( + "用量限制", + "quota", + "rate limit", + "too many requests", + "调用失败", + "connection error", + "timeout", + "timed out", + "503", + "502", + "429", +) + + +def _looks_like_infra_failure(text: str) -> bool: + low = (text or "").lower() + return any(m in low for m in _INFRA_FAILURE_MARKERS) + + +async def _run_light_agent( + prompt: str, + *, + tools: List[str], + user_id: str, + model_name: Optional[str], + model_provider_id: Optional[str], +) -> tuple[str, bool]: + """无历史、无沙箱、无技能、无子智能体的一次性子作业。 + + 这正是「去掉共享上下文 / 流式回灌 / 历史累积」后的执行路径:每次调用的上下文只有 + 这一条 prompt,成本与工作项总数无关。 + """ + from core.llm.agent_factory import create_agent_executor + from core.llm.mcp_manager import close_clients + from orchestration.streaming import StreamingAgent + + agent, clients = await create_agent_executor( + enabled_skill_ids=[], + enabled_mcp_ids=list(tools or []), + enabled_kb_ids=[], + disable_tools=not tools, + chat_mode="fast", + isolated=True, + allow_bash=False, + read_only=True, + max_iters=6, + model_name=model_name, + model_provider_id=model_provider_id, + current_user_id=user_id, + top_level_chat=False, + ) + text = "" + tool_calls = 0 + tool_failures = 0 + infra_failure = False + try: + sa = StreamingAgent(agent, clients) + async for et, payload in sa.stream( + [{"role": "user", "content": prompt}], + {"user_id": user_id, "enable_thinking": False, "chat_mode": "fast"}, + ): + if et == "text_delta": + text += payload + elif et == "tool_result": + tool_calls += 1 + content = str((payload or {}).get("content") or "") + failed = str((payload or {}).get("status") or "") in ("error", "denied", "interrupted") + if failed or _looks_like_infra_failure(content): + tool_failures += 1 + if _looks_like_infra_failure(content): + infra_failure = True + elif et == "error": + logger.warning("[job-agent] sub-agent error: %s", payload) + infra_failure = True + break + finally: + await close_clients(clients) + + # 声明了工具却每一次都失败 → 这一轮没有任何证据,结论不可信 + if tools and tool_calls > 0 and tool_failures == tool_calls: + infra_failure = True + # 模型自己在正文里如实说了"工具调用失败/超限",同样不能当业务结论 + if _looks_like_infra_failure(text): + infra_failure = True + return text, infra_failure + + +# ── 端点 ────────────────────────────────────────────────────────────── + + +@router.post("/{job_id}/agent") +async def job_agent( + job_id: str, + body: AgentBody, + x_job_token: Optional[str] = Header(None, alias="X-Job-Token"), +): + ctx = _auth(job_id, x_job_token) + + with SessionLocal() as db: + left = JobService(db).budget_left(job_id) + if left["calls_left"] <= 0: + raise HTTPException(status_code=429, detail="作业子调用预算已用尽") + if left["seconds_left"] <= 0: + raise HTTPException(status_code=429, detail="作业墙钟预算已用尽") + + # 工具面 = 脚本声明 ∩ 发起会话的工具面。脚本不能提权到会话本身没有的能力。 + declared = [t for t in (body.tools or []) if isinstance(t, str)] + allowed = ctx["allowed_tools"] + tools = [t for t in declared if t in allowed] if allowed else [] + dropped = sorted(set(declared) - set(tools)) + if dropped: + logger.info("[job-agent] job=%s dropped out-of-scope tools=%s", job_id, dropped) + + prompt = body.prompt or "" + schema = body.schema_ if isinstance(body.schema_, dict) else None + if schema: + prompt = prompt + _schema_hint(schema) + + attempts = max(1, min(int(body.max_attempts or 2), 4)) + started = time.monotonic() + last_text = "" + result: Any = None + + # 调用即落账(服务端安全网):脚本自己的 ledger.update 才是权威,但脚本可能压根没写回 + # ——历史事故正是如此:568 项全程 pending、调用真在跑、成果全丢,外面看不出任何异常。 + # 所以只要带了 item_key,服务端先把该项标成 running 并累加 attempts;脚本随后的回写会 + # 覆盖它(顺序天然如此),进度条与中途唤醒因此永远有真实分母。 + if body.item_key: + try: + with SessionLocal() as db: + JobService(db).update_item( + job_id, body.item_key, status="running", bump_attempts=True + ) + except Exception: # noqa: BLE001 —— 落账失败绝不能挡住真正的作业 + logger.warning("[job-agent] job=%s pre-mark failed key=%s", job_id, body.item_key) + + gate = _gate(job_id, left.get("concurrency", 8)) + infra_failed = False + async with _GLOBAL_GATE, gate: + for i in range(attempts): + try: + last_text, infra_failed = await _run_light_agent( + prompt if i == 0 else prompt + "\n\n上一次输出无法解析为合法 JSON,请只输出 JSON。", + tools=tools, + user_id=ctx["user_id"], + model_name=body.model or ctx["model_name"], + model_provider_id=ctx["model_provider_id"], + ) + except Exception as exc: # noqa: BLE001 + logger.warning("[job-agent] job=%s attempt=%d failed: %s", job_id, i, exc) + last_text, infra_failed = "", True + continue + # 工具全挂/配额打爆:这一轮没有任何证据,**不能**把它当成业务结论回给脚本, + # 否则"搜不到"会被固化进数据。抛可重试错误,让该项留在台账里待续跑。 + if infra_failed: + logger.warning("[job-agent] job=%s attempt=%d infra failure", job_id, i) + continue + if not schema: + result = last_text + break + parsed = _parse_schema(last_text, schema) + if parsed is not None: + result = parsed + break + + duration_ms = int((time.monotonic() - started) * 1000) + ok = result is not None + # 归因必须分流:工具挂了和模型输出不合 schema 是两回事,共用一条文案会把排障线索抹平 + # (历史事故:17 次失败全记「结构化输出解析失败」,实际分不清是搜索不通还是模型不听话)。 + reason = ( + None + if ok + else ("工具不可用/配额/超时,本项未取得证据" if infra_failed else "结构化输出解析失败") + ) + with SessionLocal() as db: + svc = JobService(db) + svc.add_usage(job_id, calls=1) + svc.record_call( + job_id, + item_key=body.item_key, + prompt=prompt, + model=body.model or ctx["model_name"], + duration_ms=duration_ms, + status="success" if ok else "failed", + error=reason, + ) + # 结账:脚本的回写在这之后发生、会覆盖这里的判断——这只是脚本没回写时的兜底真相 + if body.item_key: + try: + if ok: + svc.update_item( + job_id, + body.item_key, + status="done", + result=result if isinstance(result, dict) else {"text": result}, + ) + else: + svc.update_item(job_id, body.item_key, status="failed", error=reason) + except Exception: # noqa: BLE001 + logger.warning("[job-agent] job=%s post-mark failed key=%s", job_id, body.item_key) + if not ok: + if infra_failed: + # 503 = 可重试:SDK 会退避重试,仍失败则该项记 failed 留在台账等续跑, + # 而不是被写成"查无"。环境故障绝不能变成业务结论。 + raise HTTPException( + status_code=503, + detail=f"工具不可用(配额/连接/超时),本项未取得证据:{(last_text or '')[:200]}", + ) + raise HTTPException( + status_code=422, + detail=f"子作业未产出合法结果({attempts} 次尝试):{(last_text or '')[:200]}", + ) + return success_response(data=result) + + +@router.post("/{job_id}/ledger") +async def job_ledger( + job_id: str, + body: LedgerBody, + x_job_token: Optional[str] = Header(None, alias="X-Job-Token"), +): + _auth(job_id, x_job_token) + op = (body.op or "").strip() + + with SessionLocal() as db: + svc = JobService(db) + if op == "seed": + return success_response(data=svc.seed(job_id, body.items or [])) + if op == "pending": + return success_response( + data=svc.pending(job_id, status=body.status or "pending", limit=body.limit) + ) + if op == "update": + if body.key in (None, ""): + raise HTTPException(status_code=400, detail="update 需要 key") + ok = svc.update_item( + job_id, + str(body.key), + status=body.status, + result=body.result, + review=body.review, + error=body.error, + bump_attempts=body.bump_attempts, + ) + # ok=False 表示台账里根本没有这个 key(多半是漏了 seed)。这个信号必须回给脚本: + # 事故中脚本直连 job.map 没建台账,568 次回写全部打空,外面看到的只有"进度 0"。 + return success_response(data={"ok": ok, "known_key": ok}) + if op == "stats": + stats = svc.stats(job_id) + # progressed:本次统计相对上次是否有推进,脚本据此判停滞(不必自己记账) + prev = int((svc.get(job_id).extra_data or {}).get("last_settled", -1)) + stats["progressed"] = stats["settled"] != prev + job = svc.get(job_id) + meta = dict(job.extra_data or {}) + meta["last_settled"] = stats["settled"] + job.extra_data = meta + from sqlalchemy.orm.attributes import flag_modified + + flag_modified(job, "extra_data") + db.commit() + return success_response(data=stats) + if op == "budget": + return success_response(data=svc.budget_left(job_id)) + + raise HTTPException(status_code=400, detail=f"未知 ledger op: {op}") + + +@router.post("/{job_id}/log") +async def job_log( + job_id: str, + body: LogBody, + x_job_token: Optional[str] = Header(None, alias="X-Job-Token"), +): + ctx = _auth(job_id, x_job_token) + + if body.lifecycle: + with SessionLocal() as db: + svc = JobService(db) + if body.lifecycle == "running": + svc.mark_running(job_id) + elif body.lifecycle in ("completed", "failed"): + svc.finish(job_id, body.lifecycle, error=body.error) + return success_response(data={"ok": True}) + + msg = (body.message or "").strip() + if msg: + logger.info("[job %s] %s", job_id, msg[:500]) + try: + from orchestration.job_runtime import _emit_progress + + _emit_progress(ctx["chat_id"], msg[:200]) + except Exception: # noqa: BLE001 + pass + return success_response(data={"ok": True}) diff --git a/src/backend/api/routes/v1/jobs.py b/src/backend/api/routes/v1/jobs.py new file mode 100644 index 00000000..613bf627 --- /dev/null +++ b/src/backend/api/routes/v1/jobs.py @@ -0,0 +1,88 @@ +"""批量作业的**用户可见**只读视图 —— 喂输入框上方那条作业状态条。 + +与 ``internal_jobs.py`` 划清界限:那套端点是给沙箱里的作业脚本用的(job token 鉴权, +可写台账、可派子作业);这里是给**人**看的(登录用户鉴权,只读、只给聚合数), +两者永远不共用鉴权,脚本拿到的 token 也永远换不到这里的数据。 + +为什么必须有它:工作流模式下作业跑在后台,主对话是安静的——不给一个零成本的进度视图, +用户根本无法判断"到底有没有在跑",只能干等或反复追问(而每次追问都是一轮真实推理)。 +状态条走这个接口轮询,代价是一次 SQL 聚合,和模型完全无关。 +""" + +from typing import List, Optional + +from core.auth.backend import UserContext, get_current_user +from core.db.engine import SessionLocal +from core.db.models import Job +from core.infra.logging import get_logger +from core.infra.responses import success_response +from core.services.job_service import JobService +from fastapi import APIRouter, Depends, HTTPException, Query + +logger = get_logger(__name__) + +router = APIRouter(prefix="/v1/jobs", tags=["Jobs"]) + +_LIVE_STATUSES = ("pending", "running") + + +def _view(svc: JobService, job: Job) -> dict: + """一条作业的对外形状 —— 只给聚合,逐项明细一律不出这个接口。 + + 明细走 ``run_job(action='export')`` 落沙箱文件:几百上千项读进浏览器(或对话) + 毫无意义,还会把上下文撑爆。 + """ + stats = svc.stats(job.job_id) + return { + "job_id": job.job_id, + "chat_id": job.chat_id, + "name": job.name or "", + "status": job.status, + "stats": stats, + "usage": dict(job.usage or {}), + "budget_left": svc.budget_left(job.job_id), + "error": job.error_message or "", + "created_at": job.created_at.isoformat() if job.created_at else None, + "started_at": job.started_at.isoformat() if job.started_at else None, + "completed_at": job.completed_at.isoformat() if job.completed_at else None, + } + + +@router.get("", summary="列出作业(按会话过滤,供状态条轮询)") +async def list_jobs( + chat_id: Optional[str] = Query(None, description="只看这个会话的作业"), + live: bool = Query(True, description="true 只返回未结束的作业(状态条默认口径)"), + limit: int = Query(20, ge=1, le=100), + user: UserContext = Depends(get_current_user), +): + """列出当前用户的作业。默认只给未结束的——状态条只关心"现在有没有在跑"。""" + with SessionLocal() as db: + svc = JobService(db) + q = db.query(Job).filter(Job.user_id == user.user_id) + if chat_id: + q = q.filter(Job.chat_id == chat_id) + if live: + q = q.filter(Job.status.in_(_LIVE_STATUSES)) + rows: List[Job] = q.order_by(Job.created_at.desc()).limit(limit).all() + return success_response(data={"jobs": [_view(svc, r) for r in rows]}) + + +@router.get("/{job_id}", summary="查单个作业进度") +async def get_job(job_id: str, user: UserContext = Depends(get_current_user)): + with SessionLocal() as db: + svc = JobService(db) + job = svc.get(job_id) + if job is None or job.user_id != user.user_id: + raise HTTPException(status_code=404, detail="job not found") + return success_response(data=_view(svc, job)) + + +@router.post("/{job_id}/cancel", summary="用户手动取消作业") +async def cancel_job(job_id: str, user: UserContext = Depends(get_current_user)): + """状态条上的取消按钮 —— 跑歪了的作业不该只能等它烧完预算。""" + from orchestration import job_runtime + + ok = await job_runtime.cancel_job(job_id, user_id=user.user_id) + if not ok: + raise HTTPException(status_code=404, detail="job not found or not cancellable") + return success_response(data={"job_id": job_id, "status": "cancelled"}) diff --git a/src/backend/api/schemas.py b/src/backend/api/schemas.py index 353a90e7..e405b0fa 100644 --- a/src/backend/api/schemas.py +++ b/src/backend/api/schemas.py @@ -107,6 +107,15 @@ class ChatRequest(BaseModel): "为 True 时强制启用 batch_runner MCP,并在系统提示中提示模型优先调用 batch_plan。" ), ) + workflow_chat: bool = Field( + default=False, + description=( + "是否为工作流模式对话(用户显式触发:输入框 / 斜杠命令、或 + 菜单里选「工作流模式」)。" + "为 True 时才注册 run_job 批量作业工具,并在系统提示中给出作业脚本写法。" + "与计划模式/批量执行同属用户触发的模式:不触发就完全不存在," + "避免普通问答被无关的批量规则干扰。" + ), + ) disable_batch_plan: bool = Field( default=False, description=( diff --git a/src/backend/core/db/models/__init__.py b/src/backend/core/db/models/__init__.py index 3ecfd748..472831b3 100644 --- a/src/backend/core/db/models/__init__.py +++ b/src/backend/core/db/models/__init__.py @@ -56,6 +56,7 @@ UserFolder, UserShadow, ) +from core.db.models.job import JOB_LIVE_STATUSES, JOB_TERMINAL_STATUSES, Job, JobCall, JobItem from core.db.models.knowledge import CatalogOverride, KBChunk, KBDocument, KBSpace from core.db.models.logs import SkillCallLog, SubAgentCallLog, ToolCallLog from core.db.models.memory import MemoryRefShadow, MemorySanitizerRule, ProfileMemory diff --git a/src/backend/core/db/models/job.py b/src/backend/core/db/models/job.py new file mode 100644 index 00000000..bf857969 --- /dev/null +++ b/src/backend/core/db/models/job.py @@ -0,0 +1,136 @@ +"""SQLAlchemy ORM models — 作业编排运行时(Job Runtime)。 + +一个 **job** 是「对 N 个同构工作项批量作业」的一次执行:主对话智能体写一段作业脚本, +脚本跑在沙箱里(普通 Python 进程,拥有文件/网络/并发),需要模型判断时通过带 job token +的回调请求后端派子智能体——模型凭据因此永远不进沙箱。 + +三张表的分工: + +- ``jobs`` 一次作业的元信息、预算、用量、脚本快照(供审计与 resume 原样续跑) +- ``job_items`` 工作项台账,**唯一真源**。按 (job_id, item_key) 复合主键幂等, + 断点续跑靠它:重跑脚本时 ``pending()`` 自动跳过已完成项。 +- ``job_calls`` 每次子作业调用的审计与成本归因 + +台账放 DB 而不是沙箱文件:沙箱池化复用会让新 job 看见旧 job 残留的账本文件 +(autonomous_loop 踩过这个坑,靠盖 loop_id 章解决),以 job_id 为主键从结构上避免。 +""" + +from datetime import datetime, timezone +from sqlalchemy import ( + Column, String, Integer, BigInteger, Text, TIMESTAMP, + ForeignKey, CheckConstraint, Index, JSON +) +from sqlalchemy.dialects.postgresql import JSONB +from sqlalchemy.orm import relationship +from core.db.engine import Base + +JSONType = JSON().with_variant(JSONB(), "postgresql") + + +def _utcnow() -> datetime: + """带时区的「现在」。 + + ⚠️ 这里**不能**用 `datetime.utcnow`:它给的是 naive 值,写进 `TIMESTAMP(timezone=True)` + 时由 PostgreSQL 按**会话时区**解释。生产/测试机容器都是 `TZ=Asia/Shanghai`,于是 + naive 的 UTC 时刻被当成 +08 存下来,落库瞬间就比真实时刻早 8 小时——状态条上一条刚 + 提交的作业因此显示「已运行 8 小时」(实测于 HugAgentOS 测试机)。带 tzinfo 的值不受 + 会话时区影响,写进去是哪个时刻读出来就是哪个时刻。 + """ + return datetime.now(timezone.utc) + + +# 终态:驱动不再推进,token 失效 +JOB_TERMINAL_STATUSES = ("completed", "failed", "cancelled") +# 活跃态:进程重启后需要对账(归位 interrupted 或按需续跑) +JOB_LIVE_STATUSES = ("pending", "running") + + +class Job(Base): + """一次批量作业。""" + __tablename__ = "jobs" + + job_id = Column(String(64), primary_key=True) + user_id = Column(String(64), ForeignKey("users_shadow.user_id", ondelete="CASCADE"), nullable=False) + chat_id = Column(String(64)) + name = Column(String(255), default="") + + status = Column(String(20), nullable=False, default="pending") + # 脚本正文随 job 快照,便于审计与「改一行再 resume」;script_path 是沙箱内路径 + script_path = Column(Text, default="") + script_text = Column(Text, default="") + sandbox_session_id = Column(String(128)) + + # budget: {max_calls, max_tokens, max_seconds, concurrency} + budget = Column(JSONType, default=dict) + # usage: {calls, tokens, seconds} + usage = Column(JSONType, default=dict) + # extra_data.start_params:发起时的运行时参数(模型/工具面/思考档位),resume 原样续跑不降级 + extra_data = Column("metadata", JSONType, default=dict) + + error_message = Column(Text) + created_at = Column(TIMESTAMP(timezone=True), default=_utcnow) + started_at = Column(TIMESTAMP(timezone=True)) + completed_at = Column(TIMESTAMP(timezone=True)) + updated_at = Column(TIMESTAMP(timezone=True), default=_utcnow, onupdate=_utcnow) + + items = relationship("JobItem", back_populates="job", cascade="all, delete-orphan") + + __table_args__ = ( + CheckConstraint( + "status IN ('pending','running','paused','completed','failed','cancelled','interrupted')", + name="jobs_status_check", + ), + Index("idx_jobs_user_id", "user_id"), + Index("idx_jobs_chat_status", "chat_id", "status"), + Index("idx_jobs_status", "status"), + ) + + +class JobItem(Base): + """工作项台账 —— 完成判据的唯一真源。""" + __tablename__ = "job_items" + + job_id = Column(String(64), ForeignKey("jobs.job_id", ondelete="CASCADE"), + primary_key=True, nullable=False) + item_key = Column(String(128), primary_key=True, nullable=False) + + status = Column(String(20), nullable=False, default="pending") + payload = Column(JSONType, default=dict) # seed 时给的输入 + result = Column(JSONType) # 子作业产出(schema 校验后) + review = Column(JSONType) # 验收裁决(可多轮,键为规格名) + attempts = Column(Integer, default=0) + error = Column(Text) + updated_at = Column(TIMESTAMP(timezone=True), default=_utcnow, onupdate=_utcnow) + + job = relationship("Job", back_populates="items") + + __table_args__ = ( + CheckConstraint( + "status IN ('pending','running','done','not_found','failed','needs_review')", + name="job_items_status_check", + ), + # pending() 的查询路径 + Index("idx_job_items_job_status", "job_id", "status"), + ) + + +class JobCall(Base): + """每次 ``agent()`` 回调的审计与成本归因。""" + __tablename__ = "job_calls" + + call_id = Column(String(64), primary_key=True) + job_id = Column(String(64), ForeignKey("jobs.job_id", ondelete="CASCADE"), nullable=False) + item_key = Column(String(128)) + seq = Column(Integer, default=0) + + prompt_hash = Column(String(64)) + model = Column(String(128)) + tokens = Column(JSONType, default=dict) + duration_ms = Column(BigInteger, default=0) + status = Column(String(20), default="running") + error = Column(Text) + created_at = Column(TIMESTAMP(timezone=True), default=_utcnow) + + __table_args__ = ( + Index("idx_job_calls_job_id", "job_id"), + ) diff --git a/src/backend/core/llm/agent_factory.py b/src/backend/core/llm/agent_factory.py index eb8884aa..2c7d841e 100644 --- a/src/backend/core/llm/agent_factory.py +++ b/src/backend/core/llm/agent_factory.py @@ -31,6 +31,7 @@ FinishPinGuardMiddleware, GoalAnchorReminderMiddleware, IterBudgetReminderMiddleware, + JobLedgerReminderMiddleware, OntologyGateMiddleware, StallInterventionMiddleware, SteerMiddleware, @@ -81,6 +82,61 @@ "若请求确实只针对单一对象/单一概念,再走普通回答即可。\n" ) +# 工作流模式提示段 —— 只在用户显式进入工作流模式时拼进系统提示(与 _BATCH_MODE_HINT 同源做法)。 +# 不进入这个模式时,下面这一整套批量规则**完全不存在**,普通问答不受任何干扰。 +_WORKFLOW_MODE_HINT = ( + "\n\n## 工作流模式(用户已主动进入)\n" + "用户显式进入了工作流模式,明确希望用**批量作业**的方式处理任务。你有 `run_job` 工具。\n" + "\n" + "### 什么时候必须用\n" + "当任务是「对 N 个**同构**工作项做同一件事」——补全表格某列、逐份审阅文档、逐个文件改代码、" + "逐条数据打标——且 N 较多(经验刻度 ~20 起,真正的判据是:对象彼此独立、处理逻辑同构、总量大)" + "时,**禁止在对话主循环里逐项处理**。主循环每一轮都要重发全部累积历史,成本随进度二次方增长," + "必然做不完。\n" + "\n" + "### 怎么做(三步)\n" + "1. 用 `write` 把作业脚本写进沙箱(例如 `/workspace/jobs/fill.py`)。脚本是普通 Python," + "开头 `from hugagent_job import ledger, agent, job, log`,SDK 由系统注入:\n" + " - `ledger.seed(items)` 建台账(每项给 key 与 payload 两个字段),按 key 幂等;\n" + " - `job.map(items, fn, concurrency=8)` 并发跑,**fn 返回 dict 会自动逐项落账**" + "(返回 None 表示你自己 update 过了;抛异常自动记 failed)。落账要取台账主键," + "默认按 `key`→`item_key`→`id`→`seq` 在 item 里找,都没有就用 `job.map(..., key=\"字段名\")` " + "显式指定——**别让 seed 用的 key 和 map 传的对象对不上**;\n" + " - `agent(prompt, schema=..., tools=[...], item_key=...)` 派子智能体做每项的模型判断;\n" + " - `ledger.pending()` / `ledger.stats()` / `job.budget()` / `log(...)`。\n" + " 其余一切用标准 Python:抓网页、解析、写 Excel、`subprocess` 跑校验命令。\n" + " **能机检的验收就别烧模型**——`mypy` / `pytest` / 一段校验函数都比 `agent()` 便宜得多。\n" + " ⚠️ `agent()` 在工具全挂/配额打爆/超时时会**抛异常**,不要 try/except 把它转写成" + "「未查询到」——那是把环境故障固化成数据。让异常抛出去,该项会记 failed 留在台账等续跑。\n" + " 同理:真的查无请用 `_status` 标 `not_found`,**不要**把占位串写进结果字段。\n" + "2. `run_job(action=\"start\", script_path=..., name=...)` 提交。\n" + "3. 作业结束后 `run_job(action=\"export\", job_id=...)` 把台账导成沙箱里的 JSONL," + "再用 bash/python 读它写产物。\n" + "\n" + "### 两条硬规矩\n" + "- **不要轮询**:`wait=True`(默认)会一直等到作业结束,中途不占推理轮次;`wait=False` 时" + "作业每隔一段时间会**主动叫醒你播报进度**(默认 15 分钟,`progress_wake_sec` 可调)," + "跑完还会再叫你一次做交付——所以你直接结束本轮回复即可,反复调 `status` 纯属浪费。" + "**小时级作业请用 `wait=False`**:阻塞一小时既看不到进度也没法中途干预。" + "用户那边有独立的作业状态条实时显示进度,不需要你复述作业还活着。\n" + "- **被进度唤醒时只汇报、不干活**:那一轮只需一两句话转述进度," + "**不要**重复提交作业(它还在跑)、不要 `export`、不要把逐项结果读进对话。" + "确实要换脚本重跑,用 `run_job(action=\"start\", on_conflict=\"replace\")`——" + "它会先停掉旧作业;默认的拦截只是不让你**意外**叠加两份,不是不让重跑。\n" + "- **用户说停就立刻停**:用户说「停止任务/别跑了/取消」时," + "马上 `run_job(action=\"cancel\", job_id=...)` 把作业停掉再回话," + "**不要**先解释、不要反问要不要保留进度——台账已经落库,随时可以 `resume` 续跑。" + "不知道 job_id 就先 `run_job(action=\"status\")` 查,别让作业在用户喊停后还在烧预算。\n" + "- **逐项结果不进对话**:要用结果就 `export` 成文件再脚本处理。\n" + "\n" + "### 交付纪律\n" + "台账里还剩多少是**可查证的事实**。不得以「边际收益递减」「消耗较大」为由在只完成一部分时" + "转向交付;确实未做完,必须报出分母、已完成数与未覆盖清单,并给出续跑方式" + "(`run_job(action=\"resume\", job_id=...)`,已完成的项不会重做)。\n" + "「查无 / 待定 / 失败」只能落在独立的状态字段,**不得**把占位串写进原始数据位——" + "一旦写入,「哪些还没做」就不再可判定。\n" +) + from orchestration.registry import AgentSpec load_dotenv() @@ -511,6 +567,10 @@ async def create_agent_executor( # 高于 main_agent 兜底;plan_mode=True 等价于 model_role="plan_agent"。 model_role: Optional[str] = None, batch_mode: bool = False, + # workflow_mode: 工作流模式(用户显式触发:斜杠命令 /workflow 或 + 菜单选「工作流模式」)。 + # 只有它为 True 才注册 run_job 并注入作业脚本写法——与计划模式/批量执行同属"用户触发的 + # 模式",不触发就完全不存在,普通问答不会被无关的批量规则干扰。 + workflow_mode: bool = False, # top_level_chat: whether this construction is a "top-level interactive main # conversation capable of hosting plan mode" — astream_chat_workflow passes # True explicitly after determining (has chat_id, not @@ -1424,6 +1484,27 @@ async def _connect_http(key: str, cfg: dict): # assistant message. See core/llm/workspace.py for the per-run state. register_pin_to_workspace(toolkit, scope=_proj_scope) + # ── Phase 3.85: run_job(工作流模式的作业编排面) ── + # **用户显式触发才注册**(workflow_mode):斜杠命令 /workflow 或 + 菜单选「工作流 + # 模式」。与计划模式/批量执行同属用户触发的模式——不触发就完全不存在,普通问答的 + # 工具面与提示词一点都不受影响。 + # 触发之后它才是这段对话的原生能力(不走 catalog 开关、关不掉):面对 N 个同构 + # 工作项时,主循环逐项处理会让每一轮重发全部历史(成本随进度二次方增长,做不完 + # 就自行收工)。run_job 把循环体交给沙箱脚本,模型调用由后端代持凭据派出—— + # 脚本因此拿不到任何 key。isolated=True 的子作业不注册,杜绝 job 套 job。 + if workflow_mode and not isolated and _sbx_sess: + from core.llm.tools.job_tool import register_run_job + + register_run_job( + toolkit, + user_id=current_user_id or "", + chat_id=chat_id, + sandbox_session_id=_sbx_sess, + allowed_tools=sorted(enabled_mcp_keys or []), + model_name=model_name, + model_provider_id=model_provider_id, + ) + # ── Phase 3.9: get_data_context (the "data dictionary" tool for direct-DB data retrieval) ── # Three gates combined: (1) a direct DB server is enabled this run # (db_query / es_query); (2) the external NL2SQL black box is excluded @@ -1690,6 +1771,11 @@ def _build_toolkit() -> Toolkit: system_prompt += _BATCH_MODE_HINT _log.info("[factory] +%s batch mode hint injected", _elapsed()) + # ── Inject workflow-mode hint (user explicitly entered workflow mode) ── + if workflow_mode: + system_prompt += _WORKFLOW_MODE_HINT + _log.info("[factory] +%s workflow mode hint injected", _elapsed()) + # ── Register call_subagent tool for main agent ── if visible_subagents: from core.llm.builtin_subagents import refresh_builtin_subagents @@ -2020,9 +2106,18 @@ def _build_toolkit() -> Toolkit: # the overestimate, but in practice it triggered too early (compression # kicked in while real occupancy was far below the threshold, and # tool-heavy sessions compressed repeatedly), so it has been relaxed. + from core.config.settings import _env as _cfg_env + from core.config.settings import _int as _cfg_int + context_config = ContextConfig( trigger_ratio=_in_turn_ratio, - tool_result_limit=int(tool_result_limit) if tool_result_limit else 20_000, + # 单条工具结果进上下文的上限(超出部分 offloader 落盘到 /workspace/.offload, + # 模型按需读回)。保持 20k 不再收紧:批量场景已由 run_job 接走(逐项结果根本 + # 不进主上下文),主对话这边继续保留完整的单条可读性更划算。需要时用 + # CHAT_TOOL_RESULT_LIMIT 按部署调。 + tool_result_limit=int(tool_result_limit) + if tool_result_limit + else _cfg_int(_cfg_env("CHAT_TOOL_RESULT_LIMIT"), 20_000), compression_prompt=( "你一直在处理上述任务但尚未完成,对话历史即将被本摘要替换。" "请生成一份【可恢复 ReAct 工作流】的结构化续写摘要,使你能在新的上下文窗口" @@ -2200,6 +2295,14 @@ def _build_toolkit() -> Toolkit: CitationAnchorMiddleware(), # on_acting: 证据锚点——工具结果回给模型前发号回注 cite_id ActingToolCallIdMiddleware(), # on_acting: expose call_subagent's tool_call.id to tools (parent-child linkage) ] + # on_reasoning: 会话里有未收敛的批量作业时,每轮把台账数字回灌进上下文。 + # 进度是外部事实(job_items 表),不是模型的记忆——不主动回灌,隔十几轮之后就会 + # 退化成"边际收益递减,先交付吧"(568 行只补 66 行正是这么停的)。 + # 子作业内部不挂(isolated),避免嵌套噪声。 + if workflow_mode and not isolated and chat_id: + _middlewares.append( + JobLedgerReminderMiddleware(chat_id=chat_id, user_id=current_user_id) + ) if not batch_mode: _middlewares.append(GoalAnchorReminderMiddleware(chat_id=chat_id, batch_mode=False)) _middlewares.append(FinishPinGuardMiddleware(batch_mode=batch_mode)) diff --git a/src/backend/core/llm/middlewares.py b/src/backend/core/llm/middlewares.py index 9057db62..5669bace 100644 --- a/src/backend/core/llm/middlewares.py +++ b/src/backend/core/llm/middlewares.py @@ -884,6 +884,88 @@ def _maybe_remind(self, agent: Agent, max_iters: int, cur_iter: int, remaining: ) +# ── JobLedgerReminder ────────────────────────────────────────────────────── +class JobLedgerReminderMiddleware(MiddlewareBase): + """本会话存在未收敛的批量作业时,每轮把台账状态回灌进上下文。 + + 为什么要 harness 主动回灌,而不是指望模型记着:进度是**外部事实**,写在 + ``job_items`` 表里,不在模型的记忆里。模型隔了十几轮之后对"还剩多少没做"的印象 + 只会越来越糊,最后就演变成"边际收益递减,先交付吧"——上一轮 568 行只补 66 行 + 就是这么停的。 + + 每轮注入一条 ````,内容是可查证的数字(总计/已完成/待办/失败) + 和明确的下一步。同一轮只注入一次;台账已收敛(待办与失败都为 0)就不再打扰。 + """ + + def __init__(self, *, chat_id: Optional[str], user_id: Optional[str]) -> None: + self._chat_id = chat_id or "" + self._user_id = user_id or "" + self._last_key: tuple | None = None + + async def on_reasoning(self, agent: Agent, input_kwargs: dict, next_handler): + try: + self._maybe_remind(agent) + except Exception as exc: # noqa: BLE001 —— 提醒失败绝不该拖垮主链路 + logger.warning("[job_ledger] reminder failed: %s", exc) + async for evt in next_handler(**input_kwargs): + yield evt + + def _maybe_remind(self, agent: Agent) -> None: + if not self._chat_id: + return + key = (getattr(agent.state, "reply_id", "") or "", int(getattr(agent.state, "cur_iter", 0) or 0)) + if key == self._last_key: + return + + from core.db.engine import SessionLocal + from core.db.models import Job + from core.services.job_service import JobService + + with SessionLocal() as db: + jobs = ( + db.query(Job) + .filter(Job.chat_id == self._chat_id, Job.user_id == self._user_id) + .order_by(Job.created_at.desc()) + .limit(3) + .all() + ) + if not jobs: + return + svc = JobService(db) + lines = [] + for job in jobs: + stats = svc.stats(job.job_id) + unsettled = int(stats.get("pending", 0)) + int(stats.get("failed", 0)) + if unsettled <= 0 and job.status in ("completed", "cancelled"): + continue + lines.append( + f"- 作业「{job.name or job.job_id}」({job.job_id},状态 {job.status}):" + f"总计 {stats.get('total', 0)},已完成 {stats.get('done', 0)}," + f"查无 {stats.get('not_found', 0)},待办 {stats.get('pending', 0)}," + f"失败 {stats.get('failed', 0)}" + ) + if not lines: + return + + self._last_key = key + reminder = ( + "本会话有尚未结算完的批量作业(数字取自台账,是可查证的事实,不是估计):\n" + + "\n".join(lines) + + "\n\n还有待办或失败项时:用 run_job(action='resume', job_id=...) 续跑" + "(已完成的项不会重做),或先查 run_job(action='status', job_id=...) 看明细。" + "**不得**把未完成当作完成来交付;确实要收尾,也必须如实报出分母、完成数与未覆盖清单。" + ) + agent.state.context.append( + Msg( + name="user", + role="user", + content=[ + TextBlock(type="text", text=f"\n{reminder}\n") + ], + ) + ) + + # ── StallIntervention ────────────────────────────────────────────────────── class StallInterventionMiddleware(MiddlewareBase): """Apply the active profile's intervention rules to the **ReAct loop**. diff --git a/src/backend/core/llm/tools/job_tool.py b/src/backend/core/llm/tools/job_tool.py new file mode 100644 index 00000000..92376825 --- /dev/null +++ b/src/backend/core/llm/tools/job_tool.py @@ -0,0 +1,424 @@ +"""run_job 工具 —— 主对话智能体提交批量作业的唯一入口。 + +定位:这不是「又一个固定形状的批处理工具」,而是一个**可编程的编排面**。智能体先用 +``write`` 把一段作业脚本写进沙箱,再用 ``run_job`` 提交;脚本里的控制流(循环、条件、 +分层取数、动态收敛、嵌套 map)都是普通 Python,平台不需要知道任务在做什么。 + +与 bash / read / write / glob / grep 同级原生注册,不走 catalog 开关——「对话内默认 +可用」就落在这里。 +""" + +from __future__ import annotations + +import json as _json +import logging +from typing import Any, Dict, List, Optional + +from agentscope.message import TextBlock +from agentscope.tool import Toolkit +from agentscope.tool._response import ToolChunk as ToolResponse + +logger = logging.getLogger(__name__) + + +def _resp(payload: Dict[str, Any]) -> ToolResponse: + return ToolResponse( + content=[TextBlock(type="text", text=_json.dumps(payload, ensure_ascii=False))] + ) + + +def register_run_job( + toolkit: Toolkit, + *, + user_id: str, + chat_id: Optional[str], + sandbox_session_id: Optional[str], + allowed_tools: Optional[List[str]] = None, + model_name: Optional[str] = None, + model_provider_id: Optional[str] = None, +) -> None: + """注册 run_job。 + + ``allowed_tools`` 是本次会话解析出来的可用 MCP 工具面;作业脚本声明的工具会与它取 + 交集,脚本不能提权到会话本身没有的能力。 + """ + + async def run_job( + action: str = "start", + script_path: str = "", + name: str = "", + job_id: str = "", + wait: bool = False, + # 默认必须是**带三方库**的那个解释器。沙箱里裸 python3 是干净的系统解释器, + # 连 openpyxl/pandas 都没有——默认值写成 python3 的直接后果是作业脚本第一行 + # import 就 ModuleNotFoundError 当场失败(实测踩过)。$PY_BIN 指向预装全套数据 + # 依赖的解释器,取不到时才退回 python3。 + interpreter: str = "${PY_BIN:-python3}", + max_calls: int = 0, + max_seconds: int = 0, + concurrency: int = 8, + dest_path: str = "", + status: str = "", + progress_wake_sec: int = 300, + on_conflict: str = "block", + ) -> ToolResponse: + """提交并管理一次**批量作业**:对 N 个同构工作项做同一件事。 + + 什么时候必须用它:当工作项 ≥ 20 且彼此同构(补全表格某列、逐份审阅文档、 + 逐个文件改代码、逐条数据打标),**禁止**在对话主循环里逐项处理——那样每一轮都要 + 重发全部历史,成本随进度二次方增长,做不完。 + + 怎么用(三步): + + 1. 用 ``write`` 把作业脚本写到沙箱,例如 ``/workspace/jobs/fill.py``。 + 脚本里 ``from hugagent_job import ledger, agent, job, log`` 即可,SDK 由系统注入: + + - ``ledger.seed([{"key": "r2", "payload": {...}}, ...])`` 建台账(按 key 幂等) + - ``ledger.pending()`` 取待办;``ledger.update(key, status="done", result=...)`` 回写 + - ``ledger.stats()`` → ``{total, done, pending, settled, remaining, progressed}`` + - ``agent(prompt, schema=..., tools=["internet_search"], item_key=...)`` 派子智能体, + 返回按 schema 校验后的对象;**每项一次判断**用它,多轮工具循环也用它 + - ``job.map(items, fn, concurrency=8)`` 并发跑,单项异常自动隔离并写回 error + - ``job.budget()`` → ``{calls_left, tokens_left, seconds_left}``;``log("...")`` 报进度 + + 其余一切用标准 Python:抓网页、解析、写 Excel、``subprocess`` 跑校验命令。 + **验收能机检就别烧模型**——``mypy`` / ``pytest`` / 一段校验函数都比 ``agent()`` 便宜。 + + 2. ``run_job(action="start", script_path="/workspace/jobs/fill.py", name="补全展品")``。 + 3. 作业结束后用 ``action="export"`` 把台账导成沙箱里的 JSONL,再用 bash/python + 读它写产物(Excel、报告、校验)。**不要**把逐项结果读回对话。 + + 两条硬规矩: + + - **提交完就回话,不要轮询**。默认 ``wait=False``:作业在后台跑,工具立刻返回 + job_id。此时**先把当前这轮回复收掉**——告诉用户作业已在后台开始、进度看输入框 + 上方的状态条、随时可以继续聊别的。作业跑完(以及每隔一段时间)系统会**自动 + 唤醒本会话**让你播报,不需要你守着。反复调 ``action="status"`` 干等纯属浪费轮次。 + 只有确信几十秒内能跑完的小作业才值得 ``wait=True`` 原地等——那会把整个会话 + 阻塞住:用户看不到中间进度、插不上话、也没法中途改主意。 + - **逐项结果不进对话**。要用结果就 ``export`` 成文件再脚本处理。 + - **用户喊停就立刻停**。用户说「停止任务 / 别跑了 / 取消」时,先 + ``action="cancel"`` 停掉再回话,不要先解释也不要反问——台账已落库, + 之后 ``action="resume"`` 就能接着跑,停一下不损失任何已完成的工作。 + + Args: + action (`str`): ``start`` 提交 / ``status`` 查进度 / ``export`` 导出台账 / + ``resume`` 断点续跑 / ``cancel`` 取消。 + script_path (`str`): 作业脚本在沙箱里的绝对路径(action=start 必填)。 + name (`str`): 作业名,便于在进度里辨认。 + job_id (`str`): status / resume / cancel 必填。 + wait (`bool`): **默认 false = 后台跑**:立即返回 job_id,作业跑完 / 每隔一段 + 时间自动叫醒本会话播报,用户全程能看状态条、能插话、能取消。 + true 则原地阻塞到作业结束——只适合几十秒的小作业,长作业阻塞会让会话 + 看起来「卡死在前台」,既看不到进度也没法中途干预。 + progress_wake_sec (`int`): 仅 wait=false:每隔多少秒把你叫回来播报一次进度 + (默认 300 秒=5 分钟;0 表示只在终态叫一次)。被叫醒时只需转述进度, + 别重复提交作业。用户看到的实时进度条不靠它,它只决定你何时该介入。 + on_conflict (`str`): 仅 start,本会话已有在跑作业时怎么办。``block``(默认) + 拦下并把三条出路告诉你;``replace`` 先停掉旧作业再提交新的(换了脚本要重跑 + 就用它);``parallel`` 两份并行(确实互不相干时才用,预算是双份的)。 + interpreter (`str`): 跑脚本的解释器。默认 ``${PY_BIN:-python3}`` —— 沙箱里 + **裸 python3 是干净的系统解释器,连 openpyxl/pandas 都没有**,而 ``$PY_BIN`` + 指向预装全套数据依赖的那个。除非有特别理由,**别覆盖这个默认值**;真要装 + 额外的包再用 ``uv run --with python``(沙箱可出网)。 + max_calls (`int`): 子作业调用次数上限,0 = 用默认。 + max_seconds (`int`): 墙钟秒数上限,0 = 用默认。 + concurrency (`int`): 子作业并发,默认 8,上限 16。 + dest_path (`str`): 仅 export:导出文件路径,默认 + ``/workspace/jobs/_ledger.jsonl``。 + status (`str`): 仅 export:只导出这些状态,逗号分隔(如 ``"done,not_found"``); + 留空导出全部。 + + Returns: + JSON。start/resume 返回 ``{ok, job_id, status, stats}``;status 返回 + ``{ok, status, stats, usage, budget_left}``。``stats.remaining>0`` 表示还没做完, + 必须如实告诉用户并给出续跑方式,不得当作完成。 + """ + from core.db.engine import SessionLocal + from core.db.models import Job + from core.services.job_service import JobService + from orchestration import job_runtime + + act = (action or "start").strip().lower() + + if act == "start": + if not script_path: + return _resp({"ok": False, "error": "script_path 必填:先用 write 把作业脚本写进沙箱"}) + if not sandbox_session_id: + return _resp({"ok": False, "error": "当前会话没有可用沙箱,无法提交作业"}) + + # 同一会话不许叠加在跑的作业。被进度唤醒后"改个脚本再交一份"是很自然的动作, + # 但旧作业不会自己消失:两份并存会同时烧预算、同时叫醒会话,用户在状态条上 + # 也分不清哪份才算数。要么先 cancel,要么 resume(已完成的项不会重做)。 + # 本会话已有在跑的作业时**默认**拦一道:默默叠加两份会双倍烧预算、双份叫醒会话, + # 用户在状态条上也分不清哪份算数。但这只是默认,不是禁令——确实要换新版本时 + # 用 on_conflict 明说:'replace' 停掉旧的再跑新的,'parallel' 两份并行。 + # 判断权留给调用方,工具只负责让"叠加"变成一个显式选择而不是意外。 + conflict = (on_conflict or "block").strip().lower() + if chat_id and conflict != "parallel": + # 字段必须在 session 内取出:出了 with 块 ORM 实例就 detached,再读属性会炸 + with SessionLocal() as db: + live = ( + db.query(Job) + .filter( + Job.chat_id == chat_id, + Job.status.in_(("pending", "running")), + ) + .order_by(Job.created_at.desc()) + .first() + ) + live_id = live.job_id if live is not None else "" + live_name = (live.name or "未命名") if live is not None else "" + if live_id and conflict == "replace": + await job_runtime.cancel_job(live_id, user_id=user_id) + logger.info("[run_job] replaced live job %s in chat %s", live_id, chat_id) + elif live_id: + return _resp( + { + "ok": False, + "error": ( + f"本会话已有在跑的作业 {live_id}({live_name})。三条路选一条:" + "① 想用新脚本取代它 → 再调一次本工具并带 on_conflict='replace'" + "(自动停掉旧作业再提交新的);" + f"② 旧作业只是中断、脚本没问题 → run_job(action='resume', job_id='{live_id}')" + "断点续跑,已完成的项不会重做;" + "③ 确实需要两份同时跑 → on_conflict='parallel'。" + "默认拦下只是为了让叠加成为显式选择,不是不让跑。" + ), + "job_id": live_id, + "hint": "on_conflict=replace|parallel", + } + ) + + # 读脚本正文:既做存在性校验,也作为 job 的审计快照与 resume 依据。 + # ⚠️ 必须走 base64,不能直接 `cat`:沙箱 execute 回传的 stdout 会丢换行, + # 脚本会被压成一行落地 → SyntaxError(实测踩过)。base64 -w0 保字节不变。 + try: + code, out, err = await job_runtime._sbx_bash( + f"base64 -w0 {script_path} 2>/dev/null || base64 -i {script_path}", + session_id=sandbox_session_id, + user_id=user_id, + timeout=30, + ) + except Exception as exc: # noqa: BLE001 + return _resp({"ok": False, "error": f"读取脚本失败: {exc}"}) + if code != 0 or not (out or "").strip(): + return _resp({"ok": False, "error": f"脚本不存在或为空: {script_path} {(err or '')[:200]}"}) + import base64 as _b64dec + + try: + out = _b64dec.b64decode("".join((out or "").split())).decode("utf-8") + except Exception as exc: # noqa: BLE001 + return _resp({"ok": False, "error": f"脚本解码失败: {exc}"}) + if not out.strip(): + return _resp({"ok": False, "error": f"脚本为空: {script_path}"}) + + budget: Dict[str, Any] = {"concurrency": concurrency} + if max_calls: + budget["max_calls"] = max_calls + if max_seconds: + budget["max_seconds"] = max_seconds + + try: + jid = await job_runtime.start_job( + user_id=user_id, + chat_id=chat_id, + name=name or "批量作业", + script_path=script_path, + script_text=out, + session_id=sandbox_session_id, + budget=budget, + start_params={ + "allowed_tools": list(allowed_tools or []), + "model_name": model_name, + "model_provider_id": model_provider_id, + # wait=False 时作业跑完没人叫醒本会话 → 打标,让驱动在终态入队一轮续跑 + "wake_on_finish": not wait, + # 中途进度播报间隔(秒);0 = 只在终态叫一次 + "progress_wake_sec": max(0, int(progress_wake_sec)), + }, + interpreter=interpreter or "${PY_BIN:-python3}", + ) + except Exception as exc: # noqa: BLE001 + logger.warning("[run_job] start failed: %s", exc) + return _resp({"ok": False, "error": f"作业启动失败: {exc}"}) + + if not wait: + job_runtime.spawn_background(jid, chat_id=chat_id) + return _resp( + { + "ok": True, + "job_id": jid, + "status": "started", + "waited": False, + "next": ( + "作业已在后台开始。**现在就结束这轮回复**:告诉用户作业已提交、" + "输入框上方的状态条会实时显示进度(几分之几、失败数、已运行多久)," + "期间可以继续聊别的、也可以点状态条上的按钮取消。" + "作业跑完和中途都会自动叫醒你播报,不要在这里轮询 action='status' 干等。" + ), + } + ) + + res = await job_runtime.run_and_wait(jid, chat_id=chat_id) + with SessionLocal() as db: + svc = JobService(db) + stats = svc.stats(jid) + job = svc.get(jid) + usage = dict(job.usage or {}) if job else {} + payload = { + "ok": res.get("status") == "completed", + "job_id": jid, + "status": res.get("status"), + "stats": stats, + "usage": usage, + } + if res.get("error"): + payload["error"] = res["error"] + if stats.get("remaining", 0) > 0: + payload["note"] = ( + f"仍有 {stats['remaining']} 项未完成——必须如实告知用户并给出未覆盖清单与" + f"续跑方式(run_job action=resume job_id={jid}),不得当作已完成。" + ) + # 脚本崩溃时把 runner 日志尾巴带回来,省一轮排查 + if res.get("status") == "failed" and sandbox_session_id: + try: + payload["runner_log"] = ( + await job_runtime.read_runner_log( + jid, user_id=user_id, session_id=sandbox_session_id + ) + )[-1500:] + except Exception: # noqa: BLE001 + pass + return _resp(payload) + + if not job_id: + return _resp({"ok": False, "error": f"action={act} 需要 job_id"}) + + # job_id 容错 + 找不到时把本会话的作业列出来。 + # 实测踩过:模型凭记忆抄 id 时漏了 "job_" 前缀,只回一句「job 不存在」它无从自救, + # 结果决定把 568 次子调用整个重跑一遍。错误信息必须自带下一步。 + with SessionLocal() as db: + svc = JobService(db) + resolved = svc.get(job_id) + if (resolved is None or resolved.user_id != user_id) and not job_id.startswith("job_"): + cand = svc.get(f"job_{job_id}") + if cand is not None and cand.user_id == user_id: + resolved, job_id = cand, f"job_{job_id}" + if resolved is None or resolved.user_id != user_id: + from core.db.models import Job + + rows = ( + db.query(Job) + .filter(Job.user_id == user_id, Job.chat_id == chat_id) + .order_by(Job.created_at.desc()) + .limit(5) + .all() + ) + return _resp( + { + "ok": False, + "error": f"job 不存在或无权访问: {job_id}", + "jobs_in_this_chat": [ + { + "job_id": r.job_id, + "name": r.name, + "status": r.status, + "stats": svc.stats(r.job_id), + } + for r in rows + ], + "hint": "用上面列表里的完整 job_id 重试(注意 job_ 前缀);" + "已经跑完的作业**不要重跑**,直接 export 取结果。", + } + ) + + if act == "status": + with SessionLocal() as db: + svc = JobService(db) + job = svc.get(job_id) + if job is None or job.user_id != user_id: + return _resp({"ok": False, "error": "job 不存在或无权访问"}) + return _resp( + { + "ok": True, + "job_id": job_id, + "status": job.status, + "stats": svc.stats(job_id), + "usage": dict(job.usage or {}), + "budget_left": svc.budget_left(job_id), + "error": job.error_message, + } + ) + + if act == "export": + # 台账在 DB,逐项结果**不进对话上下文**——所以导出的方式是写成沙箱里的 + # JSONL 文件,之后用 bash/python 处理(写 Excel、跑校验、喂评审)。 + if not sandbox_session_id: + return _resp({"ok": False, "error": "当前会话没有可用沙箱"}) + with SessionLocal() as db: + svc = JobService(db) + job = svc.get(job_id) + if job is None or job.user_id != user_id: + return _resp({"ok": False, "error": "job 不存在或无权访问"}) + wanted = [s.strip() for s in (status or "").split(",") if s.strip()] or [ + "done", + "not_found", + "failed", + "needs_review", + "pending", + ] + rows = [] + for st in wanted: + rows.extend(svc.pending(job_id, status=st)) + stats = svc.stats(job_id) + + dest = dest_path or f"/workspace/jobs/{job_id}_ledger.jsonl" + body = "\n".join(_json.dumps(r, ensure_ascii=False) for r in rows) + # 分块写 + 读回校验:单条 bash 携带大 base64 到约 170KB 会**静默失败** + # (exit=0、stderr 空、文件不存在)。568 行台账正好落在这个区间——历史上 + # 因此出现过「导出报成功 rows=568,文件却从来没出现」。成功与否只认沙箱 + # 里读回的真实字节数,绝不用 Python 侧的行数报平安。 + try: + ok_write, detail = await job_runtime.write_sandbox_file( + dest, body, session_id=sandbox_session_id, user_id=user_id + ) + except Exception as exc: # noqa: BLE001 + return _resp({"ok": False, "error": f"导出失败: {exc}"}) + if not ok_write: + return _resp({"ok": False, "error": f"导出失败(落盘未通过校验): {detail}"}) + return _resp( + { + "ok": True, + "job_id": job_id, + "path": dest, + "rows": len(rows), + "bytes": detail, + "stats": stats, + "hint": "每行一个 JSON:{key, status, payload, result, review, attempts}。" + "用 bash/python 读这个文件写产物,不要把逐项内容读回对话。", + } + ) + + if act == "cancel": + ok = await job_runtime.cancel_job(job_id, user_id=user_id) + return _resp({"ok": ok, "job_id": job_id, "status": "cancelled" if ok else "unknown"}) + + if act == "resume": + res = await job_runtime.resume_job(job_id, user_id=user_id, chat_id=chat_id) + if not res.get("ok"): + return _resp({"ok": False, "error": res.get("error")}) + if not wait: + job_runtime.spawn_background(job_id, chat_id=chat_id) + return _resp({"ok": True, "job_id": job_id, "status": "running", "waited": False}) + done = await job_runtime.run_and_wait(job_id, chat_id=chat_id) + with SessionLocal() as db: + stats = JobService(db).stats(job_id) + return _resp( + {"ok": done.get("status") == "completed", "job_id": job_id, + "status": done.get("status"), "stats": stats} + ) + + return _resp({"ok": False, "error": f"未知 action: {act}"}) + + toolkit.register_tool_function(run_job) diff --git a/src/backend/core/services/job_service.py b/src/backend/core/services/job_service.py new file mode 100644 index 00000000..a3a2ffbc --- /dev/null +++ b/src/backend/core/services/job_service.py @@ -0,0 +1,318 @@ +"""作业编排运行时(Job Runtime)的持久化服务层。 + +台账(``job_items``)是完成判据的唯一真源:驱动侧读它决定收敛,脚本侧读它做断点续跑。 +所有写入按 ``(job_id, item_key)`` 幂等——脚本重跑同一份 seed 不会重复建项、也不会把 +已完成项打回 pending。 +""" + +from __future__ import annotations + +import hashlib +import hmac +import logging +import secrets +import uuid +from datetime import datetime, timezone +from typing import Any, Dict, List, Optional + +from sqlalchemy import func +from sqlalchemy.orm import Session +from sqlalchemy.orm.attributes import flag_modified + +from core.db.models import Job, JobCall, JobItem + +logger = logging.getLogger(__name__) + +# 台账状态:done/not_found 视为"这一项不必再做",其余都还欠着 +SETTLED_STATUSES = ("done", "not_found") + +DEFAULT_BUDGET: Dict[str, int] = { + "max_calls": 5000, + "max_tokens": 20_000_000, + "max_seconds": 7200, + "concurrency": 8, +} + + +def _utcnow() -> datetime: + return datetime.now(timezone.utc) + + +def new_job_id() -> str: + return f"job_{uuid.uuid4().hex[:16]}" + + +def mint_token() -> str: + """一次性 job token:随 job 建立、随 job 终态失效。 + + 只用于回调三端点(agent / ledger / log),换不到模型凭据、碰不到其它 job。 + """ + return secrets.token_urlsafe(32) + + +def _same(a: str, b: str) -> bool: + return hmac.compare_digest(str(a or ""), str(b or "")) + + +class JobService: + def __init__(self, db: Session) -> None: + self.db = db + + # ── 生命周期 ────────────────────────────────────────────────────── + + def create( + self, + *, + user_id: str, + chat_id: Optional[str], + name: str, + script_path: str, + script_text: str, + sandbox_session_id: Optional[str], + budget: Optional[Dict[str, Any]] = None, + start_params: Optional[Dict[str, Any]] = None, + ) -> Job: + merged = dict(DEFAULT_BUDGET) + for k, v in (budget or {}).items(): + if isinstance(v, (int, float)) and v > 0: + merged[k] = int(v) + # 并发上限同时受全局护栏约束,防止单会话吃光全后端的子作业槽位 + merged["concurrency"] = max(1, min(int(merged["concurrency"]), 16)) + + job = Job( + job_id=new_job_id(), + user_id=user_id, + chat_id=chat_id or None, + name=(name or "")[:255], + status="pending", + script_path=script_path, + script_text=script_text or "", + sandbox_session_id=sandbox_session_id, + budget=merged, + usage={"calls": 0, "tokens": 0, "seconds": 0}, + extra_data={"token": mint_token(), "start_params": start_params or {}}, + ) + self.db.add(job) + self.db.commit() + self.db.refresh(job) + return job + + def get(self, job_id: str) -> Optional[Job]: + return self.db.query(Job).filter(Job.job_id == job_id).first() + + def verify_token(self, job_id: str, token: str) -> Optional[Job]: + """回调鉴权:token 匹配且 job 未进终态才放行。""" + job = self.get(job_id) + if job is None: + return None + if job.status in ("completed", "failed", "cancelled"): + return None + stored = str((job.extra_data or {}).get("token") or "") + if not stored or not _same(stored, token): + return None + return job + + def mark_running(self, job_id: str) -> None: + job = self.get(job_id) + if job is None or job.status not in ("pending", "interrupted", "paused"): + return + job.status = "running" + job.started_at = job.started_at or _utcnow() + self.db.commit() + + def finish(self, job_id: str, status: str, *, error: Optional[str] = None) -> None: + job = self.get(job_id) + if job is None or job.status in ("completed", "failed", "cancelled"): + return + job.status = status + job.error_message = (error or "")[:4000] or None + job.completed_at = _utcnow() + # 终态即销毁 token:沙箱可能被别的会话复用,旧脚本不得再回调 + meta = dict(job.extra_data or {}) + meta.pop("token", None) + job.extra_data = meta + flag_modified(job, "extra_data") + self.db.commit() + + def rotate_token(self, job_id: str) -> Optional[str]: + """resume 时换发新 token(旧 token 已在 finish 时销毁)。""" + job = self.get(job_id) + if job is None: + return None + token = mint_token() + meta = dict(job.extra_data or {}) + meta["token"] = token + job.extra_data = meta + flag_modified(job, "extra_data") + self.db.commit() + return token + + # ── 台账 ───────────────────────────────────────────────────────── + + def seed(self, job_id: str, items: List[Dict[str, Any]]) -> Dict[str, int]: + """幂等建项:已存在的 item_key 一律跳过(不覆盖已有状态与结果)。""" + if not items: + return {"created": 0, "skipped": 0} + existing = { + row.item_key + for row in self.db.query(JobItem.item_key).filter(JobItem.job_id == job_id).all() + } + created = 0 + for it in items: + key = str(it.get("key") or "").strip()[:128] + if not key or key in existing: + continue + self.db.add( + JobItem( + job_id=job_id, + item_key=key, + status="pending", + payload=it.get("payload") or {}, + attempts=0, + ) + ) + existing.add(key) + created += 1 + self.db.commit() + return {"created": created, "skipped": len(items) - created} + + def pending( + self, job_id: str, *, status: str = "pending", limit: Optional[int] = None + ) -> List[Dict[str, Any]]: + q = ( + self.db.query(JobItem) + .filter(JobItem.job_id == job_id, JobItem.status == status) + .order_by(JobItem.item_key) + ) + if limit: + q = q.limit(int(limit)) + return [ + { + "key": r.item_key, + "payload": r.payload or {}, + "status": r.status, + "attempts": r.attempts or 0, + "result": r.result, + "review": r.review, + } + for r in q.all() + ] + + def update_item( + self, + job_id: str, + item_key: str, + *, + status: Optional[str] = None, + result: Optional[Any] = None, + review: Optional[Dict[str, Any]] = None, + error: Optional[str] = None, + bump_attempts: bool = False, + ) -> bool: + row = ( + self.db.query(JobItem) + .filter(JobItem.job_id == job_id, JobItem.item_key == str(item_key)[:128]) + .first() + ) + if row is None: + return False + if status: + row.status = status + if result is not None: + row.result = result + if review is not None: + merged = dict(row.review or {}) + merged.update(review) + row.review = merged + if error is not None: + row.error = str(error)[:4000] + if bump_attempts: + row.attempts = (row.attempts or 0) + 1 + row.updated_at = _utcnow() + self.db.commit() + return True + + def stats(self, job_id: str) -> Dict[str, int]: + rows = ( + self.db.query(JobItem.status, func.count()) + .filter(JobItem.job_id == job_id) + .group_by(JobItem.status) + .all() + ) + out: Dict[str, int] = {s: int(c) for s, c in rows} + total = sum(out.values()) + settled = sum(out.get(s, 0) for s in SETTLED_STATUSES) + return { + "total": total, + "done": out.get("done", 0), + "pending": out.get("pending", 0), + "failed": out.get("failed", 0), + "not_found": out.get("not_found", 0), + "needs_review": out.get("needs_review", 0), + "running": out.get("running", 0), + "settled": settled, + "remaining": total - settled, + } + + # ── 用量与审计 ──────────────────────────────────────────────────── + + def budget_left(self, job_id: str) -> Dict[str, int]: + job = self.get(job_id) + if job is None: + return {"calls_left": 0, "tokens_left": 0, "seconds_left": 0} + budget = dict(DEFAULT_BUDGET) + budget.update(job.budget or {}) + usage = job.usage or {} + started = job.started_at or job.created_at or _utcnow() + if started.tzinfo is None: + started = started.replace(tzinfo=timezone.utc) + elapsed = int((_utcnow() - started).total_seconds()) + return { + "calls_left": max(0, int(budget["max_calls"]) - int(usage.get("calls", 0))), + "tokens_left": max(0, int(budget["max_tokens"]) - int(usage.get("tokens", 0))), + "seconds_left": max(0, int(budget["max_seconds"]) - elapsed), + "concurrency": int(budget.get("concurrency", 8)), + } + + def add_usage(self, job_id: str, *, calls: int = 0, tokens: int = 0) -> None: + job = self.get(job_id) + if job is None: + return + usage = dict(job.usage or {}) + usage["calls"] = int(usage.get("calls", 0)) + int(calls) + usage["tokens"] = int(usage.get("tokens", 0)) + int(tokens) + job.usage = usage + flag_modified(job, "usage") + self.db.commit() + + def record_call( + self, + job_id: str, + *, + item_key: Optional[str], + prompt: str, + model: Optional[str], + duration_ms: int, + status: str, + tokens: Optional[Dict[str, Any]] = None, + error: Optional[str] = None, + ) -> None: + seq = ( + self.db.query(func.count(JobCall.call_id)).filter(JobCall.job_id == job_id).scalar() + or 0 + ) + self.db.add( + JobCall( + call_id=f"jc_{uuid.uuid4().hex[:16]}", + job_id=job_id, + item_key=(item_key or None), + seq=int(seq) + 1, + prompt_hash=hashlib.sha256((prompt or "").encode("utf-8")).hexdigest()[:64], + model=(model or "")[:128] or None, + tokens=tokens or {}, + duration_ms=int(duration_ms), + status=status, + error=(error or "")[:4000] or None, + ) + ) + self.db.commit() diff --git a/src/backend/orchestration/chat_run_executor.py b/src/backend/orchestration/chat_run_executor.py index c5cca567..00090c23 100644 --- a/src/backend/orchestration/chat_run_executor.py +++ b/src/backend/orchestration/chat_run_executor.py @@ -2219,6 +2219,36 @@ async def _stream_last_write_ms(run_id: str) -> Optional[int]: return None +def _chat_has_live_job(chat_id: Optional[str], now: datetime) -> bool: + """这个会话是否挂着**还在推进**的批量作业(跨进程可见的活性证据)。 + + 判据是 ``jobs.updated_at`` 在静默窗口内前进过:作业每完成一次子调用都会写 usage, + onupdate 随之推进 updated_at。只要作业还在动,这个 run 就不是僵尸——哪怕它的 + asyncio task 属于另一个进程(多 worker / 多副本),也哪怕主链路一个流事件都没有。 + + 作业本身有 max_seconds 墙钟预算与熔断,所以这条豁免不会让 run 永生。 + """ + if not chat_id: + return False + try: + from core.db.models import Job + + cutoff = now - timedelta(seconds=_STALE_QUIET_SEC) + with SessionLocal() as db: + return ( + db.query(Job.job_id) + .filter( + Job.chat_id == chat_id, + Job.status.in_(("pending", "running")), + Job.updated_at >= cutoff, + ) + .first() + is not None + ) + except Exception: # noqa: BLE001 —— 证据查不到就按原判据走,绝不因此放过真僵尸 + return False + + async def reap_stale_runs() -> int: """Periodic safety net: fail 'running' runs that show no sign of life. @@ -2288,6 +2318,15 @@ async def reap_stale_runs() -> int: continue hard_expired = False # 孤儿 loop:不按年龄硬杀,交给静默判据 + # 工作流模式的批量作业:run 卡在 run_job(wait=True) 里等作业,期间主链路一个 + # 流事件都不产生(逐项结果按设计不进对话),"流静默"判据必然误判。进程内 task + # 豁免只在本进程有效——多 worker / 多副本部署里,另一个进程看不到这个 task, + # 照样会把健康的长作业杀掉。所以这里用**跨进程可见的证据**:DB 里这个会话是否 + # 还有正在推进的作业(每次子调用都会 add_usage → jobs.updated_at 前进)。 + if _chat_has_live_job(cid, now): + logger.info("chat_run_stale_skip_live_job", run_id=rid, chat_id=cid) + continue + if not hard_expired: # A run whose asyncio task is alive in THIS process is never a # zombie — the in-process inactivity watchdog owns hang detection diff --git a/src/backend/orchestration/job_runtime.py b/src/backend/orchestration/job_runtime.py new file mode 100644 index 00000000..f027eb19 --- /dev/null +++ b/src/backend/orchestration/job_runtime.py @@ -0,0 +1,1008 @@ +"""作业编排运行时(Job Runtime)驱动。 + +一个 job = 主对话智能体写的一段**作业脚本**的一次执行。脚本跑在沙箱里(普通 Python +进程,拥有文件/网络/并发/子进程),需要模型判断时通过带 job token 的**回调**请求后端 +派子智能体——模型凭据因此永远不进沙箱。 + +驱动职责: + +1. 把 SDK(``hugagent_job.py``)+ 用户脚本 + 运行器写进沙箱的 job 目录 +2. 以 detached 方式启动运行器(不能同步等:沙箱 HTTP 客户端有 120s 请求超时) +3. 轮询 DB 里的 job 状态直到终态,期间按节流发 ``sub_type=progress`` + —— 这条是喂主 run 无活动看门狗的活性信号,缺了长作业会被当成卡死强杀 +4. 到点熔断(墙钟预算)、取消、进程重启后对账 + +台账在 DB(``job_items``)而不是沙箱文件:沙箱池化复用会让新 job 读到旧 job 的残留账本 +(autonomous_loop 踩过这个坑),以 job_id 作主键从结构上避免。 +""" + +from __future__ import annotations + +import asyncio +import base64 +import json +import logging +import os +import time +from datetime import datetime, timezone +from typing import Any, Dict, List, Optional, Tuple + +from sqlalchemy.orm.attributes import flag_modified + +from core.db.engine import SessionLocal +from core.db.models import Job +from core.services.job_service import JobService + +logger = logging.getLogger(__name__) + +JOB_ROOT = "/workspace/.job" + +# 进程内活跃 job 的驱动 task —— 与 chat_run/loop 的做法一致:进程内 task 还活着的 job +# 不按孤儿处理,重启后才由 resume_running_jobs() 对账。 +_active_jobs: Dict[str, asyncio.Task] = {} + +_POLL_INTERVAL_S = 2.0 +_PROGRESS_EVERY_S = 5.0 +# 中途唤醒间隔:每次都是一轮真实推理,太密就是烧钱,太疏就等于全程失联。 +# 取 5 分钟——15 分钟的盲区太长,作业跑歪了要等一刻钟才有人发现;而播报本身很短, +# 5 分钟一次的上下文成本可以接受。可用 run_job 的 start_params.progress_wake_sec +# 覆盖;<=0 关闭中途唤醒(实时进度看状态条,那条零推理成本)。 +_PROGRESS_WAKE_EVERY_S = 300.0 +# 无回调静默多久判失联。给得比单次子作业耗时宽裕得多(实测单次可达 210s,还要算上 +# 退避重试),但远小于 2 小时墙钟——僵在 running 空烧两小时是最难受的失败形态。 +_SILENT_TIMEOUT_S = 900.0 +# 孤儿对账(reap_orphan_jobs):pending 迟迟不转 running 的短闸 + 巡检间隔。 +# runner 起来第一件事就是上报 running,5 分钟还没动静基本就是没起来(回调不通/沙箱没了)。 +_ORPHAN_PENDING_GRACE_S = 300.0 +_ORPHAN_REAP_INTERVAL_S = 120.0 + + +# 探测成功的回调基址(网络拓扑一进程内不会变,探到一次就够) +_resolved_callback_base: Optional[str] = None + + +def callback_base_candidates() -> List[str]: + """沙箱回调后端的候选基址,按可靠性排序。 + + ⚠️ 这里**不能**只有一个写死的默认值。沙箱与后端的网络关系随部署形态而变: + 本机开发时沙箱不在 compose 网络里(只有宿主映射端口可达),而 HugAgentOS / + 主测试机上沙箱容器与 backend 同在一张 docker 网络(服务名可达, + ``host.docker.internal`` 反而**解析不了**)。写死宿主的后果实测过:runner 起来后 + 第一发回调就 ``Name or service not known`` 当场死掉,作业永远停在 pending、 + 台账一条没有——用户只看见状态条上一个转圈的菊花,什么都不知道。 + + 所以改成候选表 + 启动前从沙箱里真探一次(见 ``resolve_callback_base``)。 + ``JOB_CALLBACK_URL`` 仍然是最高优先级的手动覆盖。 + """ + env = (os.environ.get("JOB_CALLBACK_URL") or "").strip() + if env: + return [env.rstrip("/")] + port = (os.environ.get("PORT") or os.environ.get("BACKEND_PORT") or "3001").strip() + return [ + f"http://backend:{port}", # 同网 docker:服务名直连后端(后端自身不带 /api 前缀) + "http://frontend/api", # 同网 docker:经前端 nginx 反代(/api 由它剥掉) + "http://host.docker.internal:3000/api", # 沙箱不在同网:回宿主映射端口 + ] + + +# 探测脚本:在沙箱里逐个候选打 /health,第一个应答的即选中。只用标准库, +# 因为沙箱镜像不保证有 curl。 +_PROBE_SOURCE = r'''import json, sys, urllib.request + +for base in json.loads(sys.argv[1]): + try: + with urllib.request.urlopen(base + "/health", timeout=4) as resp: + if resp.status < 500: + print("PICK " + base) + sys.exit(0) + except Exception: + continue +print("NONE") +''' + + +async def resolve_callback_base(*, session_id: str, user_id: str) -> str: + """从沙箱里探出一个真正可达的回调基址;一个都不通就抛错(**不许静默启动**)。 + + 宁可在提交作业这一步就失败——错误会原样回到模型和用户手上,而"启动成功但永远 + 没有进度"是最贵的失败形态:驱动干等、状态条转圈、用户等一小时才发现什么都没发生。 + """ + global _resolved_callback_base + + candidates = callback_base_candidates() + if (os.environ.get("JOB_CALLBACK_URL") or "").strip(): + return candidates[0] # 手动指定即信任,不浪费一次探测 + if _resolved_callback_base: + return _resolved_callback_base + + payload = json.dumps(candidates, ensure_ascii=False) + cmd = ( + f"echo '{_b64(_PROBE_SOURCE)}' | base64 -d > /tmp/_job_probe.py && " + f"${{PY_BIN:-python3}} /tmp/_job_probe.py '{payload}'" + ) + try: + _code, out, _err = await _sbx_bash( + cmd, session_id=session_id, user_id=user_id, timeout=60 + ) + except Exception as exc: # noqa: BLE001 + raise RuntimeError(f"回调地址探测失败(沙箱不可用): {exc}") from exc + + for line in (out or "").splitlines(): + if line.startswith("PICK "): + base = line[5:].strip().rstrip("/") + _resolved_callback_base = base + logger.info("[job] callback base resolved: %s", base) + return base + + raise RuntimeError( + "沙箱连不上后端回调地址,作业无法上报进度,已拒绝启动。已尝试:" + + "、".join(candidates) + + "。请在后端环境变量里设置 JOB_CALLBACK_URL 指向沙箱可达的后端地址" + "(同一 docker 网络用 http://backend:<端口>,跨网络用宿主映射地址)。" + ) + + +def callback_base_url() -> str: + """已探到的回调基址(探测前调用则给候选表里的第一个)。""" + return _resolved_callback_base or callback_base_candidates()[0] + + +# ── 沙箱侧 SDK(只依赖标准库;沙箱里不保证有 httpx/requests) ────────────── +SDK_SOURCE = r'''"""hugagent_job —— 作业脚本 SDK(由 Job Runtime 注入沙箱,请勿手工修改)。 + +暴露三类能力,其余一切(HTTP 抓取、解析、并发、写文件)都用标准 Python 做: + + ledger.seed / pending / update / stats 工作项台账(按业务主键幂等) + agent(prompt, schema=…, tools=…) 派一个子智能体;凭据在后端,脚本看不到 + job.map / job.budget / log 并发、预算、进度 + +断点续跑:重跑同一脚本时 ledger.pending() 自动跳过已完成项,不必重放调用序列。 +""" + +import json +import os +import random +import time +import urllib.error +import urllib.request +from concurrent.futures import ThreadPoolExecutor, as_completed + +JOB_ID = os.environ.get("JOB_ID", "") +JOB_TOKEN = os.environ.get("JOB_TOKEN", "") +BASE = (os.environ.get("JOB_CALLBACK_URL") or "").rstrip("/") + +__all__ = ["ledger", "agent", "job", "log", "JobError"] + + +class JobError(RuntimeError): + pass + + +_warned = set() + + +def _warn_once(message): + """同一类问题只喊一次,但一定要喊 —— 沉默是这套东西最贵的失败模式。""" + if message[:60] in _warned: + return + _warned.add(message[:60]) + log("[warn] " + message) + + +def _post(path, payload, timeout=180): + url = "%s/v1/internal/jobs/%s/%s" % (BASE, JOB_ID, path) + body = json.dumps(payload or {}).encode("utf-8") + req = urllib.request.Request(url, data=body, method="POST") + req.add_header("Content-Type", "application/json") + req.add_header("X-Job-Token", JOB_TOKEN) + last = None + # 限流要退避到"真的等得起"为止:并发工作项会被同一次 429 同时弹回, + # 次数太少等于没退避,所以给限流单独一条更长的重试预算。 + for attempt in range(6): + try: + with urllib.request.urlopen(req, timeout=timeout) as resp: + raw = resp.read().decode("utf-8") or "{}" + data = json.loads(raw) + # 错误一律以 HTTP 4xx/5xx 返回(见后端 HTTPException),这里只拆信封 + if isinstance(data, dict) and "data" in data: + return data["data"] + return data + except urllib.error.HTTPError as exc: + detail = "" + try: + detail = exc.read().decode("utf-8")[:400] + except Exception: + pass + # 4xx 是契约问题,重试没有意义 —— 但 **429 除外**:它是限流,是"待会儿再来", + # 不是"你写错了"。把 429 当契约错误曾让一次作业整体崩掉:并发回调打爆网关限流后, + # 连 runner 上报终态的那一发也被 429 拒,作业于是永远停在 running。 + if 400 <= exc.code < 500 and exc.code != 429: + raise JobError("callback %s -> HTTP %s %s" % (path, exc.code, detail)) + last = exc + except Exception as exc: + last = exc + # 指数退避 + 抖动:等步长退避会让被同一次限流弹回的并发项整齐地再撞一次 + time.sleep(min(1.5 * (2 ** attempt), 30.0) + random.uniform(0, 0.5)) + raise JobError("callback %s failed: %s" % (path, last)) + + +class _Ledger: + def seed(self, items): + """items: [{"key": "...", "payload": {...}}, ...];已存在的 key 一律跳过。""" + out = {"created": 0, "skipped": 0} + batch = [] + for it in items: + batch.append(it) + if len(batch) >= 500: + r = _post("ledger", {"op": "seed", "items": batch}) + out["created"] += r.get("created", 0) + out["skipped"] += r.get("skipped", 0) + batch = [] + if batch: + r = _post("ledger", {"op": "seed", "items": batch}) + out["created"] += r.get("created", 0) + out["skipped"] += r.get("skipped", 0) + return out + + def pending(self, status="pending", limit=None): + return _post("ledger", {"op": "pending", "status": status, "limit": limit}) or [] + + def update(self, key, status=None, result=None, review=None, error=None, bump_attempts=False): + out = self._update(key, status, result, review, error, bump_attempts) + # 后端说这个 key 不在台账里 —— 几乎总是"忘了 ledger.seed"。必须喊出来: + # 静默打空曾让一次 568 项的作业跑完全程、进度停在 0、成果一条没留下。 + if isinstance(out, dict) and out.get("known_key") is False: + _warn_once( + "ledger.update 写了一个台账里不存在的 key=%r —— 是不是漏了 ledger.seed()?" + "没有台账就没有进度、没有断点续跑,本次回写已被丢弃。" % (key,) + ) + return out + + def _update(self, key, status, result, review, error, bump_attempts): + return _post( + "ledger", + { + "op": "update", + "key": key, + "status": status, + "result": result, + "review": review, + "error": error, + "bump_attempts": bool(bump_attempts), + }, + ) + + def stats(self): + return _post("ledger", {"op": "stats"}) or {} + + +class _Job: + def budget(self): + return _post("ledger", {"op": "budget"}) or {} + + def map(self, items, fn, concurrency=8, key=None): + """并发跑 fn(item),**逐项立即落账**。 + + 回写约定(这样写出来的脚本天然可断点续跑): + + - fn 返回 dict → 立刻 ``ledger.update(key, status="done", result=)``; + 想要别的状态就在 dict 里放 ``_status``(如 ``{"_status": "not_found"}``)。 + - fn 返回 None → 不自动回写(表示 fn 自己已经 update 过了)。 + - fn 抛异常 → 该项记 failed + error,**不拖垮其余项**。 + + 千万别写成「先 map 完再统一回写」:那样中途全程 pending,进程一挂全白跑。 + + **台账主键怎么取**:默认按 ``key`` → ``item_key`` → ``id`` → ``seq`` 顺序在 item 里找。 + ``ledger.seed`` 用的是 ``{"key": ..., "payload": ...}`` 形状,而 map 常常直接收原始 + 业务对象(``{"seq": 7, "name": ...}``)——两者形状不同是常态,所以这里必须兜底, + 否则回写会**静默跳过**(实测:调用真跑了、台账全程 pending、成果全丢)。 + 取不到时用 ``key=`` 显式指定字段名或函数;仍取不到则 log 告警,绝不静默。 + """ + items = list(items) + if not items: + return [] + n = max(1, min(int(concurrency), 16)) + results = [None] * len(items) + warned = [] + + def _key_of(it): + if callable(key): + got = key(it) + return str(got) if got not in (None, "") else None + if not isinstance(it, dict): + return None + fields = [key] if isinstance(key, str) else ["key", "item_key", "id", "seq"] + for f in fields: + got = it.get(f) + if got not in (None, ""): + return str(got) + return None + + def _warn_unbookable(it): + sample = list(it.keys())[:8] if isinstance(it, dict) else type(it).__name__ + _warn_once( + "job.map 取不到台账主键,本次结果无法回写(字段=%s)。" + "请让 item 带 key/item_key/id/seq,或用 job.map(..., key='字段名')。" % (sample,) + ) + + with ThreadPoolExecutor(max_workers=n) as pool: + futs = {pool.submit(fn, it): i for i, it in enumerate(items)} + done = 0 + for fut in as_completed(futs): + i = futs[fut] + k = _key_of(items[i]) + try: + out = fut.result() + results[i] = out + if isinstance(out, dict): + if k: + payload = dict(out) + status = payload.pop("_status", "done") + ledger.update(k, status=status, result=payload) + else: + _warn_unbookable(items[i]) + except Exception as exc: # noqa: BLE001 —— 异常隔离是本方法的契约 + results[i] = None + # 隔离 ≠ 吞掉。事故里 568 项每一项都抛异常,日志上却只有整齐的 + # "已处理 N/568",没人看得出一次模型调用都没成功。首个异常必须留痕。 + _warn_once("job.map 首个失败项:%s" % repr(exc)[:300]) + if k: + try: + ledger.update(k, status="failed", error=repr(exc)[:1000], + bump_attempts=True) + except Exception: + pass + else: + _warn_unbookable(items[i]) + done += 1 + if done % 10 == 0: + log("已处理 %d/%d" % (done, len(items))) + return results + + +def agent(prompt, schema=None, tools=(), model=None, timeout=180, max_attempts=2, + item_key=None): + """派一个无历史子智能体。schema 非空时强制结构化输出并校验。 + + 凭据在后端,脚本永远拿不到模型端点或 key。 + + **重要**:工具全挂 / 配额打爆 / 连接超时时,本函数**抛 JobError**,不会返回一个 + "查无"的结论——因为那一轮压根没取到证据。请让异常自然向上抛(job.map 会把该项记 + failed 留在台账里等续跑),**不要**用 try/except 把它转写成"未查询到",那等于把 + 环境故障固化成数据。 + """ + return _post( + "agent", + { + "prompt": prompt, + "schema": schema, + "tools": list(tools or ()), + "model": model, + "max_attempts": int(max_attempts), + "item_key": item_key, + }, + timeout=timeout + 60, + ) + + +def log(message): + try: + _post("log", {"message": str(message)[:2000]}, timeout=30) + except Exception: + pass + print("[job] %s" % message, flush=True) + + +def _lifecycle(status, error=None): + return _post("log", {"lifecycle": status, "error": error}, timeout=60) + + +ledger = _Ledger() +job = _Job() +''' + + +_RUNNER_SOURCE = r'''"""作业运行器 —— 包住用户脚本,上报生命周期。由 Job Runtime 生成。""" + +import json +import runpy +import sys +import time +import traceback + +import hugagent_job as hj + + +def _report(status, error=None): + """上报终态 —— 这一发**绝不允许失败**。 + + 终态上报失败过一次,代价是作业永远停在 running:驱动只能等墙钟熔断(默认 2 小时), + 期间还在按间隔叫醒智能体报"停滞"。所以这里自己兜住异常并重试;实在报不上去也要 + 把状态写进本地文件,让驱动的存活探测能读到真相。 + """ + for _ in range(3): + try: + hj._lifecycle(status, error) + return + except BaseException: + time.sleep(3) + try: + with open("lifecycle.final", "w") as fh: + fh.write(json.dumps({"status": status, "error": (error or "")[-2000:]})) + except BaseException: + pass + + +hj._lifecycle("running") +try: + runpy.run_path("user_script.py", run_name="__main__") +except SystemExit as exc: + code = exc.code if isinstance(exc.code, int) else 0 + _report("completed" if code == 0 else "failed", + None if code == 0 else "script exited with code %s" % code) + sys.exit(0) +except BaseException: + _report("failed", traceback.format_exc()[-4000:]) + sys.exit(0) +else: + _report("completed") +''' + + +def _b64(text: str) -> str: + return base64.b64encode(text.encode("utf-8")).decode("ascii") + + +async def _sbx_bash(command: str, *, session_id: str, user_id: str, timeout: int = 60): + """在持久沙箱里跑一段 bash,返回 (exit_code, stdout, stderr)。""" + from core.sandbox import ExecuteRequest, get_sandbox_provider + + provider = get_sandbox_provider() + res = await provider.execute( + ExecuteRequest( + script_content=command, + script_name="job_ctl.sh", + language="bash", + timeout=timeout, + session_id=session_id, + user_id=user_id, + ) + ) + return res.exit_code, (res.stdout or ""), (res.stderr or "") + + +def _emit_progress(chat_id: Optional[str], note: str) -> None: + """活性信号:转译成 model_progress 喂主 run 的无活动看门狗。 + + 长作业期间主对话没有任何可渲染输出,缺了这条会被 600s 看门狗当成卡死强杀。 + """ + if not chat_id: + return + try: + from core.llm import _subagent_stream + + if not _subagent_stream.is_active(chat_id): + return + _subagent_stream.push( + chat_id, + {"sub_type": "progress", "agent_id": "job", "agent_name": "批量作业", "note": note}, + ) + except Exception: # noqa: BLE001 —— 活性信号是尽力而为,永远不该拖垮作业 + pass + + +async def _maybe_wake(job_row_id: str) -> None: + """作业终态后叫醒会话(幂等;失败只记日志,不影响作业结果)。""" + try: + from orchestration.job_wakeup import wake_on_job_finish + + await wake_on_job_finish(job_row_id) + except Exception as exc: # noqa: BLE001 + logger.warning("[job] wake failed job=%s: %s", job_row_id, exc) + + +async def _maybe_wake_progress( + job_row_id: str, *, stats: Dict[str, Any], budget_left: Dict[str, Any], stalled: bool +) -> None: + """中途播报进度(失败只记日志,绝不影响作业本身)。""" + try: + from orchestration.job_wakeup import wake_on_job_progress + + await wake_on_job_progress( + job_row_id, stats=stats, budget_left=budget_left, stalled=stalled + ) + except Exception as exc: # noqa: BLE001 + logger.warning("[job] progress wake failed job=%s: %s", job_row_id, exc) + + +def _final_from_marker(text: str) -> Tuple[str, str]: + """从 runner 落盘的 ``lifecycle.final`` 里捡回它没能上报的终态。 + + 上报走网络会丢,落盘不会——所以进程退出前写的这行 JSON 是最后一手真相。 + 捡不到就按 failed 处理:进程没了而作业还 running,本来就不是正常收尾。 + """ + for line in (text or "").splitlines(): + line = line.strip() + if not line.startswith("{") or '"status"' not in line: + continue + try: + data = json.loads(line) + except Exception: # noqa: BLE001 + continue + status = str(data.get("status") or "") + if status in ("completed", "failed", "cancelled"): + return status, str(data.get("error") or "") + return "failed", "" + + +async def _runner_liveness( + job_row_id: str, *, session_id: str, user_id: str +) -> Tuple[bool, str]: + """探测沙箱里的 runner 进程是否还活着,并捡回它没能上报的终态。 + + 为什么必须有:终态上报是一次网络调用,它自己也会失败(实测被网关限流 429 打掉过)。 + 一旦丢了,作业就永远 running——驱动干等 2 小时墙钟,期间还在按间隔叫醒智能体报停滞。 + 进程存活是**本地事实**,不依赖任何网络,所以拿它当兜底真相。 + + 返回 (是否还活着, 终态说明)。 + """ + workdir = f"{JOB_ROOT}/{job_row_id}" + cmd = ( + f"cd {workdir} 2>/dev/null || exit 9; " + "if pgrep -f '_runner.py' >/dev/null 2>&1; then echo ALIVE; else echo DEAD; fi; " + "cat lifecycle.final 2>/dev/null; " + "tail -c 1200 runner.log 2>/dev/null" + ) + try: + code, out, _err = await _sbx_bash( + cmd, session_id=session_id, user_id=user_id, timeout=45 + ) + except Exception as exc: # noqa: BLE001 —— 探测失败一律当"还活着",绝不误杀 + logger.warning("[job] liveness probe failed job=%s: %s", job_row_id, exc) + return True, "" + if code == 9: + return True, "" # 目录还没建好(刚启动),别急着判死 + text = out or "" + if text.lstrip().startswith("ALIVE"): + return True, "" + return False, text[-1200:] + + +async def _keepalive_sandbox(session_id: str) -> None: + try: + from core.sandbox import get_sandbox_provider + + provider = get_sandbox_provider() + touch = getattr(provider, "touch_session", None) + if touch: + await touch(session_id) + except Exception: # noqa: BLE001 + pass + + +async def prepare_and_launch( + job_row_id: str, + *, + user_id: str, + session_id: str, + script_text: str, + token: str, + interpreter: str = "${PY_BIN:-python3}", +) -> None: + """把 SDK/用户脚本/运行器写进沙箱并以 detached 方式启动。 + + 不能同步等脚本跑完:沙箱 HTTP 客户端有 120s 请求超时,长作业必然撞上。 + + 启动前**先探回调地址**:runner 的一切(建台账、派子作业、上报终态)都走回调, + 回调不通的作业等于没跑,却会以 pending 挂在状态条上转圈。探不到就在这里失败, + 错误直接回到模型/用户手上。 + """ + callback_url = await resolve_callback_base(session_id=session_id, user_id=user_id) + workdir = f"{JOB_ROOT}/{job_row_id}" + cmd = "\n".join( + [ + "set -e", + f"mkdir -p {workdir}", + f"cd {workdir}", + f"echo '{_b64(SDK_SOURCE)}' | base64 -d > hugagent_job.py", + f"echo '{_b64(_RUNNER_SOURCE)}' | base64 -d > _runner.py", + f"echo '{_b64(script_text)}' | base64 -d > user_script.py", + # detached:立即返回,后续状态全部由回调驱动 + "JOB_ID={jid} JOB_TOKEN={tok} JOB_CALLBACK_URL={url} PYTHONUNBUFFERED=1 " + "nohup {interp} _runner.py > runner.log 2>&1 &".format( + jid=job_row_id, tok=token, url=callback_url, interp=interpreter + ), + "echo launched", + ] + ) + code, out, err = await _sbx_bash(cmd, session_id=session_id, user_id=user_id, timeout=90) + if code != 0: + raise RuntimeError(f"作业启动失败 exit={code} stderr={(err or out)[:500]}") + + +async def write_sandbox_file( + path: str, content: str, *, session_id: str, user_id: str +) -> Tuple[bool, str]: + """把文本写进沙箱,**分块 + 读回校验**。返回 (是否成功, 说明)。 + + 为什么不能一条 `echo '' | base64 -d > f` 了事:沙箱 execute 对命令体积有上限, + 超了之后**静默失败**——exit=0、stderr 为空、文件却不存在(实测拐点在 b64 约 + 170KB;568 行台账导出正好落在这个区间,于是"导出成功"但文件从来没出现过)。 + 所以这里按块追加,并且以**沙箱里读回的真实字节数**为准,绝不用调用方的计数报成功。 + """ + raw = (content or "").encode("utf-8") + b64 = base64.b64encode(raw).decode("ascii") + # 单块 48KB b64(≈36KB 原文),远离静默失败拐点 + chunk = 48_000 + parts = [b64[i : i + chunk] for i in range(0, len(b64), chunk)] or [""] + + code, out, err = await _sbx_bash( + f"mkdir -p $(dirname {path}) && : > {path}.b64", + session_id=session_id, + user_id=user_id, + timeout=60, + ) + if code != 0: + return False, f"创建目标失败: {(err or out)[:200]}" + + for idx, part in enumerate(parts): + code, out, err = await _sbx_bash( + f"printf '%s' '{part}' >> {path}.b64", + session_id=session_id, + user_id=user_id, + timeout=90, + ) + if code != 0: + return False, f"第 {idx + 1}/{len(parts)} 块写入失败: {(err or out)[:200]}" + + code, out, err = await _sbx_bash( + f"base64 -d {path}.b64 > {path} && rm -f {path}.b64 && wc -c < {path}", + session_id=session_id, + user_id=user_id, + timeout=90, + ) + if code != 0: + return False, f"解码失败: {(err or out)[:200]}" + + written = "".join((out or "").split()) + if not written.isdigit() or int(written) != len(raw): + return False, f"落盘校验不通过:期望 {len(raw)} 字节,沙箱读回 {written or '(空)'}" + return True, f"{len(raw)} 字节 / {len(parts)} 块" + + +async def read_runner_log(job_row_id: str, *, user_id: str, session_id: str, tail: int = 40) -> str: + # 同样走 base64:沙箱 execute 的 stdout 会丢换行,直接 tail 出来的日志会连成一坨 + code, out, _ = await _sbx_bash( + f"tail -n {int(tail)} {JOB_ROOT}/{job_row_id}/runner.log 2>/dev/null | base64 -w0 || true", + session_id=session_id, + user_id=user_id, + timeout=30, + ) + if code != 0 or not (out or "").strip(): + return "" + try: + return base64.b64decode("".join(out.split())).decode("utf-8", "replace") + except Exception: # noqa: BLE001 + return out + + +async def drive(job_row_id: str, *, chat_id: Optional[str]) -> Dict[str, Any]: + """轮询直到 job 进终态;负责墙钟熔断、沙箱保活、活性信号、中途进度唤醒。""" + last_progress = 0.0 + last_keepalive = 0.0 + last_liveness = time.monotonic() + started = time.monotonic() + # 中途唤醒的记账:只在进程内存着——驱动重启时作业本来就要重新接管,多播一次无害, + # 而写库打标会让每个作业多出一串没人读的 metadata 抖动。 + last_wake = time.monotonic() + last_wake_settled = -1 + # 心跳 = 累计子作业调用数 + 已结算项数。两者都不动就是真的没在推进。 + last_heartbeat = -1 + last_heartbeat_ts = time.monotonic() + + while True: + with SessionLocal() as db: + svc = JobService(db) + job = svc.get(job_row_id) + if job is None: + return {"status": "failed", "error": "job 不存在"} + status = str(job.status) + session_id = job.sandbox_session_id or "" + user_id = job.user_id + budget = dict(job.budget or {}) + stats = svc.stats(job_row_id) + wake_every = float( + dict((job.extra_data or {}).get("start_params") or {}).get( + "progress_wake_sec", _PROGRESS_WAKE_EVERY_S + ) + ) + budget_left = svc.budget_left(job_row_id) if wake_every > 0 else {} + + if status in ("completed", "failed", "cancelled"): + result = {"status": status, "stats": stats, "error": job.error_message} + # 终态 → 叫醒会话(仅 wait=False 提交的作业;wait=True 的调用方本来就在等返回) + await _maybe_wake(job_row_id) + return result + + elapsed = time.monotonic() - started + if elapsed > int(budget.get("max_seconds", 7200)): + svc.finish(job_row_id, "failed", error="超出墙钟预算,作业已熔断") + return {"status": "failed", "stats": stats, "error": "超出墙钟预算,作业已熔断"} + + # 无回调静默熔断 —— **不依赖沙箱探测**的兜底。 + # 存活探测要经沙箱 provider,而它自己也会坏(实测:跨事件循环的 asyncio.Lock + # 让探测每次都抛,作业于是继续僵在 running)。回调有没有来是后端自己的事实, + # 任何外部组件坏掉都不影响这个判断,所以拿它当最后一道闸。 + heartbeat = int(job.usage.get("calls", 0)) if job.usage else 0 + heartbeat += int(stats.get("settled", 0)) + if heartbeat != last_heartbeat: + last_heartbeat = heartbeat + last_heartbeat_ts = time.monotonic() + elif time.monotonic() - last_heartbeat_ts > _SILENT_TIMEOUT_S and elapsed > 120: + svc.finish( + job_row_id, + "failed", + error=( + f"作业静默超过 {int(_SILENT_TIMEOUT_S / 60)} 分钟" + "(无子作业调用、台账无推进),判定沙箱或脚本已失联;" + "可用 run_job(action='resume') 断点续跑,已完成的项不会重做" + ), + ) + logger.warning("[job] silent timeout, forced failed job=%s", job_row_id) + await _maybe_wake(job_row_id) + return {"status": "failed", "stats": stats, "error": "作业静默超时"} + + now = time.monotonic() + if now - last_progress >= _PROGRESS_EVERY_S: + last_progress = now + _emit_progress(chat_id, f"作业进行中 done={stats.get('done')} pending={stats.get('pending')}") + if session_id and now - last_keepalive >= 60: + last_keepalive = now + await _keepalive_sandbox(session_id) + # 存活探测:runner 没了但作业还挂着 running,说明终态上报丢了,就地补判终态。 + # 60s 起探(给启动留足时间),之后每 90s 一次——比墙钟熔断早两个数量级发现问题。 + if session_id and now - last_liveness >= 90 and now - started >= 60: + last_liveness = now + alive, tail = await _runner_liveness( + job_row_id, session_id=session_id, user_id=user_id + ) + if not alive: + final = _final_from_marker(tail) + with SessionLocal() as db: + svc = JobService(db) + fresh = svc.stats(job_row_id) + svc.finish( + job_row_id, + final[0], + error=final[1] or f"作业进程已退出但未上报终态;runner 日志尾部:{tail[-600:]}", + ) + logger.warning("[job] runner gone, forced terminal job=%s -> %s", job_row_id, final[0]) + await _maybe_wake(job_row_id) + return {"status": final[0], "stats": fresh, "error": final[1]} + if wake_every > 0 and now - last_wake >= wake_every: + last_wake = now + settled = int(stats.get("settled", 0)) + # 第一次播报没有基线,一律当"有进展"处理;之后零增量即判定停滞 + stalled = last_wake_settled >= 0 and settled <= last_wake_settled + last_wake_settled = settled + await _maybe_wake_progress( + job_row_id, stats=stats, budget_left=budget_left, stalled=stalled + ) + + await asyncio.sleep(_POLL_INTERVAL_S) + + +async def start_job( + *, + user_id: str, + chat_id: Optional[str], + name: str, + script_path: str, + script_text: str, + session_id: str, + budget: Optional[Dict[str, Any]] = None, + start_params: Optional[Dict[str, Any]] = None, + interpreter: str = "${PY_BIN:-python3}", +) -> str: + with SessionLocal() as db: + svc = JobService(db) + job = svc.create( + user_id=user_id, + chat_id=chat_id, + name=name, + script_path=script_path, + script_text=script_text, + sandbox_session_id=session_id, + budget=budget, + start_params={**(start_params or {}), "interpreter": interpreter}, + ) + job_row_id = job.job_id + token = str((job.extra_data or {}).get("token") or "") + + await prepare_and_launch( + job_row_id, + user_id=user_id, + session_id=session_id, + script_text=script_text, + token=token, + interpreter=interpreter, + ) + return job_row_id + + +async def run_and_wait(job_row_id: str, *, chat_id: Optional[str]) -> Dict[str, Any]: + task = asyncio.create_task(drive(job_row_id, chat_id=chat_id)) + _active_jobs[job_row_id] = task + try: + return await task + finally: + _active_jobs.pop(job_row_id, None) + + +def spawn_background(job_row_id: str, *, chat_id: Optional[str]) -> None: + """wait=False:驱动挂后台 task,主对话立即继续。""" + if job_row_id in _active_jobs: + return + + async def _runner(): + try: + await drive(job_row_id, chat_id=chat_id) + except asyncio.CancelledError: + raise + except Exception as exc: # noqa: BLE001 + logger.warning("[job] driver crashed job=%s: %s", job_row_id, exc) + finally: + _active_jobs.pop(job_row_id, None) + + _active_jobs[job_row_id] = asyncio.create_task(_runner()) + + +async def cancel_job(job_row_id: str, *, user_id: str) -> bool: + """协作式取消:杀沙箱里的运行器进程 + 归位状态(无活跃 task 也不报错)。""" + with SessionLocal() as db: + svc = JobService(db) + job = svc.get(job_row_id) + if job is None or job.user_id != user_id: + return False + session_id = job.sandbox_session_id or "" + svc.finish(job_row_id, "cancelled", error="用户取消") + + task = _active_jobs.pop(job_row_id, None) + if task and not task.done(): + task.cancel() + if session_id: + await _sbx_bash( + f"pkill -f '{JOB_ROOT}/{job_row_id}' || true", + session_id=session_id, + user_id=user_id, + timeout=30, + ) + return True + + +async def resume_job(job_row_id: str, *, user_id: str, chat_id: Optional[str]) -> Dict[str, Any]: + """断点续跑:换发 token、原样重启脚本。 + + 已完成的项留在台账里,脚本侧 ``ledger.pending()`` 自动跳过——不必重放调用序列。 + """ + with SessionLocal() as db: + svc = JobService(db) + job = svc.get(job_row_id) + if job is None or job.user_id != user_id: + return {"ok": False, "error": "job 不存在或无权访问"} + if job.status == "running" and job_row_id in _active_jobs: + return {"ok": False, "error": "作业仍在运行中"} + script_text = job.script_text or "" + session_id = job.sandbox_session_id or "" + interpreter = str((job.extra_data or {}).get("start_params", {}).get("interpreter") or "${PY_BIN:-python3}") + job.status = "pending" + job.completed_at = None + job.error_message = None + # 续跑后再次终态时要能再叫醒一次 + meta = dict(job.extra_data or {}) + meta.pop("woken_at", None) + job.extra_data = meta + from sqlalchemy.orm.attributes import flag_modified + + flag_modified(job, "extra_data") + db.commit() + token = svc.rotate_token(job_row_id) or "" + + await prepare_and_launch( + job_row_id, + user_id=user_id, + session_id=session_id, + script_text=script_text, + token=token, + interpreter=interpreter, + ) + return {"ok": True, "job_id": job_row_id} + + +async def reap_orphan_jobs() -> int: + """周期性对账:活跃态但**没人在驱动**的 job 判失联,归位 interrupted。 + + 为什么必须有:``drive()`` 里的所有护栏(墙钟熔断、静默熔断、runner 存活探测)都长在 + 驱动协程上——驱动本身没了,护栏也一起没了。而驱动是会没的:``wait=True`` 提交的作业 + 驱动挂在工具调用里,用户中止这轮对话、SSE 断掉、run 被回收,驱动就跟着被取消,作业 + 则永远停在 pending/running(实测:HugAgentOS 上一条作业 runner 早已死亡,12 分钟后 + DB 里还是 pending、无错误、无台账——状态条只能一直转圈)。启动钩子 + ``resume_running_jobs`` 只在进程重启时兜底,进程没重启就永远兜不到。 + + 判据用**跨进程可见的证据**(DB ``updated_at``:每次回调写台账/记用量都会推进它), + 而不是只看本进程的 task 表——多 worker 部署里别的进程持有驱动,本进程看不见。 + """ + now = datetime.now(timezone.utc) + reaped: List[Tuple[str, Optional[str]]] = [] + with SessionLocal() as db: + rows = db.query(Job).filter(Job.status.in_(("pending", "running"))).all() + for row in rows: + job_row_id = str(row.job_id) + task = _active_jobs.get(job_row_id) + if task is not None and not task.done(): + continue # 本进程正在驱动,护栏归 drive() 管 + last = row.updated_at or row.created_at + if last is None: + continue + if last.tzinfo is None: + last = last.replace(tzinfo=timezone.utc) + quiet = (now - last).total_seconds() + # pending 用短闸:runner 起来的第一件事就是上报 running,几分钟还没动静就是没起来。 + # running 用与驱动同一把静默闸,避免误杀"单项耗时很长但确实在跑"的作业。 + limit = _ORPHAN_PENDING_GRACE_S if row.status == "pending" else _SILENT_TIMEOUT_S + if quiet < limit: + continue + row.status = "interrupted" + row.error_message = ( + f"作业已失联({int(quiet / 60)} 分钟无任何回调,且没有驱动在跟进)," + "已归位为可续跑状态;run_job(action='resume') 可断点续跑,已完成的项不会重做" + ) + meta = dict(row.extra_data or {}) + meta.pop("token", None) # 失联即作废旧 token,防残留进程回来乱写 + row.extra_data = meta + flag_modified(row, "extra_data") + reaped.append((job_row_id, row.chat_id)) + if reaped: + db.commit() + + for job_row_id, _chat_id in reaped: + logger.warning("[job] orphan job reaped job=%s", job_row_id) + await _maybe_wake(job_row_id) + return len(reaped) + + +async def run_job_reaper_loop() -> None: + """启动时拉起一次的后台循环;正常情况下永不返回。""" + while True: + try: + await asyncio.sleep(_ORPHAN_REAP_INTERVAL_S) + await reap_orphan_jobs() + except asyncio.CancelledError: + raise + except Exception: # noqa: BLE001 + logger.warning("[job] orphan reaper iteration failed", exc_info=True) + + +async def resume_running_jobs() -> int: + """进程重启对账:活跃态的 job 全是孤儿(进程内没有任何 driver task),归位 interrupted。 + + 与 chat_run 的 recover_orphan_runs 一样是启动钩子;区别是 job 的工作项台账在 DB, + 归位后可直接 resume 续跑,已完成的项不会重做。 + """ + with SessionLocal() as db: + rows = db.query(Job).filter(Job.status.in_(("pending", "running"))).all() + if not rows: + return 0 + from sqlalchemy.orm.attributes import flag_modified + + for row in rows: + row.status = "interrupted" + row.error_message = "服务重启导致作业中断,可续跑" + meta = dict(row.extra_data or {}) + meta.pop("token", None) # 旧 token 一并作废 + row.extra_data = meta + flag_modified(row, "extra_data") + db.commit() + n = len(rows) + logger.info("[job] orphan jobs recovered count=%d", n) + return n diff --git a/src/backend/orchestration/job_wakeup.py b/src/backend/orchestration/job_wakeup.py new file mode 100644 index 00000000..0e93e67d --- /dev/null +++ b/src/backend/orchestration/job_wakeup.py @@ -0,0 +1,227 @@ +"""作业唤醒 —— 后台作业跑到里程碑/终点时,把智能体叫回同一个会话继续干活。 + +为什么需要它:``run_job(wait=False)`` 让主对话立刻脱身(小时级作业不能让一次工具调用 +挂那么久),但作业跑完之后**没人叫醒智能体**——用户得再说一句话才会回来看结果。 +这就是"后台干完活,前台不知道"。 + +做法与定时任务同源:由驱动在**同一个会话**里入队一条新的 chat run,带一条系统口吻的 +指令。智能体因此像收到一条新消息一样自己醒过来。前端不用改——它本来就会看到会话里多 +出一轮对话。 + +两种唤醒,别混: + +- **终态唤醒** ``wake_on_job_finish``:作业进终态叫一次,让智能体读台账、写交付。 + 靠 ``jobs.metadata.woken_at`` 打标去重(驱动重入、进程重启续跑都不会重复叫)。 +- **中途唤醒** ``wake_on_job_progress``:作业跑几十分钟甚至几小时时,只在终点播报等于 + 全程失联——用户既不知道还剩多少,也不知道是不是早就卡死了。所以按间隔播报一次进度, + **只汇报、不干活**(提示词里明令禁止重复提交作业/导出台账)。会话里已有在跑的 run + 时直接跳过:那说明智能体正忙,再入队只会堆栈。 + +注意中途唤醒是"贵"的(每次都是一轮真实推理),所以间隔默认 15 分钟、且要求确有进展或 +确已停滞才叫——纯粹的噪声播报不如不叫。实时进度看输入框上方的作业状态条(零推理成本), +中途唤醒解决的是"智能体自己该不该介入"。 +""" + +from __future__ import annotations + +import logging +from typing import Any, Dict, Optional + +from sqlalchemy.orm.attributes import flag_modified + +from core.db.engine import SessionLocal +from core.db.models import Job + +logger = logging.getLogger(__name__) + + +def _wake_prompt(job: Job, stats: Dict[str, Any]) -> str: + """写给智能体自己的续跑指令 —— 只给事实与下一步,不替它下结论。""" + remaining = int(stats.get("remaining", 0)) + lines = [ + f"[系统] 你先前提交的批量作业「{job.name or job.job_id}」已结束。", + f"作业 ID:{job.job_id} 状态:{job.status}", + ( + f"台账统计:总计 {stats.get('total', 0)} 项,已完成 {stats.get('done', 0)}," + f"查无 {stats.get('not_found', 0)},失败 {stats.get('failed', 0)}," + f"待办 {stats.get('pending', 0)}。" + ), + ] + if job.error_message: + lines.append(f"作业错误信息:{job.error_message}") + lines.append("") + if remaining > 0: + lines.append( + f"仍有 {remaining} 项未结算。请先判断是环境故障还是脚本问题:可以用 " + f"run_job(action='resume', job_id='{job.job_id}') 断点续跑(已完成的项不会重做)," + "也可以改脚本后再 resume。**不要**把未完成当作完成交付。" + ) + else: + lines.append( + "全部工作项已结算。请读取作业结果完成最终交付:产出用户要的文件," + "并如实报告覆盖率(分母、完成数、查无数)与未覆盖清单。" + ) + lines.append("查看明细:run_job(action='status', job_id='%s')。" % job.job_id) + return "\n".join(lines) + + +def _progress_prompt(job: Job, stats: Dict[str, Any], budget_left: Dict[str, Any], stalled: bool) -> str: + """进度播报 —— 只让智能体转述现状,**不要**让它顺手再干点什么。 + + 这里的措辞是刻意的:中途唤醒的每一次都是一轮真实推理,如果不把边界写死,智能体 + 很容易"顺手"再提交一个作业或把台账明细读进对话,既烧钱又污染上下文。 + """ + total = int(stats.get("total", 0)) + settled = int(stats.get("settled", 0)) + pct = int(settled * 100 / total) if total else 0 + lines = [ + f"[系统] 进度播报:你提交的批量作业「{job.name or job.job_id}」仍在后台运行。", + f"作业 ID:{job.job_id}", + ( + f"台账:共 {total} 项,已结算 {settled}({pct}%)——完成 {stats.get('done', 0)}," + f"查无 {stats.get('not_found', 0)},失败 {stats.get('failed', 0)}," + f"处理中 {stats.get('running', 0)},待办 {stats.get('pending', 0)}。" + ), + ( + f"预算余量:调用 {budget_left.get('calls_left', 0)}," + f"墙钟 {int(budget_left.get('seconds_left', 0) / 60)} 分钟。" + ), + "", + ] + if stalled: + lines.append( + "⚠️ 距上次播报**没有任何新的结算**。请判断是不是卡住了:" + f"可以 run_job(action='status', job_id='{job.job_id}') 看明细," + "确认是环境故障还是脚本缺陷;确实跑不动就 cancel 掉改脚本,别干等。" + ) + else: + lines.append( + "作业推进正常。**请只用一两句话把上面的进度转述给用户**,然后结束本轮回复。" + ) + lines.append( + "本轮的硬性边界:不要重复提交作业(它还在跑),不要 export 台账," + "不要把逐项结果读进对话——作业跑完会再叫你一次,那时才做交付。" + ) + return "\n".join(lines) + + +async def wake_on_job_progress( + job_id: str, *, stats: Dict[str, Any], budget_left: Dict[str, Any], stalled: bool +) -> bool: + """作业运行途中播报一次进度。返回是否真的发起了唤醒。 + + 三道闸:作业得还在跑、得是 ``wait=False`` 提交的、会话里不能已有在跑的 run。 + 最后一道最重要——智能体正忙时再入队一轮,只会让两轮互相打架。 + """ + with SessionLocal() as db: + job = db.query(Job).filter(Job.job_id == job_id).first() + if job is None or not job.chat_id: + return False + if job.status != "running": + return False + meta = dict(job.extra_data or {}) + if not dict(meta.get("start_params") or {}).get("wake_on_finish"): + return False # wait=True 的调用方本来就阻塞在那儿等,不需要播报 + chat_id = job.chat_id + user_id = job.user_id + model_name = dict(meta.get("start_params") or {}).get("model_name") + prompt = _progress_prompt(job, stats, budget_left, stalled) + + try: + from orchestration.chat_run_executor import get_active_run_for_chat + + if get_active_run_for_chat(chat_id, user_id) is not None: + logger.info("[job-wake] skip progress wake, chat busy chat=%s job=%s", chat_id, job_id) + return False + except Exception: # noqa: BLE001 —— 探测失败就当它不忙,宁可多叫一次也别哑掉 + pass + + try: + await _enqueue_followup_run( + chat_id=chat_id, user_id=user_id, message=prompt, model_name=model_name + ) + except Exception as exc: # noqa: BLE001 + logger.warning("[job-wake] progress enqueue failed job=%s: %s", job_id, exc) + return False + logger.info("[job-wake] progress woke chat=%s job=%s stalled=%s", chat_id, job_id, stalled) + return True + + +async def wake_on_job_finish(job_id: str) -> bool: + """作业终态后叫醒会话。返回是否真的发起了唤醒。""" + with SessionLocal() as db: + job = db.query(Job).filter(Job.job_id == job_id).first() + if job is None or not job.chat_id: + return False + if job.status not in ("completed", "failed", "cancelled"): + return False + meta = dict(job.extra_data or {}) + if meta.get("woken_at"): + return False # 已经叫过了 + start_params = dict(meta.get("start_params") or {}) + if not start_params.get("wake_on_finish"): + return False # 只有 wait=False 提交的作业才需要唤醒 + chat_id = job.chat_id + user_id = job.user_id + model_name = start_params.get("model_name") + + from core.services.job_service import JobService + + stats = JobService(db).stats(job_id) + prompt = _wake_prompt(job, stats) + + meta["woken_at"] = True + job.extra_data = meta + flag_modified(job, "extra_data") + db.commit() + + try: + await _enqueue_followup_run( + chat_id=chat_id, user_id=user_id, message=prompt, model_name=model_name + ) + except Exception as exc: # noqa: BLE001 —— 唤醒失败不该反过来影响作业本身的终态 + logger.warning("[job-wake] enqueue failed job=%s: %s", job_id, exc) + return False + logger.info("[job-wake] woke chat=%s for job=%s", chat_id, job_id) + return True + + +async def _enqueue_followup_run( + *, chat_id: str, user_id: str, message: str, model_name: Optional[str] +) -> None: + """在既有会话里入队一条 chat run —— 与用户手动发一条消息走的是同一条路。 + + 这样前端不用任何改动:它本来就会在会话里看到新的一轮(断线还能按 run_id 续播)。 + """ + from api.routes.v1.chats import _load_session_messages + from core.chat.context import build_runtime_context + from core.config.catalog_resolver import resolve_all_runtime_enabled + from core.services.chat_service import ChatService + from orchestration import chat_run_executor + + with SessionLocal() as db: + chat_service = ChatService(db) + session_messages = _load_session_messages(chat_service, chat_id, user_id) + skills, agents, mcps = resolve_all_runtime_enabled(db, user_id) + # 唤醒消息按 user 角色落库:它要能进历史、能被模型看见,走和普通消息完全一样的链路 + chat_service.add_message(chat_id=chat_id, role="user", content=message) + + session_messages.append({"role": "user", "content": message}) + context = build_runtime_context( + model_name=model_name, + user_id=user_id, + chat_id=chat_id, + enabled_skills=skills, + enabled_agents=agents, + enabled_mcps=mcps, + ) + await chat_run_executor.start_run( + chat_id=chat_id, + user_id=user_id, + session_messages=session_messages, + effective_user_message=message, + raw_user_message=message, + context=context, + request_payload={"chat_id": chat_id, "message": message, "kind": "job_wakeup"}, + model_name=model_name, + ) diff --git a/src/backend/orchestration/workflow.py b/src/backend/orchestration/workflow.py index d638e8af..1a1fbb31 100644 --- a/src/backend/orchestration/workflow.py +++ b/src/backend/orchestration/workflow.py @@ -1263,6 +1263,7 @@ async def _run(): invoked_mcp_ids=[m for m in (context.get("mcp_ids") or []) if isinstance(m, str)], memory_enabled=_workflow_mem_enabled, batch_mode=_workflow_batch_chat if _direct_user_agent is None else False, + workflow_mode=bool(context.get("workflow_chat", False)), user_agent=_direct_user_agent, read_only=_direct_read_only, allow_bash=_direct_allow_bash, @@ -2476,6 +2477,7 @@ async def astream_chat_workflow( visible_subagents=_visible_subagents if _visible_subagents else None, plan_mode=_plan_chat, batch_mode=_batch_chat, + workflow_mode=bool(context.get("workflow_chat", False)), chat_id=context.get("chat_id"), project_ctx=_extract_project_ctx(context), channel_origin=context.get("channel_origin"), diff --git a/src/backend/prompts/prompt_text/code_exec/system/00_sandbox_environment.system.md b/src/backend/prompts/prompt_text/code_exec/system/00_sandbox_environment.system.md index 5a209c15..ded7c4b2 100644 --- a/src/backend/prompts/prompt_text/code_exec/system/00_sandbox_environment.system.md +++ b/src/backend/prompts/prompt_text/code_exec/system/00_sandbox_environment.system.md @@ -1,7 +1,35 @@ ## 代码沙箱环境 -- 隔离的云端沙箱(**不是**用户本地):Debian 12 / Python 3.11 / Node.js / bash,起始目录 `/workspace/`。 -- **无网络**:不能联网、连数据库或调外部 API。爬虫/在线请求类任务如实告知并给替代方案。 -- 资源:内存 256MB、CPU 1 核、单文件 ≤50MB、命令超时默认 60s / 最大 120s。数据大就分块或采样。 -- 预装免装:pandas、numpy、matplotlib、seaborn、scipy、openpyxl、xlsxwriter;缺库时不要尝试联网安装,改用预装库、标准库或纯本地实现。 -- 不可用:网络请求、数据库、GPU(torch/CUDA)、交互输入 `input()`、GUI(Tk/Qt)。 +隔离的云端沙箱(**不是**用户本地),Ubuntu + bash,起始目录 `/workspace/`。同一会话内文件持久, +下一条命令还能读到上一条写的文件。 + +### 跑 Python:一律用 `$PY_BIN` + +`$PY_BIN`(即 `/opt/python/current/bin/python3`)是装好全套依赖的解释器: + +- 数据/表格:pandas、numpy、scipy、openpyxl、xlsxwriter +- 文档:python-docx、python-pptx、pypdf / PyPDF2、PyMuPDF(fitz) +- 网络/解析:requests、httpx、beautifulsoup4、lxml +- 绘图/图像:matplotlib、Pillow +- 以及全部技能依赖 + +**裸 `python3` 是系统精简解释器,上面这些一个都没有。** 用它必然 ModuleNotFoundError —— +遇到这种报错**不要去装库、不要建虚拟环境**,把命令换成 `$PY_BIN` 即可。 + +确实缺某个库时才装:`$PY_BIN -m pip install `。 + +### 可以联网 + +沙箱能访问公网:抓网页、调公开 API、装依赖都可以直接做。 + +### 其它已装命令 + +`git` / `curl` / `wget` / `node` / `npm` / `uv` / `zip` / `unzip` / `libreoffice` / `pandoc`。 +(没有 `jq`、`ffmpeg`、`sqlite3` 命令行;JSON 用 `$PY_BIN` 处理,SQLite 用 Python 的 `sqlite3` 模块。) + +### 资源与限制 + +- **CPU 限 1 核**——注意 `nproc` 会报宿主的核数,别据此开多进程并行,不会更快。 +- 内存约 2GB:数据大就分块处理或采样,别整表读进内存。 +- 单条 bash 命令超时:默认 60s、最大 120s。**长任务不要挂在一条命令上**,用后台进程或批量作业。 +- 不可用:GPU(torch/CUDA)、交互输入 `input()`、GUI(Tk/Qt)。 diff --git a/src/backend/prompts/prompt_text/code_exec/system/20_execution_guidelines.system.md b/src/backend/prompts/prompt_text/code_exec/system/20_execution_guidelines.system.md index 99661d88..89cb5ebc 100644 --- a/src/backend/prompts/prompt_text/code_exec/system/20_execution_guidelines.system.md +++ b/src/backend/prompts/prompt_text/code_exec/system/20_execution_guidelines.system.md @@ -2,7 +2,7 @@ - **何时用代码**:数学/统计/数值模拟、数据处理(CSV/JSON/Excel)、算法验证、画图、生成 HTML、用户明确要求跑代码。简单算术或纯知识问答不用。 - **工作流**:理解需求 → 简述方案 → 写**完整可独立运行**的脚本(含全部 import,不依赖上一轮状态)→ 解释结果 → 出错读 stderr 修正重试(最多 2 次)。 -- **编写规范**:`print()` 输出关键结果;输出/注释/图表用中文;可能失败处加 try/except;注意 256MB(大数据分块或采样)。 +- **编写规范**:`print()` 输出关键结果;输出/注释/图表用中文;可能失败处加 try/except;大数据分块或采样(资源上限见「代码沙箱环境」)。 - **可视化**:matplotlib/seaborn 直接用;代码开头设中文字体: ```python import matplotlib.pyplot as plt diff --git a/src/backend/prompts/prompt_text/default/system/10_constraints.system.md b/src/backend/prompts/prompt_text/default/system/10_constraints.system.md index 8b3714f3..e24b0fe4 100644 --- a/src/backend/prompts/prompt_text/default/system/10_constraints.system.md +++ b/src/backend/prompts/prompt_text/default/system/10_constraints.system.md @@ -2,7 +2,12 @@ 1. **空结果如实声明**:工具无结果时必须告知"当前知识库/数仓中未查询到关于【主题】的相关内容",禁止用预训练知识补全。 2. **禁止模糊填补**:禁止"通常…""据了解…""一般情况下…""可能是…"等表述替代实际数据。 -3. **结论须有数据支撑**:趋势、比较、排名、原因等分析结论,必须建立在工具返回数据之上。无数据则不下结论。 -4. **文件不存在即说明**:知识库未返回某文件/报告,禁止根据文件名推测或生成内容。 -5. **范围外不推断**:MCP 工具和 Agent Skills 都不支持的产业/指标,说明未覆盖并建议替代渠道,不编造数据。**声明"无法提供"之前,必须确认 MCP 工具和 Agent Skills 都无法满足(参见"工具与技能使用策略")。** -6. **数据缺失不插值**:缺少某年份数据时只基于已有年份作答,明确标注缺失,不插值补全。 +3. **结论须有数据支撑**:趋势、比较、排名、原因等分析结论必须建立在工具返回数据之上,无数据不下结论。 +4. **文件不存在即说明**:知识库未返回某文件/报告时,禁止按文件名推测或生成内容。 +5. **范围外不推断**:MCP 工具和 Agent Skills 都不覆盖的产业/指标,说明未覆盖并建议替代渠道,不编造数据;声明"无法提供"之前必须确认工具与技能均无法满足。 +6. **数据缺失不插值**:缺某年份数据时只基于已有年份作答并明确标注缺失,不插值补全。 + +## 禁止自我截断 + +7. **不得以成本为由提前收工**:禁止用"边际收益递减""消耗较大""工作量太大""单轮对话无法覆盖"等理由在任务只完成一部分时就转向交付。完成与否由**可判定的事实**决定(台账/清单里还剩哪几项),不由你对剩余成本的估计决定。 +8. **未完成必须给出清单与续跑方式**:确实无法一次做完时,如实说明**分母、已完成数、未完成清单**,并给出下一步怎么继续(如 `run_job action=resume`)。把未完成说成完成、或用占位内容填满交付物,都属严重错误。 diff --git a/src/backend/tests/orchestration/test_job_conflict_policy.py b/src/backend/tests/orchestration/test_job_conflict_policy.py new file mode 100644 index 00000000..640eafed --- /dev/null +++ b/src/backend/tests/orchestration/test_job_conflict_policy.py @@ -0,0 +1,166 @@ +"""同会话重复作业的冲突策略 —— 拦下必须是"默认",不能是"禁令"。 + +背景:一次事故里智能体被进度唤醒后改了脚本、又交了一份新作业,两份并存同时烧预算、 +同时叫醒会话,状态条上也分不清哪份算数。于是加了拦截。 + +但拦死是另一种错:确实要换新脚本重跑时,工具必须给得出路,而不是让调用方无路可走。 +所以这里锁的是三条分支都通:默认拦下并给出路、replace 先停旧再跑新、parallel 放行。 +""" + +import asyncio +import json + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import StaticPool + +import core.db.engine as db_engine +from core.db.engine import Base +from core.db.models import Job + + +@pytest.fixture() +def db_session(monkeypatch): + engine = create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + Base.metadata.create_all(engine) + Session = sessionmaker(bind=engine) + monkeypatch.setattr(db_engine, "SessionLocal", Session) + import core.llm.tools.job_tool as jt + + monkeypatch.setattr(jt, "SessionLocal", Session, raising=False) + return Session + + +class _Toolkit: + """够用的假 toolkit:只把注册进来的函数抓出来直接调。""" + + def __init__(self): + self.fn = None + + def register_tool_function(self, fn): + self.fn = fn + + +def _make_tool(monkeypatch, db_session, cancelled: list): + from core.llm.tools import job_tool + + # 真正启动作业的部分全部打桩:这里只验冲突策略,不碰沙箱 + async def fake_cancel(job_id, *, user_id): + cancelled.append(job_id) + with db_session() as db: + row = db.query(Job).filter(Job.job_id == job_id).first() + if row: + row.status = "cancelled" + db.commit() + return True + + async def fake_sbx_bash(cmd, *, session_id, user_id, timeout=60): + import base64 + + return 0, base64.b64encode(b"print(1)").decode(), "" + + async def fake_start(**kw): + return "job_new" + + from orchestration import job_runtime + + monkeypatch.setattr(job_runtime, "cancel_job", fake_cancel) + monkeypatch.setattr(job_runtime, "_sbx_bash", fake_sbx_bash) + monkeypatch.setattr(job_runtime, "start_job", fake_start) + monkeypatch.setattr(job_runtime, "spawn_background", lambda *a, **k: None) + + tk = _Toolkit() + job_tool.register_run_job( + tk, + user_id="u1", + chat_id="chat_1", + sandbox_session_id="sbx_1", + allowed_tools=["internet_search"], + model_name="m", + model_provider_id="p", + ) + return tk.fn + + +def _seed_live_job(db_session, job_id="job_old", status="running"): + with db_session() as db: + db.add( + Job( + job_id=job_id, + user_id="u1", + chat_id="chat_1", + name="旧作业", + status=status, + script_path="/w/a.py", + script_text="x", + sandbox_session_id="sbx_1", + ) + ) + db.commit() + + +def _payload(resp): + """把 ToolResponse 的文本块拼回 JSON —— 块可能是 dict 也可能是带 .text 的对象。""" + text = "" + for blk in getattr(resp, "content", []) or []: + text += blk.get("text", "") if isinstance(blk, dict) else str(getattr(blk, "text", "") or "") + assert text, f"工具没有返回文本内容: {resp!r}" + return json.loads(text) + + +def test_default_blocks_but_hands_back_every_exit(monkeypatch, db_session): + """默认拦下时必须把出路说全 —— 只说"不行"等于把调用方逼进死胡同。""" + cancelled: list = [] + fn = _make_tool(monkeypatch, db_session, cancelled) + _seed_live_job(db_session) + + out = _payload(asyncio.run(fn(action="start", script_path="/w/a.py", name="新作业"))) + + assert out["ok"] is False + assert out["job_id"] == "job_old" + assert "replace" in out["error"] and "resume" in out["error"] and "parallel" in out["error"] + assert cancelled == [], "默认分支不该动旧作业" + + +def test_replace_cancels_old_then_starts(monkeypatch, db_session): + """换了脚本要重跑 —— 这条路必须真的通,且旧作业确实被停掉。""" + cancelled: list = [] + fn = _make_tool(monkeypatch, db_session, cancelled) + _seed_live_job(db_session) + + out = _payload( + asyncio.run( + fn(action="start", script_path="/w/a.py", name="新作业", wait=False, on_conflict="replace") + ) + ) + + assert out["ok"] is True and out["job_id"] == "job_new" + assert cancelled == ["job_old"], "replace 必须先停掉旧作业,否则又是两份并存" + + +def test_parallel_allows_coexistence(monkeypatch, db_session): + cancelled: list = [] + fn = _make_tool(monkeypatch, db_session, cancelled) + _seed_live_job(db_session) + + out = _payload( + asyncio.run( + fn(action="start", script_path="/w/a.py", name="新作业", wait=False, on_conflict="parallel") + ) + ) + + assert out["ok"] is True + assert cancelled == [], "parallel 明确表示两份都要,不许偷偷停掉一份" + + +def test_no_live_job_starts_normally(monkeypatch, db_session): + """没有在跑的作业时,冲突策略不该有任何存在感。""" + cancelled: list = [] + fn = _make_tool(monkeypatch, db_session, cancelled) + _seed_live_job(db_session, status="completed") + + out = _payload(asyncio.run(fn(action="start", script_path="/w/a.py", wait=False))) + assert out["ok"] is True and cancelled == [] diff --git a/src/backend/tests/orchestration/test_job_orphan_reaper.py b/src/backend/tests/orchestration/test_job_orphan_reaper.py new file mode 100644 index 00000000..98e8ab81 --- /dev/null +++ b/src/backend/tests/orchestration/test_job_orphan_reaper.py @@ -0,0 +1,205 @@ +"""失联作业的对账 + 回调地址探测 + 时间戳时区 —— 三条都对应线上实测到的故障。 + +事故还原(HugAgentOS 测试机):一条批量作业提交后,沙箱里的 runner 第一发回调就 +``Name or service not known`` 当场死亡——默认回调基址写死了 ``host.docker.internal``, +而那台机器上沙箱与后端同在一张 docker 网络、宿主别名根本解析不了。后果是三重的: + +1. 作业永远停在 ``pending``:驱动挂在 ``run_job(wait=True)`` 的工具调用里,用户一中止 + 这轮对话驱动就被取消,``drive()`` 里的全部护栏跟着消失,没有任何东西再来收尸; +2. 台账一条都没有,状态条只剩一个转圈的菊花,看不出"到底在干什么"; +3. 状态条上显示「已运行 8 小时 4 分」——作业其实刚提交 4 分钟,8 小时正是容器 + ``TZ=Asia/Shanghai`` 与 naive UTC 时间戳之间的时差。 + +所以这里锁三件事:孤儿一定会被收、回调不通一定当场拒绝启动、时间戳一定带时区。 +""" + +import asyncio +from datetime import datetime, timedelta, timezone + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import StaticPool + +from core.db.engine import Base +from core.db.models import Job +import orchestration.job_runtime as jr + + +@pytest.fixture() +def db_session(monkeypatch): + engine = create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + Base.metadata.create_all(engine) + Session = sessionmaker(bind=engine) + monkeypatch.setattr(jr, "SessionLocal", Session) + monkeypatch.setattr(jr, "_active_jobs", {}, raising=False) + + async def _no_wake(job_row_id): + return None + + monkeypatch.setattr(jr, "_maybe_wake", _no_wake) + return Session + + +def _seed(Session, job_id: str, *, status: str, quiet_min: float, token: str = "tok") -> None: + stale = datetime.now(timezone.utc) - timedelta(minutes=quiet_min) + with Session() as db: + db.add( + Job( + job_id=job_id, + user_id="u1", + chat_id="c1", + status=status, + created_at=stale, + updated_at=stale, + extra_data={"token": token}, + ) + ) + db.commit() + + +def _status(Session, job_id: str) -> str: + with Session() as db: + return str(db.query(Job).filter(Job.job_id == job_id).first().status) + + +# ── 孤儿对账 ──────────────────────────────────────────────────────────── + + +def test_pending_job_without_driver_is_reaped(db_session): + """runner 没起来的 pending 作业:过短闸即判失联,token 一并作废。""" + _seed(db_session, "job_dead", status="pending", quiet_min=10) + + assert asyncio.run(jr.reap_orphan_jobs()) == 1 + assert _status(db_session, "job_dead") == "interrupted" + with db_session() as db: + row = db.query(Job).filter(Job.job_id == "job_dead").first() + assert "token" not in (row.extra_data or {}) # 残留进程回来也写不动了 + assert "失联" in (row.error_message or "") + assert "resume" in (row.error_message or "") # 错误信息必须自带下一步 + + +def test_recent_pending_job_is_left_alone(db_session): + """刚提交的作业不能被误杀 —— 沙箱冷启动本来就要花点时间。""" + _seed(db_session, "job_young", status="pending", quiet_min=1) + + assert asyncio.run(jr.reap_orphan_jobs()) == 0 + assert _status(db_session, "job_young") == "pending" + + +def test_running_job_uses_the_longer_silence_window(db_session): + """running 用与驱动同一把静默闸:单项耗时很长但确实在跑的作业不该被收。""" + _seed(db_session, "job_slow", status="running", quiet_min=8) + assert asyncio.run(jr.reap_orphan_jobs()) == 0 + assert _status(db_session, "job_slow") == "running" + + _seed(db_session, "job_silent", status="running", quiet_min=20) + assert asyncio.run(jr.reap_orphan_jobs()) == 1 + assert _status(db_session, "job_silent") == "interrupted" + + +def test_job_driven_in_this_process_is_never_reaped(db_session, monkeypatch): + """本进程还在驱动的作业归 drive() 管,对账绝不能插手(护栏重复 = 误杀)。""" + _seed(db_session, "job_live", status="pending", quiet_min=30) + + async def _run(): + task = asyncio.create_task(asyncio.sleep(5)) + jr._active_jobs["job_live"] = task + try: + return await jr.reap_orphan_jobs() + finally: + task.cancel() + + assert asyncio.run(_run()) == 0 + assert _status(db_session, "job_live") == "pending" + + +# ── 回调地址探测 ──────────────────────────────────────────────────────── + + +def test_callback_candidates_prefer_same_network_service_name(monkeypatch): + monkeypatch.delenv("JOB_CALLBACK_URL", raising=False) + monkeypatch.setenv("PORT", "8011") + + candidates = jr.callback_base_candidates() + + assert candidates[0] == "http://backend:8011" + # 宿主别名仍在表里兜底,但不再是唯一选项(写死它正是这次事故的根因) + assert any("host.docker.internal" in c for c in candidates) + + +def test_explicit_env_wins_and_skips_probing(monkeypatch): + monkeypatch.setenv("JOB_CALLBACK_URL", "http://custom:9000/api/") + + async def _boom(*a, **k): # 手动指定即信任,不该再去沙箱里探 + raise AssertionError("probe must not run when JOB_CALLBACK_URL is set") + + monkeypatch.setattr(jr, "_sbx_bash", _boom) + + assert asyncio.run(jr.resolve_callback_base(session_id="s", user_id="u")) == ( + "http://custom:9000/api" + ) + + +def test_probe_picks_the_reachable_base(monkeypatch): + monkeypatch.delenv("JOB_CALLBACK_URL", raising=False) + monkeypatch.setattr(jr, "_resolved_callback_base", None, raising=False) + + async def fake_bash(cmd, *, session_id, user_id, timeout=60): + return 0, "PICK http://backend:8011\n", "" + + monkeypatch.setattr(jr, "_sbx_bash", fake_bash) + + assert asyncio.run(jr.resolve_callback_base(session_id="s", user_id="u")) == ( + "http://backend:8011" + ) + + +def test_unreachable_callback_refuses_to_start(monkeypatch): + """一个都不通就必须当场失败。 + + "启动成功但永远没有进度"是最贵的失败形态:用户会等上几个小时才发现什么都没发生。 + """ + monkeypatch.delenv("JOB_CALLBACK_URL", raising=False) + monkeypatch.setattr(jr, "_resolved_callback_base", None, raising=False) + + async def fake_bash(cmd, *, session_id, user_id, timeout=60): + return 0, "NONE\n", "" + + monkeypatch.setattr(jr, "_sbx_bash", fake_bash) + + with pytest.raises(RuntimeError) as exc: + asyncio.run(jr.resolve_callback_base(session_id="s", user_id="u")) + assert "JOB_CALLBACK_URL" in str(exc.value) # 报错要带修法 + + +# ── 时间戳时区 ────────────────────────────────────────────────────────── + + +def test_job_timestamp_defaults_are_timezone_aware(): + """naive UTC 写进 timestamptz 会被按会话时区解释 —— 容器是 +08,作业一落库就"早 8 小时"。 + + 断言落在**默认值本身**上而不是落库后读回的值:SQLite 没有带时区的存储类型,读回一律 + 是 naive,用它做判据等于什么都没锁。真正决定行为的是写进去的那个值带不带 tzinfo。 + """ + from core.db.models.job import _utcnow + + now = _utcnow() + assert now.tzinfo is not None + assert abs((now - datetime.now(timezone.utc)).total_seconds()) < 60 + + for table, columns in ( + (Job.__table__, ("created_at", "updated_at")), + (Job.__table__.metadata.tables["job_items"], ("updated_at",)), + (Job.__table__.metadata.tables["job_calls"], ("created_at",)), + ): + for name in columns: + col = table.c[name] + assert col.default is not None, name + # 比对行为而不是函数身份:models 包在不同 import 路径下会有各自的模块实例, + # `is` 比较会假阴性。真正要锁的是"默认值算出来带 tzinfo"。 + assert col.default.arg(None).tzinfo is not None, name + if col.onupdate is not None: + assert col.onupdate.arg(None).tzinfo is not None, name diff --git a/src/backend/tests/orchestration/test_job_resilience.py b/src/backend/tests/orchestration/test_job_resilience.py new file mode 100644 index 00000000..89de7278 --- /dev/null +++ b/src/backend/tests/orchestration/test_job_resilience.py @@ -0,0 +1,191 @@ +"""作业韧性回归 —— 对应一次"跑满一小时、进度停在 0、还自己复制出第二个作业"的事故。 + +事故链条(每一环都不报错,这是它最贵的地方): + +1. 脚本直接 ``job.map`` 没建台账 → 回写全部打到不存在的行,被静默丢弃 → 进度恒为 0; +2. 脚本传 ``item_key=it["seq"]``(int),而请求体只声明了 ``str`` → FastAPI **422**, + 模型一次都没被调用; +3. 每项 3ms 失败 × 568 项 × 并发 8 → 回调打爆网关限流 → **429**; +4. SDK 把 4xx 一律当"契约错误不重试",而 429 恰好是 4xx → 连 runner 上报终态那一发 + 也被拒 → 作业永远停在 ``running``; +5. 驱动只有 2 小时墙钟兜底,期间按间隔叫醒智能体报停滞,智能体于是又交了一份新作业。 + +这里逐条锁住修复后的行为。 +""" + +import json +import re +from types import SimpleNamespace + +import pytest + +from api.routes.v1.internal_jobs import AgentBody, LedgerBody +from core.chat.tool_log import _payload_carries_error +from orchestration.job_runtime import SDK_SOURCE, _final_from_marker + + +# ── ② 主键类型:int 必须能进门 ────────────────────────────────────── + + +@pytest.mark.parametrize("raw, expected", [(7, "7"), ("7", "7"), (0, "0"), (None, None), ("", None)]) +def test_item_key_accepts_int(raw, expected): + """业务主键十有八九是行号——只收 str 等于把整轮作业挡在门外。""" + assert AgentBody(prompt="x", item_key=raw).item_key == expected + + +def test_ledger_key_accepts_int(): + assert LedgerBody(op="update", key=42).key == 42 + + +# ── ④ 429 是限流不是契约错误 ──────────────────────────────────────── + + +class _FakeHTTPError(Exception): + def __init__(self, code): + self.code = code + + def read(self): + return b"rate limited" + + +def _sdk_ns(responses): + """exec 出 SDK 命名空间,把 urllib 整个换成按剧本回放的假模块。 + + 注意必须换成**独立的假对象**,不能去改 ns["urllib"]——那是真模块,改了会污染 + 整个进程里其他用到 urllib 的测试。 + """ + ns: dict = {} + exec(compile(SDK_SOURCE, "", "exec"), ns) + ns["BASE"] = "http://callback.test/api" + ns["JOB_ID"] = "job_test" + ns["time"] = SimpleNamespace(sleep=lambda *_a, **_k: None) # 别在测试里真等退避 + calls = {"n": 0} + + class Resp: + def __enter__(self): + return self + + def __exit__(self, *a): + return False + + def read(self): + return json.dumps({"data": {"ok": True}}).encode() + + def fake_urlopen(req, timeout=None): + i = calls["n"] + calls["n"] += 1 + item = responses[min(i, len(responses) - 1)] + if isinstance(item, int): + raise _FakeHTTPError(item) + return Resp() + + ns["urllib"] = SimpleNamespace( + request=SimpleNamespace(urlopen=fake_urlopen, Request=lambda *a, **k: SimpleNamespace( + add_header=lambda *_a, **_k: None + )), + error=SimpleNamespace(HTTPError=_FakeHTTPError), + ) + ns["_calls"] = calls + return ns + + +def test_429_is_retried_not_raised(): + """限流是"待会儿再来"。当成契约错误直接抛,曾让终态上报也一起丢掉。""" + ns = _sdk_ns([429, 429, "ok"]) + out = ns["_post"]("log", {"message": "hi"}) + assert out == {"ok": True} + assert ns["_calls"]["n"] == 3, "429 必须重试到成功" + + +def test_real_contract_errors_still_fail_fast(): + """400/422 是真写错了,重试毫无意义,必须立刻抛。""" + ns = _sdk_ns([422, "ok"]) + with pytest.raises(Exception) as ei: + ns["_post"]("agent", {}) + assert "422" in str(ei.value) + assert ns["_calls"]["n"] == 1, "契约错误不该重试" + + +# ── ① 打空台账必须被喊出来 ────────────────────────────────────────── + + +def test_update_warns_when_key_unknown(): + """后端说这个 key 不在台账里(多半漏了 seed)→ 必须留痕,不能静默丢。""" + ns: dict = {} + exec(compile(SDK_SOURCE, "", "exec"), ns) + logged = [] + + def fake_post(path, payload, timeout=180): + if path == "log": + logged.append(str(payload.get("message") or "")) + return {} + return {"ok": False, "known_key": False} + + ns["_post"] = fake_post + ns["ledger"].update("42", status="done", result={}) + + assert any("漏了 ledger.seed" in m for m in logged), "打空台账必须告警" + + +def test_map_surfaces_first_failure(): + """异常隔离不等于吞掉:整批全挂时,日志里必须看得见第一个原因。""" + ns: dict = {} + exec(compile(SDK_SOURCE, "", "exec"), ns) + logged = [] + + def fake_post(path, payload, timeout=180): + if path == "log": + logged.append(str(payload.get("message") or "")) + return {"ok": True, "known_key": True} + + ns["_post"] = fake_post + + def boom(it): + raise RuntimeError("HTTP 422 item_key") + + ns["job"].map([{"seq": i} for i in range(3)], boom) + assert any("首个失败项" in m and "422" in m for m in logged) + + +# ── ⑤ runner 死了要能就地判终态 ───────────────────────────────────── + + +def test_final_from_marker_reads_落盘终态(): + tail = 'noise\n{"status": "failed", "error": "boom"}\nmore noise' + assert _final_from_marker(tail) == ("failed", "boom") + + +def test_final_from_marker_defaults_to_failed(): + """捡不到标记就按 failed —— 进程没了而作业还 running,本来就不是正常收尾。""" + assert _final_from_marker("just a traceback")[0] == "failed" + assert _final_from_marker("")[0] == "failed" + + +# ── 观测面:载荷里写着 error 就不能记成 success ────────────────────── + + +@pytest.mark.parametrize( + "payload, expected", + [ + ({"error": "internet_search 调用失败: 429"}, True), + ('{"error": "boom", "result": []}', True), + ({"error": ""}, False), + ({"result": ["ok"]}, False), + ("一篇讲 error 处理的网页正文", False), + (["block"], False), + # MCP 工具真正的返回形状:内容块里裹着 JSON 文本,必须穿透 + ([{"type": "text", "text": '{"error": "internet_search 调用失败: 429", "result": []}'}], True), + ([{"type": "text", "text": '{"result": [{"title": "错误码 429 是什么"}]}'}], False), + ([{"type": "text", "text": "普通正文,没有 JSON"}], False), + ], +) +def test_error_payload_detection(payload, expected): + assert _payload_carries_error(payload) is expected + + +# ── SDK 说明必须跟着代码走(模型只看得到这一份)──────────────────── + + +def test_sdk_documents_rate_limit_behaviour(): + m = re.search(r"# 4xx 是契约问题.*?\n(.*?\n){0,3}", SDK_SOURCE) + assert m and "429" in m.group(0), "429 的例外必须写在 SDK 源码注释里" diff --git a/src/backend/tests/orchestration/test_job_sdk_ledger.py b/src/backend/tests/orchestration/test_job_sdk_ledger.py new file mode 100644 index 00000000..98311f92 --- /dev/null +++ b/src/backend/tests/orchestration/test_job_sdk_ledger.py @@ -0,0 +1,144 @@ +"""作业 SDK 的台账回写回归测试 —— 对应一次「跑了等于没跑」的线上事故。 + +事故经过:一个 568 项的补全作业跑了近一小时,子智能体调用真的在发(job_calls 有记录、 +也有成功的),但台账 568 项**全程 pending、attempts 全 0**,最后导出是空的、成果全丢。 + +根因在 ``job.map``:脚本按文档用 ``ledger.seed([{"key": ..., "payload": ...}])`` 播种, +却把**原始业务对象**(``{"seq": 7, "name": ...}``)交给 ``job.map``——两者形状不同是常态。 +当时 ``_key_of`` 只认 ``it["key"]``,取不到就返回 None,于是回写分支被**静默跳过**: +不报错、不打日志、外面完全看不出异常。 + +所以这里锁三件事: +1. 常见业务主键(seq/id/item_key)必须能兜住,绝不静默丢账; +2. 实在取不到主键时必须**喊出来**(log),因为静默丢账是最坏的失败模式; +3. 异常项要记 failed 且 bump_attempts,续跑才有依据。 + +SDK 是以字符串形式注入沙箱的(``SDK_SOURCE``),测试直接 exec 它,验的就是真正下发的那份代码。 +""" + +import re + +import pytest + +from orchestration.job_runtime import SDK_SOURCE + + +@pytest.fixture() +def sdk(): + """exec 出一份 SDK 命名空间,并把回调层换成记录器。 + + ``_Ledger``/``log`` 里的 ``_post`` 是模块级名字,exec 进同一个 dict 后替换即可生效 + (闭包在调用时才解析全局名)。 + """ + ns: dict = {} + exec(compile(SDK_SOURCE, "", "exec"), ns) + calls: list = [] + + def fake_post(path, payload, timeout=180): + calls.append((path, payload)) + if path == "ledger" and payload.get("op") == "seed": + return {"created": len(payload.get("items") or []), "skipped": 0} + return {} + + ns["_post"] = fake_post + ns["_calls"] = calls + return ns + + +def _updates(ns): + return [p for path, p in ns["_calls"] if path == "ledger" and p.get("op") == "update"] + + +def _logs(ns): + return [str(p.get("message") or "") for path, p in ns["_calls"] if path == "log"] + + +@pytest.mark.parametrize( + "item, expected_key", + [ + ({"key": "r2", "v": 1}, "r2"), # 文档里的标准形状 + ({"seq": 7, "v": 1}, "7"), # 事故现场的形状:seq 当主键 + ({"id": "ent-9", "v": 1}, "ent-9"), + ({"item_key": 42, "v": 1}, "42"), + ], +) +def test_map_books_result_under_business_key(sdk, item, expected_key): + """seed 与 map 的对象形状不一致是常态,主键必须能兜住——否则成果静默蒸发。""" + sdk["job"].map([item], lambda it: {"ok": True}) + + ups = _updates(sdk) + assert len(ups) == 1, "每项都必须落一次账" + assert ups[0]["key"] == expected_key + assert ups[0]["status"] == "done" + assert ups[0]["result"] == {"ok": True} + + +def test_map_honors_status_override(sdk): + """`_status` 是脚本声明"查无"的唯一正道,不能被当成结果字段写进数据位。""" + sdk["job"].map([{"seq": 1}], lambda it: {"_status": "not_found", "core": ""}) + + ups = _updates(sdk) + assert ups[0]["status"] == "not_found" + assert "_status" not in ups[0]["result"] + + +def test_map_accepts_explicit_key_field_and_callable(sdk): + sdk["job"].map([{"编号": "x1"}], lambda it: {"ok": 1}, key="编号") + assert _updates(sdk)[0]["key"] == "x1" + + sdk["_calls"].clear() + sdk["job"].map([{"a": 5}], lambda it: {"ok": 1}, key=lambda it: f"k{it['a']}") + assert _updates(sdk)[0]["key"] == "k5" + + +def test_map_warns_loudly_when_key_is_unresolvable(sdk): + """取不到主键时可以不落账,但**绝不允许安静**——静默丢账正是那次事故的形态。""" + sdk["job"].map([{"名称": "甲"}, {"名称": "乙"}], lambda it: {"ok": 1}) + + assert _updates(sdk) == [], "没有主键就不该瞎写台账" + warned = [m for m in _logs(sdk) if "取不到台账主键" in m] + assert len(warned) == 1, "必须告警,且只喊一次(别把日志刷爆)" + assert "job.map" in warned[0] + + +def test_map_records_failure_with_attempts(sdk): + """失败项要留下 failed + attempts,断点续跑才知道该重试谁。""" + + def boom(it): + raise RuntimeError("搜索工具 503") + + sdk["job"].map([{"seq": 3}], boom) + + ups = _updates(sdk) + assert len(ups) == 1 + assert ups[0]["status"] == "failed" + assert ups[0]["bump_attempts"] is True + assert "503" in ups[0]["error"] + + +def test_map_isolates_failures_across_items(sdk): + """一项炸掉不能拖垮其余项 —— 这是 map 的核心契约。""" + + def half(it): + if it["seq"] % 2 == 0: + raise RuntimeError("nope") + return {"ok": 1} + + sdk["job"].map([{"seq": i} for i in range(1, 5)], half, concurrency=2) + + ups = _updates(sdk) + assert len(ups) == 4 + assert sorted(u["status"] for u in ups) == ["done", "done", "failed", "failed"] + + +def test_map_returns_none_means_self_managed(sdk): + """fn 返回 None = 我自己写过账了,SDK 不得再插一脚。""" + sdk["job"].map([{"seq": 1}], lambda it: None) + assert _updates(sdk) == [] + + +def test_sdk_docstring_warns_about_key_shape(): + """SDK 是照着注入沙箱的,说明必须写在源码里 —— 模型只看得到这一份。""" + m = re.search(r"def map\(self.*?\"\"\"(.*?)\"\"\"", SDK_SOURCE, re.S) + assert m, "job.map 的 docstring 不该消失,它是模型唯一的使用说明" + assert "key" in m.group(1) diff --git a/src/backend/tests/orchestration/test_stale_reaper.py b/src/backend/tests/orchestration/test_stale_reaper.py index 241c1617..cb3a3885 100644 --- a/src/backend/tests/orchestration/test_stale_reaper.py +++ b/src/backend/tests/orchestration/test_stale_reaper.py @@ -172,6 +172,71 @@ async def test_quiet_over_age_run_with_live_task_survives(reaper_env, monkeypatc task.cancel() +def _insert_job(session_factory, job_id: str, *, chat_id: str, status: str, updated_age_sec: float): + from core.db.models import Job + + with session_factory() as db: + db.add( + Job( + job_id=job_id, + user_id="user_test", + chat_id=chat_id, + name="t", + status=status, + budget={}, + usage={}, + extra_data={}, + updated_at=datetime.now(timezone.utc) - timedelta(seconds=updated_age_sec), + ) + ) + db.commit() + + +async def test_quiet_orphan_with_live_job_survives(reaper_env): + """工作流模式:run 卡在 run_job(wait=True) 里等作业,主链路一个流事件都不产生。 + + 进程内 task 豁免只在本进程有效——多 worker / 多副本部署里另一个进程看不到这个 + task,会按"流静默"把健康的长作业误杀(实测踩过:双后端共库时主栈把测试后端的 + run 杀了)。作业活性是跨进程可见的证据,必须能救下这个 run。 + """ + session_factory, _ = reaper_env + _insert_run(session_factory, "run_job_wait", age_sec=executor._STALE_RUN_MAX_AGE_SEC + 300) + _insert_job( + session_factory, "job_live", chat_id="chat_test", status="running", updated_age_sec=5 + ) + + assert await executor.reap_stale_runs() == 0 + assert _get_run(session_factory, "run_job_wait").status == "running" + + +async def test_quiet_orphan_with_stalled_job_is_reaped(reaper_env): + """作业本身也不动了(updated_at 落在静默窗口之外)→ 不是活性证据,照常回收。""" + session_factory, _ = reaper_env + _insert_run(session_factory, "run_job_dead", age_sec=executor._STALE_RUN_MAX_AGE_SEC + 300) + _insert_job( + session_factory, + "job_stalled", + chat_id="chat_test", + status="running", + updated_age_sec=executor._STALE_QUIET_SEC + 120, + ) + + assert await executor.reap_stale_runs() == 1 + assert _get_run(session_factory, "run_job_dead").status == "failed" + + +async def test_terminal_job_does_not_shield_run(reaper_env): + """作业已终态 → 不再是活性证据,run 该回收就回收(防止豁免变成永生通行证)。""" + session_factory, _ = reaper_env + _insert_run(session_factory, "run_job_done", age_sec=executor._STALE_RUN_MAX_AGE_SEC + 300) + _insert_job( + session_factory, "job_done", chat_id="chat_test", status="completed", updated_age_sec=5 + ) + + assert await executor.reap_stale_runs() == 1 + assert _get_run(session_factory, "run_job_done").status == "failed" + + async def test_orphan_with_done_task_is_reaped(reaper_env, monkeypatch): """A finished task left in _active_runs does not shield the run: quiet orphans are reaped.""" session_factory, _ = reaper_env diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json new file mode 100644 index 00000000..6005ea9f --- /dev/null +++ b/src/frontend/package-lock.json @@ -0,0 +1,9485 @@ +{ + "name": "hugagent-ui", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hugagent-ui", + "version": "0.0.0", + "dependencies": { + "@ant-design/icons": "^6.1.0", + "@codemirror/autocomplete": "^6.20.1", + "@codemirror/commands": "^6.10.3", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/language": "^6.12.3", + "@codemirror/state": "^6.6.0", + "@codemirror/theme-one-dark": "^6.1.3", + "@codemirror/view": "^6.41.0", + "@univerjs/preset-sheets-core": "^0.19.0", + "@univerjs/preset-sheets-drawing": "^0.19.0", + "@univerjs/presets": "^0.19.0", + "antd": "^6.3.0", + "codemirror": "^6.0.2", + "docx-preview": "^0.3.7", + "highlight.js": "^11.11.1", + "html2pdf.js": "^0.14.0", + "katex": "^0.16.45", + "marked": "^17.0.2", + "mermaid": "^11.14.0", + "motion": "^12.38.0", + "opentype.js": "1.3.4", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "xlsx": "^0.18.5", + "zustand": "^5.0.11" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/katex": "^0.16.8", + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.48.0", + "vite": "^7.3.1" + } + }, + "node_modules/@ant-design/colors": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-8.0.1.tgz", + "integrity": "sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^3.0.0" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-2.1.2.tgz", + "integrity": "sha512-2Hy8BnCEH31xPeSLbhhB2ctCPXE2ZnASdi+KbSeS79BNbUhL9hAEe20SkUk+BR8aKTmqb6+FKFruk7w8z0VoRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "stylis": "^4.3.4" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-2.1.2.tgz", + "integrity": "sha512-5fTHQ158jJJ5dC/ECeyIdZUzKxE/mpEMRZxthyG1sw/AKRHKgJBg00Yi6ACVXgycdje7KahRNvNET/uBccwCnA==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^2.1.2", + "@babel/runtime": "^7.23.2", + "@rc-component/util": "^1.4.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@ant-design/fast-color": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz", + "integrity": "sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==", + "license": "MIT", + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.3.2.tgz", + "integrity": "sha512-B6O5a5XJ4wjtNOfZejXYwHW5zvKV5gYkjGf11dHGLEbKn0ABDGndo41+gfIiXyTFhvESj4XTotuud33mUFid0g==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^8.0.1", + "@ant-design/icons-svg": "^4.5.0", + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.5.0.tgz", + "integrity": "sha512-1BTUFyKPTBZ53MuTP8s0k5SFEXL7o3VHEOwLgzaoWKwnBeqIcqUtVshc4SKzhI6uACfqhJqBwBUE9FsWR3uULA==", + "license": "MIT" + }, + "node_modules/@ant-design/react-slick": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-2.0.0.tgz", + "integrity": "sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "clsx": "^2.1.1", + "json2mq": "^0.2.0", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", + "license": "MIT" + }, + "node_modules/@chevrotain/types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", + "license": "Apache-2.0" + }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", + "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.11.0.tgz", + "integrity": "sha512-/K4Rl5BN0OtTiPWmJCdqODu38XnDMsDxKY5rgrPnCkutPTJf2wVbkoixLfealF5Kwse/s8P8M5jAiURiwSwnFA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.7.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-python": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.2.1.tgz", + "integrity": "sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.3.2", + "@codemirror/language": "^6.8.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/python": "^1.1.4" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz", + "integrity": "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz", + "integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.42.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.1.tgz", + "integrity": "sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.1.tgz", + "integrity": "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/theme-one-dark": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.43.9", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.9.tgz", + "integrity": "sha512-sTuUzTpPMFebRhg6dawChoKKgndIwfjmJgKVxBefPElcU2NwQ6AFroupk0SFqEerQyZOGRfDNnSN8Dw/lMAsXw==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.7.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.0", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@flatten-js/interval-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.1.3.tgz", + "integrity": "sha512-xhFWUBoHJFF77cJO1D6REjdgJEMRf2Y2Z+eKEPav8evGKcLSnj1ud5pLXQSbGuxF3VSvT1rWhMfVpXEKJLTL+A==", + "license": "MIT" + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.4.tgz", + "integrity": "sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "import-meta-resolve": "^4.2.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lezer/common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", + "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", + "license": "MIT" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/python": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.19.tgz", + "integrity": "sha512-MhQIURHRytsNzP/YXnqpYKW6la6voAH3kyplTOOiCdjyFY6cWWGFVmYVdHIPrElqSDf4iCDktQCockB9FxuhzQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.3.tgz", + "integrity": "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==", + "license": "MIT" + }, + "node_modules/@mermaid-js/parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.0.tgz", + "integrity": "sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==", + "license": "MIT", + "dependencies": { + "@chevrotain/types": "~11.1.2" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@noble/ed25519": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.3.0.tgz", + "integrity": "sha512-M7dvXL2B92/M7dw9+gzuydL8qn/jiqNHaoR3Q+cb1q1GHV7uwE17WCyFMG+Y+TZb5izcaXk5TdJRrDUxHXL78A==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz", + "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz", + "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.15.tgz", + "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz", + "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz", + "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz", + "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", + "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-effect-event": "0.0.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.24.tgz", + "integrity": "sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz", + "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", + "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-hover-card": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.23.tgz", + "integrity": "sha512-H8qONfZd3ltrU3+jHCIgITbWo6e1iTKvP9DHdrvYbX48ooRM5FjEDTn16AMwdfuOGkWdZEhpl3PLL/Wk/AnHDQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.24.tgz", + "integrity": "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-callback-ref": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.23.tgz", + "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-rect": "1.1.4", + "@radix-ui/react-use-size": "1.1.4", + "@radix-ui/rect": "1.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", + "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", + "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", + "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.3.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz", + "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.15.tgz", + "integrity": "sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", + "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", + "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", + "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz", + "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz", + "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz", + "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz", + "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", + "license": "MIT" + }, + "node_modules/@rc-component/async-validator": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-6.0.0.tgz", + "integrity": "sha512-D3AGQwdyE58gmvx6waVSXJ80JGO+IY5L2O8HDnSOex7JNlzB3GuN/4hyHNTdhy2qtOhkpbIjmeAN3tL993wKbA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/cascader": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@rc-component/cascader/-/cascader-1.22.0.tgz", + "integrity": "sha512-SffrA57aS9oub3VuI7ajPhJTPtaNxngSvtRhD40Rd8dwJ5vfWPSrVanWgeepdWFGBt7EHftIK5RUU0u3rCTwWw==", + "license": "MIT", + "dependencies": { + "@rc-component/select": "~1.10.0", + "@rc-component/tree": "~1.4.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/checkbox": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/checkbox/-/checkbox-2.0.0.tgz", + "integrity": "sha512-3CXGPpAR9gsPKeO2N78HAPOzU30UdemD6HGJoWVJOpa6WleaGB5kzZj3v6bdTZab31YuWgY/RxV3VKPctn0DwQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/collapse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/collapse/-/collapse-1.2.0.tgz", + "integrity": "sha512-ZRYSKSS39qsFx93p26bde7JUZJshsUBEQRlRXPuJYlAiNX0vyYlF5TsAm8JZN3LcF8XvKikdzPbgAtXSbkLUkw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-3.1.1.tgz", + "integrity": "sha512-OHaCHLHszCegdXmIq2ZRIZBN/EtpT6Wm8SG/gpzLATHbVKc/avvuKi+zlOuk05FTWvgaMmpxAko44uRJ3M+2pg==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^3.0.1", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-2.0.2.tgz", + "integrity": "sha512-uiGpAlblCNlziHPwj4S4Iy/oemeuz/hR03mbiEjTCXwsqOIN3BOzsRMyDwpyO5Fm0vIEEJRUf9ZtbRLbhksuTA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.0" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/dialog": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@rc-component/dialog/-/dialog-1.10.0.tgz", + "integrity": "sha512-eDukNlz9vNszAGv7i3zKXdxEd3wgVmNxuJijYt8zvTh17QwTu8KK/bdURRd/lU4qaMzhO1HKKmMrwOnkaw0BvQ==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.3.3", + "@rc-component/portal": "^2.1.0", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/drawer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@rc-component/drawer/-/drawer-1.4.2.tgz", + "integrity": "sha512-1ib+fZEp6FBu+YvcIktm+nCQ+Q+qIpwpoaJH6opGr4ofh2QMq+qdr5DLC4oCf5qf3pcWX9lUWPYX652k4ini8Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/portal": "^2.1.3", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/dropdown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rc-component/dropdown/-/dropdown-1.0.3.tgz", + "integrity": "sha512-YTST/N6kpqpDz3IMuM/PSSZnrDpSOA6dgHv12gPA90ZTSLv2CoqkZ0+9NtwTY6BeO7dstPblSic2QJg7dSFy/g==", + "license": "MIT", + "dependencies": { + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/@rc-component/form": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@rc-component/form/-/form-1.8.6.tgz", + "integrity": "sha512-1EXVsSKPZC6kGrIqxVck7kAWM6T65b6et/n5wIcyiaIa41VrDshD0nVLHoBDEIZr2ATQsOP6icb4XQkNFMLBAw==", + "license": "MIT", + "dependencies": { + "@rc-component/async-validator": "^6.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/image": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@rc-component/image/-/image-1.10.0.tgz", + "integrity": "sha512-BjeZCRQ+hw+4WAhvrw8rJvy5fckA2xpf/X2XQEOABUHvLTNB9inB98X3Mp54jYQ7g10DfWERQWHXeC4ylxp1Uw==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.0.0", + "@rc-component/portal": "^2.1.2", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/input": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@rc-component/input/-/input-1.3.1.tgz", + "integrity": "sha512-iFvTUT9W+JC/MSin2aGAk8NqsVlTzcExNC9DZariON1IWirju9NoNeEk47an4Q8iHazkoVI/y1LnDi88+CPcig==", + "license": "MIT", + "dependencies": { + "@rc-component/resize-observer": "^1.1.1", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@rc-component/input-number": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@rc-component/input-number/-/input-number-1.6.2.tgz", + "integrity": "sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==", + "license": "MIT", + "dependencies": { + "@rc-component/mini-decimal": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/listy": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rc-component/listy/-/listy-1.2.3.tgz", + "integrity": "sha512-IXiMjV5s0rczLBlfh7G5nB4M3365mrEeedjwKtf5I+Ns3PqRUsebR2h5u8CeFarsVfLUPC2I5p0h09TNoOWyvQ==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/portal": "^2.0.0", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.3.1", + "@rc-component/virtual-list": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/mentions": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@rc-component/mentions/-/mentions-1.11.0.tgz", + "integrity": "sha512-IC2qXuEBMFHxPIXEFfYWj6Sr7UiDZnOqJHCYQBbwPzopBJOPZIR6mV9U4QH1bYQRlKYlYnIsajWDMgVGgWQyWQ==", + "license": "MIT", + "dependencies": { + "@rc-component/input": "~1.3.1", + "@rc-component/menu": "~1.4.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/menu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@rc-component/menu/-/menu-1.4.1.tgz", + "integrity": "sha512-3GsVRoQ4cnF/AoIQ4P+Z1haBfgfBPQfLT1RJY3Nu4DzOnheTslfCiGSPj7bv/cLj5sW5pHqN25dDXGP3JELAlQ==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/overflow": "^1.0.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.4.tgz", + "integrity": "sha512-xiuXcaCwyOWpD8a8scdExFl+bntNphAW8XeenL1ig2en0AAZY0Pcp4pC0dI22qJ+NvxKn9RoNIoRdqYU3BLH4w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/motion": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@rc-component/motion/-/motion-1.3.3.tgz", + "integrity": "sha512-Xh3IszxvlSv3/PLYFyC2UZi9LNB83yOnkB/LNmRzaypZLvkhqUIPS7MQpGZcCMWrNsXV2p6YTSWbSGvFpEle9A==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-2.0.1.tgz", + "integrity": "sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/notification": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@rc-component/notification/-/notification-2.0.7.tgz", + "integrity": "sha512-nqZzpf6BPdaj+3ILx7si79LLmqPKyUmQoXa+/9gg0SkH0v1DbD66oJgRMSBEVnd/zUT3D4gwxWIHUKebYf2ZXQ==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/overflow": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/overflow/-/overflow-1.0.1.tgz", + "integrity": "sha512-syfmgAABaHCnCDzPwHZ/2tuvIcpOO3jefYZMmfkN+pmo8HKTzsfhS57vxo4ksPdN0By+uWVJhJWNFozNBxi2eA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@rc-component/resize-observer": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/pagination": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/pagination/-/pagination-1.4.0.tgz", + "integrity": "sha512-CW1g7P9V8u+e8JQdUsl2RWg+GCsoee0mtJjZUCCxn/vb3jzOwDKm6hAdwddHCVBfWJ58eGUBZz3IvnU8rRktjw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/picker": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@rc-component/picker/-/picker-1.12.0.tgz", + "integrity": "sha512-0FZGgDiDZFMm2hcfGv4jyjm7yWIj2MiwfeTzLL0vWkO+8cCSKdiM9XhWpkn1aAsoI2EwUy48Oz2cGfvL8ZKrfw==", + "license": "MIT", + "dependencies": { + "@rc-component/overflow": "^1.0.0", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/trigger": "^3.6.15", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=12.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/@rc-component/portal": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-2.2.1.tgz", + "integrity": "sha512-ck+r1kW/JSv0wxPji3KN2ss9K6Z0qqwusw/mf/0JobXhZ8hC2ejZwCJObW/SvDi0uhA0VzmCnx0CaCci95tcmA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=12.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/progress": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rc-component/progress/-/progress-1.0.2.tgz", + "integrity": "sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-2.0.0.tgz", + "integrity": "sha512-aAv3QhPP1xyafuTZOxub6a54pCeBnN3IwQkpETrBtthq4BL5IgxnCbuoBWPDpdLw1y1j6BgBUCAKV92+yX06Dw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/rate": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/rate/-/rate-1.0.1.tgz", + "integrity": "sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/resize-observer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/resize-observer/-/resize-observer-1.1.2.tgz", + "integrity": "sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/segmented": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@rc-component/segmented/-/segmented-1.3.0.tgz", + "integrity": "sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@rc-component/select": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@rc-component/select/-/select-1.10.1.tgz", + "integrity": "sha512-H+yQsl+qED9NilQ3g6zdpsMwUgwVjrcMTkNHAWRVU/MoNCYgTbDgU+MIMgZDK+rVdd2JUfI/MkysMcZZ0cyQKw==", + "license": "MIT", + "dependencies": { + "@rc-component/overflow": "^1.0.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.2.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/slider": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/slider/-/slider-1.1.1.tgz", + "integrity": "sha512-LSzgWGYDgeCDgR4r1XlU29gbYws6HpLnvJd/uMhLeW/vQgxldeR+Wb4uzHDCHiYEbr1bnEHWdjkPxjJRHxuiig==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/steps": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rc-component/steps/-/steps-1.2.2.tgz", + "integrity": "sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/switch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rc-component/switch/-/switch-1.0.3.tgz", + "integrity": "sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/table": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@rc-component/table/-/table-1.11.1.tgz", + "integrity": "sha512-OWdS6DMmeWb7bJBGqPxYZpQbzBlBiXZUu2sqo6Ii7Sjs9GeK1IsrXrWk26SL2c6KEseabswdxrRj7WUm9LdECw==", + "license": "MIT", + "dependencies": { + "@rc-component/context": "^2.0.1", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.0.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/tabs": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@rc-component/tabs/-/tabs-1.12.0.tgz", + "integrity": "sha512-XL7Kqy5fnUE2WTlO1/fCGrrfNlGFebdr7JseGkEIjzcVMAtIFQJ8sqCSOmxcXstjU6fonD/4rnhZHxj7sDTajQ==", + "license": "MIT", + "dependencies": { + "@rc-component/dropdown": "~1.0.0", + "@rc-component/menu": "~1.4.0", + "@rc-component/motion": "^1.1.3", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tooltip": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@rc-component/tooltip/-/tooltip-1.5.0.tgz", + "integrity": "sha512-agQ/+mBqrEQfTX4D3KhQ7j+ZbX4/VHjoJ7Noa2wIdZ1/FbQTOd7Sn92rp+jtCoqAVTLUgSOydePIgZ204gi2EQ==", + "license": "MIT", + "dependencies": { + "@rc-component/trigger": "^3.10.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-2.4.0.tgz", + "integrity": "sha512-aui4r4TqmTzwaBgcQxHYep8kM8PTjZFufjokObpy35KfFeZ0k9ArquWFZqegQlH24P14t+F0qO0mGTgzlav1yg==", + "license": "MIT", + "dependencies": { + "@rc-component/portal": "^2.2.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.7.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tree": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/tree/-/tree-1.4.0.tgz", + "integrity": "sha512-dGsJGDJQedA0BqqVgj3F8BvHXTSZijyhTXdbAdkcx8lynzZkty/CV3Z3LOm/fxz+BCfl3dfGiAQpb7Q5XNvl0Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.2.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/tree-select": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@rc-component/tree-select/-/tree-select-1.16.1.tgz", + "integrity": "sha512-a1Oi6EJhqAhdOxxupdJi6fP0RPHMKn5TcfkX2+llaQ4lF4nwfH7b6SCHcnsybaa2s+pk1yZYwVyeOYkDnEBRdg==", + "license": "MIT", + "dependencies": { + "@rc-component/select": "~1.10.0", + "@rc-component/tree": "~1.4.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/trigger": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-3.10.1.tgz", + "integrity": "sha512-mXlDN0IXdtV8Yqqm8195ECCyrbmfvvfKvwVvSlH0+qvKD6BUF8gRhEjSy0FOcD1+CcDRHgTiX99LoxfQrmh3Cw==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.3.3", + "@rc-component/portal": "^2.2.1", + "@rc-component/resize-observer": "^1.1.2", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/upload": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/upload/-/upload-1.1.1.tgz", + "integrity": "sha512-GvYWSKeaJTOxxC5p6+nOSadzfvXA1h8C/iHFPFZX+szH3JUXrvs+DLiW8YUTBgvMh8m63mJeHrlYlJzAlg+pDA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/util": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.12.0.tgz", + "integrity": "sha512-AEjPL8JVdohIITaiXokyjL9WQ6tKWWjAYK9QU16tGNE9JaQABBQy+hA4H2Lup5MgXy9yY3iLrbZJheuU13hTdQ==", + "license": "MIT", + "dependencies": { + "is-mobile": "^5.0.0", + "react-is": "^19.2.7" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/virtual-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@rc-component/virtual-list/-/virtual-list-1.5.1.tgz", + "integrity": "sha512-boqHxdtyWC88u8quYgEO49bcBy5fzRiOcnBge+N4nLzs2k8hUQ/yw7JE9dM6yCBE4jSm5YSHVCVMS+suBuJGKA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^8.0.0", + "@rc-component/resize-observer": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/virtual-list/node_modules/@babel/runtime": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-8.0.0.tgz", + "integrity": "sha512-sL6cvO2IfkSu/iU+zs2S/w01B7A8V7suXSIKEN4hPFFdZoiPGxrj5pAG0lCaqLWiEIrjKzdznIWuaLcxPR53qw==", + "license": "MIT" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.4.tgz", + "integrity": "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", + "license": "MIT" + }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.67.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@univerjs-pro/collaboration": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/collaboration/-/collaboration-0.19.0.tgz", + "integrity": "sha512-GRj2dnm+1FZkxVjOULAc65hCkbeUHmJj4OCykbRBWvViQb6nC0UGy19G1bPmvi6fCNHYhHFPwcaHC7icfQPCBA==", + "dependencies": { + "@univerjs-pro/license": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/data-validation": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/protocol": "0.1.48", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-conditional-formatting": "0.19.0", + "@univerjs/sheets-drawing": "0.19.0", + "@univerjs/sheets-filter": "0.19.0", + "@univerjs/sheets-hyper-link": "0.19.0", + "@univerjs/thread-comment": "0.19.0", + "uuid": "^13.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs-pro/collaboration-client": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/collaboration-client/-/collaboration-client-0.19.0.tgz", + "integrity": "sha512-Vgag0DGsvQFYYkwCFkFTBT/FPUSTBMze19zGVaRsqg0SPUmGwf7fGhbPHUHkH7QO1hhna2cP7k+4fgdkorJF6A==", + "dependencies": { + "@univerjs-pro/collaboration": "0.19.0", + "@univerjs-pro/license": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/network": "0.19.0", + "@univerjs/protocol": "0.1.48", + "@univerjs/sheets": "0.19.0", + "@univerjs/telemetry": "0.19.0", + "crypto-js": "4.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/collaboration-client-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/collaboration-client-ui/-/collaboration-client-ui-0.19.0.tgz", + "integrity": "sha512-Ek4dtUfBT6uxi46TuFE9/H5yTUfVpIxECNDR8nbJ1Rrpp0619eT4/Pp77x0JM2X0CQO5pu4cKOUOvFYkKdg1Ug==", + "dependencies": { + "@univerjs-pro/collaboration": "0.19.0", + "@univerjs-pro/collaboration-client": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/network": "0.19.0", + "@univerjs/protocol": "0.1.48", + "@univerjs/rpc": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0", + "crypto-js": "4.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/docs-exchange-client": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/docs-exchange-client/-/docs-exchange-client-0.19.0.tgz", + "integrity": "sha512-/CYwSAURW/uHafWkuOeOELfF8JXeqs3rip9WOrA8hxsRoj3kduDRx5HbfTZpQQnrIOWmcFpATp+7+axFJswfWw==", + "dependencies": { + "@univerjs-pro/exchange-client": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs-pro/docs-print": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/docs-print/-/docs-print-0.19.0.tgz", + "integrity": "sha512-PWQLpQt4NgNLU9UHfl++5oDEFSvCgLOf43EiT4J6lAHGMAudIMep+y/tSESou/UL1DIGMOJpnv0pJztF+rEAzA==", + "dependencies": { + "@univerjs-pro/license": "0.19.0", + "@univerjs-pro/print": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/network": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs-pro/edit-history-loader": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/edit-history-loader/-/edit-history-loader-0.19.0.tgz", + "integrity": "sha512-z+6fZ6vDcB1niQonBeMQ3xePg7w/vQVN5DCkPmusEX1CSNnZabPdGgLBRFafrPnMSXeZsznoDo/rQTZhe+0c2g==", + "dependencies": { + "@univerjs-pro/collaboration": "0.19.0", + "@univerjs-pro/collaboration-client": "0.19.0", + "@univerjs-pro/collaboration-client-ui": "0.19.0", + "@univerjs-pro/edit-history-viewer": "0.19.0", + "@univerjs-pro/license": "0.19.0", + "@univerjs-pro/sheets-chart": "0.19.0", + "@univerjs-pro/sheets-chart-ui": "0.19.0", + "@univerjs-pro/sheets-pivot": "0.19.0", + "@univerjs-pro/sheets-shape": "0.19.0", + "@univerjs-pro/sheets-shape-ui": "0.19.0", + "@univerjs-pro/sheets-sparkline": "0.19.0", + "@univerjs-pro/sheets-sparkline-ui": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/data-validation": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/drawing-ui": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/network": "0.19.0", + "@univerjs/protocol": "0.1.48", + "@univerjs/rpc": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-conditional-formatting": "0.19.0", + "@univerjs/sheets-conditional-formatting-ui": "0.19.0", + "@univerjs/sheets-data-validation": "0.19.0", + "@univerjs/sheets-data-validation-ui": "0.19.0", + "@univerjs/sheets-drawing": "0.19.0", + "@univerjs/sheets-drawing-ui": "0.19.0", + "@univerjs/sheets-filter": "0.19.0", + "@univerjs/sheets-filter-ui": "0.19.0", + "@univerjs/sheets-formula": "0.19.0", + "@univerjs/sheets-formula-ui": "0.19.0", + "@univerjs/sheets-hyper-link": "0.19.0", + "@univerjs/sheets-hyper-link-ui": "0.19.0", + "@univerjs/sheets-numfmt": "0.19.0", + "@univerjs/sheets-table": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/edit-history-viewer": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/edit-history-viewer/-/edit-history-viewer-0.19.0.tgz", + "integrity": "sha512-bzvJ7swLHSSws5eryT+WCEyd2nmibtPKDSPU/i/AXZS5pZwiO2YNYLUC+aTo7V9e1J0yRwxAu8WGrys5qOafXQ==", + "dependencies": { + "@univerjs-pro/collaboration": "0.19.0", + "@univerjs-pro/collaboration-client": "0.19.0", + "@univerjs-pro/collaboration-client-ui": "0.19.0", + "@univerjs-pro/sheets-chart": "0.19.0", + "@univerjs-pro/sheets-pivot": "0.19.0", + "@univerjs-pro/sheets-shape": "0.19.0", + "@univerjs-pro/sheets-sparkline": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/data-validation": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/network": "0.19.0", + "@univerjs/protocol": "0.1.48", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-conditional-formatting": "0.19.0", + "@univerjs/sheets-data-validation": "0.19.0", + "@univerjs/sheets-drawing": "0.19.0", + "@univerjs/sheets-filter": "0.19.0", + "@univerjs/sheets-table": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/engine-chart": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/engine-chart/-/engine-chart-0.19.0.tgz", + "integrity": "sha512-4eFHBgWfMfp/oVF4yONk0IXSokTBD8j9H+8diDP+RjqCutFBxPmbDPA0+TOE8Gy43+bxedz9rLHmvPwd6mylpg==", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-render": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/engine-formula": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/engine-formula/-/engine-formula-0.19.0.tgz", + "integrity": "sha512-hUuE0+OcoMuTdjuqmFw27K31A37fVbGdWuMUCAhDeGWK3dsdE6/sTdPkPke8u3nloWl/LnsDo9KQqwKyWuqHWA==", + "dependencies": { + "@univerjs-pro/license": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs-pro/engine-pivot": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/engine-pivot/-/engine-pivot-0.19.0.tgz", + "integrity": "sha512-SelfKAeCibUTjerzTk6ss8A8ZpfNzM9VJaxv5zwOpY2kYRbSIHxtbGKxt1kBTvu84XukLDcmxALNrBl8JkB1Lw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs-pro/engine-shape": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/engine-shape/-/engine-shape-0.19.0.tgz", + "integrity": "sha512-WMzEuCOAsqiE1Kkx85hFwVv5stetKS4LDW5h+sdui6KWvUT7KfRg8lF+y4YwTra2FcSzxqpQ9VBQYO6z6pk96A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs-pro/exchange-client": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/exchange-client/-/exchange-client-0.19.0.tgz", + "integrity": "sha512-KHhuIjGkSM5NEieeGz9MC05Txy7QgzcQUxc0HKwQer8dHAwm9lOydZLXbOcW8wQjZO55ZZbbFNndJXBnD1Oqyw==", + "dependencies": { + "@univerjs-pro/collaboration": "0.19.0", + "@univerjs-pro/license": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/network": "0.19.0", + "@univerjs/protocol": "0.1.48", + "@univerjs/ui": "0.19.0", + "pako": "^2.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/license": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/license/-/license-0.19.0.tgz", + "integrity": "sha512-aqUiH8OL+89d9+h3VAmBu/ID1O+9b3nIdR6OmL6TwiQtf7E7IX5qD8p22jlU0JDAdeM6aoxSYHcO6Gin2OY6pQ==", + "dependencies": { + "@noble/ed25519": "2.3.0", + "@noble/hashes": "1.8.0", + "@univerjs/core": "0.19.0", + "@univerjs/engine-render": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/print": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/print/-/print-0.19.0.tgz", + "integrity": "sha512-pum3lxYKJQ2T6gGTRJ2ySSWX/d/Im4/VkcYWTNLRVaK/aGAskF4qu1vkMUMO8TyMqy70AAPzwocGz046dF+EtA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs-pro/sheets-chart": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/sheets-chart/-/sheets-chart-0.19.0.tgz", + "integrity": "sha512-5nWzkHAOuhCiAyGg7QxOensNAvu4R2++vFzz3sZl0m31Nr+TKUyLQ8JkRbORgBwuVL6YfV6uUX60NGj95S7wtg==", + "dependencies": { + "@univerjs-pro/engine-chart": "0.19.0", + "@univerjs-pro/license": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/sheets-chart-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/sheets-chart-ui/-/sheets-chart-ui-0.19.0.tgz", + "integrity": "sha512-1zzBChTAGaH0hXl7N3N/xAtgzC7jz1rnL6G6iXZD1fQt9Q80pm+C+1LdndcYoKLdCy1OCMbaKDnOSnBgkluOlw==", + "dependencies": { + "@univerjs-pro/engine-chart": "0.19.0", + "@univerjs-pro/sheets-chart": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-drawing": "0.19.0", + "@univerjs/sheets-drawing-ui": "0.19.0", + "@univerjs/sheets-formula-ui": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/sheets-exchange-client": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/sheets-exchange-client/-/sheets-exchange-client-0.19.0.tgz", + "integrity": "sha512-HcB22iA+QoTNke0ibXwpnTjkR8ykEeTriTFBO8CP5PqPczTlICAQYGdNtW5xAwAee29jbF7sXrIC/MvgHOw2nQ==", + "dependencies": { + "@univerjs-pro/exchange-client": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs-pro/sheets-pivot": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/sheets-pivot/-/sheets-pivot-0.19.0.tgz", + "integrity": "sha512-ObGnE8byo1N0k5q9u3LTs/QCcsoRwqI/PgrBRMnRX6BnOjdmUR+EQ9UDNvLzE07JVSmjuP3p7UatREugyBWOrw==", + "dependencies": { + "@univerjs-pro/engine-pivot": "0.19.0", + "@univerjs-pro/license": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/rpc": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-filter": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/sheets-pivot-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/sheets-pivot-ui/-/sheets-pivot-ui-0.19.0.tgz", + "integrity": "sha512-Y6B4qV5priGjcSKmBjAqQdjBz25q1DO4vlfGhLbMAk39GtWB6z7W4jfG4KIke89PsKfaYnA2thFVqHuf90dHDA==", + "dependencies": { + "@univerjs-pro/engine-pivot": "0.19.0", + "@univerjs-pro/sheets-pivot": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-formula-ui": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/sheets-print": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/sheets-print/-/sheets-print-0.19.0.tgz", + "integrity": "sha512-CdMjdh3FRNH16U72XcaZltVb8rL4eq5x3K3+9h3LZGQ9KpLMwgRJvT5fgwxXM8Ahvvkbr3kr2tzl3upT4ukfWQ==", + "dependencies": { + "@univerjs-pro/collaboration-client": "0.19.0", + "@univerjs-pro/license": "0.19.0", + "@univerjs-pro/print": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/network": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/sheets-shape": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/sheets-shape/-/sheets-shape-0.19.0.tgz", + "integrity": "sha512-mTRSO5A0vK/u/Tp8TmjOrlVNrVFTBzTfs9xq1P4cxC8DRWJNlJYQyQFOOO5yKc0/FjrwbTZ/s5x2TWRSkfJAQg==", + "dependencies": { + "@univerjs-pro/engine-shape": "0.19.0", + "@univerjs-pro/license": "0.19.0", + "@univerjs/core": "0.19.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs-pro/sheets-shape-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/sheets-shape-ui/-/sheets-shape-ui-0.19.0.tgz", + "integrity": "sha512-JvzJUux5oQwGGvOwI74fZNYf2l6b8e7zOprpzcdGLDgcWltT/vWb1U44DuRAg3elhsZtvt6hveUyWR3JGu3g2A==", + "dependencies": { + "@univerjs-pro/engine-shape": "0.19.0", + "@univerjs-pro/sheets-shape": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/drawing-ui": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-drawing": "0.19.0", + "@univerjs/sheets-drawing-ui": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/sheets-sparkline": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/sheets-sparkline/-/sheets-sparkline-0.19.0.tgz", + "integrity": "sha512-EF5Dvpo/wDXkEVkc7zfKR3IeZ5yMLRTnQhcJb/0xlFqKZoYyxNifAUMcOcrClfFkOp9VF90LGxdTSwpiWTCKHA==", + "dependencies": { + "@univerjs-pro/license": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/sheets-sparkline-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/sheets-sparkline-ui/-/sheets-sparkline-ui-0.19.0.tgz", + "integrity": "sha512-FH3hugOKnV58RXt9BM2YaJi6THZxpp7U6F1YZ8nEb0a9+FAHmyvLilH0je5nQMQ+QFSz/S3zAiobP7+MqUVubw==", + "dependencies": { + "@univerjs-pro/sheets-sparkline": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-formula-ui": "0.19.0", + "@univerjs/sheets-graphics": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs-pro/thread-comment-datasource": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs-pro/thread-comment-datasource/-/thread-comment-datasource-0.19.0.tgz", + "integrity": "sha512-oh+1lN5z6VzoxI53hrZ0od6NNJQozqBK2ymh/AZMp3SugXO/xVVhDdPa3zk88noUdhXhS2qzV+IrCrlTBllBBw==", + "dependencies": { + "@univerjs-pro/collaboration-client": "0.19.0", + "@univerjs-pro/license": "0.19.0", + "@univerjs/core": "0.19.0", + "@univerjs/network": "0.19.0", + "@univerjs/protocol": "0.1.48", + "@univerjs/thread-comment": "0.19.0", + "@univerjs/thread-comment-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/core/-/core-0.19.0.tgz", + "integrity": "sha512-bB1vNixeOlGnd7r3S9raYH73BqpHToLWEN7Wc4UynIoouoYVFCQJ7V0sAz7Th5vGAfwg9+Isotug8somvMu3/g==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/protocol": "0.1.48", + "@univerjs/themes": "0.19.0", + "@wendellhu/redi": "1.1.1", + "async-lock": "^1.4.1", + "dayjs": "^1.11.20", + "fast-diff": "1.3.0", + "kdbush": "^4.0.2", + "lodash-es": "^4.17.23", + "nanoid": "5.1.7", + "numfmt": "^3.2.3", + "ot-json1": "^1.0.2", + "rbush": "^4.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/data-validation": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/data-validation/-/data-validation-0.19.0.tgz", + "integrity": "sha512-JbKycYiz+72ao1gjVE8gLnoAQCUbg5LDm4Wx2KOhCO1Bx+NggGkmym2jxuUcnZy1Cwa/TZb5/uR9jqHN4cD2Gw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/design": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/design/-/design-0.19.0.tgz", + "integrity": "sha512-uANO24s4L69M40AtkKD2hbhadv8YJId7NCQKvflEaJt1eswgDWWj/3WZUv9idEQI/U2Vl2Pu59CbufgADfmpEw==", + "license": "Apache-2.0", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-hover-card": "^1.1.15", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slot": "^1.2.4", + "@univerjs/icons": "^1.1.1", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "dayjs": "^1.11.20", + "react-transition-group": "^4.4.5", + "sonner": "^2.0.7", + "tailwind-merge": "2.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/@univerjs/docs": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/docs/-/docs-0.19.0.tgz", + "integrity": "sha512-o6HkW/OZ85cpmNxd4/2LW0uEulfDAMfue76kFtVxWBOCtGVwWrFdXlSdv3QrFk/WS0A8uK2JpRuubbVplmuD3A==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-render": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/docs-drawing": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/docs-drawing/-/docs-drawing-0.19.0.tgz", + "integrity": "sha512-r12OopPz2SHO6P7/bqnSNB/iuesKKwniFbHOeYnY8YsOL489XATH9xTHyr7YHH08YAVXREIoFjrcH58xsOw9jw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/drawing": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs/docs-drawing-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/docs-drawing-ui/-/docs-drawing-ui-0.19.0.tgz", + "integrity": "sha512-nljXloxv/BvTjNinXpGEfuwpsuvVeZzQg8SaC1hVxl+kyMgLeSZ6xkzlSZXLL3OLlzbCjEmpRmwiH371ZCloZg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-drawing": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/drawing-ui": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/docs-hyper-link": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/docs-hyper-link/-/docs-hyper-link-0.19.0.tgz", + "integrity": "sha512-Ah6jKr0+YCqDTjm15sdYPAzmfxnx7CEORQzGBmEBaAKkFqXEX3eCgcXG/+bRVj2Xu4e447WyORtvlmM/Elzbdw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs/docs-hyper-link-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/docs-hyper-link-ui/-/docs-hyper-link-ui-0.19.0.tgz", + "integrity": "sha512-0Rwp8S9jqUxVUf8Og2SsxO5CHu0ENq8WTuWGKS0ENdLMY3GeQxsaDP05x+uyrMi+J+fYgUma0wgc13eSy0Ebbw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-hyper-link": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/docs-thread-comment-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/docs-thread-comment-ui/-/docs-thread-comment-ui-0.19.0.tgz", + "integrity": "sha512-lKDQQgJGPSb+Gz9THPWpOzwMzCkX4lmY0JdZ2Bnh1jbHOV+w3zEAzAEI94hM2uvIVn+tz71jOmfmEom4N1xiVg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/thread-comment": "0.19.0", + "@univerjs/thread-comment-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/docs-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/docs-ui/-/docs-ui-0.19.0.tgz", + "integrity": "sha512-qIeMxmm9rkAV5IcNMUd/4puo05w3DR8StbnY0Hxf7Opw8LXUeoI5pCGhyezYL4Xi/sdPgaWEiqZJTJrqoN1AgA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/drawing": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/drawing/-/drawing-0.19.0.tgz", + "integrity": "sha512-TtL/4gWsbDv7t8flGnGCmnUmwW5JcOycxkjU+It9yVbUqkBUEGDIFylt90oqs8wq4AO8vezs/D0DaizDcxmd6A==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "ot-json1": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/drawing-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/drawing-ui/-/drawing-ui-0.19.0.tgz", + "integrity": "sha512-atXQNKBnzftrhBHqKZYeHXiMYZJL3PA5U3wJTzSBjF8GQ7A+GAIuU2bxA2OZfFaKqzRUGXCJgOZikDCt05PJ2A==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/engine-formula": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/engine-formula/-/engine-formula-0.19.0.tgz", + "integrity": "sha512-KsXt+dZzqIQif1EAiDgvzFzdMXT/7cWyZVRI8gUAZVYQMXlO8NESnrRUp9xtWsJw4UxSywf+OijsEFHfxLoV/A==", + "license": "Apache-2.0", + "dependencies": { + "@flatten-js/interval-tree": "1.1.3", + "@univerjs/core": "0.19.0", + "@univerjs/rpc": "0.19.0", + "decimal.js": "^10.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/engine-render": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/engine-render/-/engine-render-0.19.0.tgz", + "integrity": "sha512-4vU/yngPdZed8nvk5WekLSVblMqx55WDgSyNJ1kFDrSOkcqct6tsTqyc4cdDJjQGxkjt4AjlmeaitXAvJZRI9w==", + "license": "Apache-2.0", + "dependencies": { + "@floating-ui/dom": "^1.7.4", + "@floating-ui/utils": "^0.2.10", + "@univerjs/core": "0.19.0", + "cjk-regex": "^3.4.0", + "franc-min": "^6.2.0", + "opentype.js": "^1.3.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/find-replace": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/find-replace/-/find-replace-0.19.0.tgz", + "integrity": "sha512-sIPi3Tr1XyDpDYC7mAUAu9Z8+0XR+0HaFoP32CjmbBrfXzRxlYXqnMMPZQztbDKw43CZEI8t+90hs8NBgvGlZw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/icons": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@univerjs/icons/-/icons-1.1.1.tgz", + "integrity": "sha512-3agWxYwNEyfpiCerajLZvZWfa+Fsx2LhGY9EeacSiPk+32BX9NwmCa9uTpDVe0F94iEO+fGfkTB8pCeIFU4l8w==", + "license": "MIT", + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@univerjs/network": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/network/-/network-0.19.0.tgz", + "integrity": "sha512-j8MvEVhQicRppI68Nb6hscKWFGuykYXY1tTVaPSdUuiz4RfZDfADqnIU2xp04QJH4xEYXyt+dtxCPhz5NAAVfw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-docs-advanced": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-docs-advanced/-/preset-docs-advanced-0.19.0.tgz", + "integrity": "sha512-VsnW9a+I/zb1fTRuUEhzI5iYWOrWN4s6QR8+N/8ir20mJDtoLgIHewbNWji0Xfu3EJDHdBjd/r1SIhi21TOehw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs-pro/docs-exchange-client": "0.19.0", + "@univerjs-pro/docs-print": "0.19.0", + "@univerjs-pro/exchange-client": "0.19.0", + "@univerjs-pro/license": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-docs-collaboration": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-docs-collaboration/-/preset-docs-collaboration-0.19.0.tgz", + "integrity": "sha512-HjTfO9OpKwjtZUCl4ulmxdvEEjOoTCEX4/5yXiMTPBkXGy3YD1l+VHq9VlGDRpIOAPFlgxLBCxZ5yCtQgKlKoQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs-pro/collaboration": "0.19.0", + "@univerjs-pro/collaboration-client": "0.19.0", + "@univerjs-pro/collaboration-client-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-docs-core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-docs-core/-/preset-docs-core-0.19.0.tgz", + "integrity": "sha512-vziR5mTkKXZz1fG0D9eP/H99OgWLV+zHdeVzEker3BclRFHjZc72OsVvuUl4zD6/0WdkXR64C9V33LLdVd7DkQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/network": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-docs-drawing": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-docs-drawing/-/preset-docs-drawing-0.19.0.tgz", + "integrity": "sha512-kVfe2yjVBCLNLeexzFezAEdWaTZaPo7K5pLVkHstl99zcPMIcxOuap9k2QPZ7rLz5EynUZRKjmIYwlzeC9GlyQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/docs-drawing": "0.19.0", + "@univerjs/docs-drawing-ui": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/drawing-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-docs-hyper-link": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-docs-hyper-link/-/preset-docs-hyper-link-0.19.0.tgz", + "integrity": "sha512-CLI3t1U83liPCVaZGjoDKV/JbLwnjqaTGZDx/4lxsA2NjFmkTbP9xfDGDIrRsALcCfwBDV/rjFuZjhmBasLmGg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/docs-hyper-link": "0.19.0", + "@univerjs/docs-hyper-link-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-docs-node-core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-docs-node-core/-/preset-docs-node-core-0.19.0.tgz", + "integrity": "sha512-g+gktZ8JZRbSBNBsMSNH5O8SJLxQ7TEITJUcdTI/xMjVdpeJLrvz2J1O3NHKUNwPXsXICJdN9879q1fsEozqfg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/docs": "0.19.0", + "@univerjs/docs-drawing": "0.19.0", + "@univerjs/docs-hyper-link": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/rpc-node": "0.19.0", + "@univerjs/thread-comment": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-docs-thread-comment": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-docs-thread-comment/-/preset-docs-thread-comment-0.19.0.tgz", + "integrity": "sha512-k63nkqJT6NWCS9srxAXBwUU+lxpZbdBoJI1T/inabxwfe4P2PfXn6d/lj9K5LJ/zmrrhGRDkpdmthHnE/4cfpQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/docs-thread-comment-ui": "0.19.0", + "@univerjs/thread-comment": "0.19.0", + "@univerjs/thread-comment-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-advanced": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-advanced/-/preset-sheets-advanced-0.19.0.tgz", + "integrity": "sha512-zoTHM6j4DClcvzbwtH0t+65AwOcI52eqtVnsU54fP+C4h42umqzJtUVl4yUS4P0Hi0P5YyH9U+6nzeKY3qh+2g==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs-pro/engine-chart": "0.19.0", + "@univerjs-pro/engine-formula": "0.19.0", + "@univerjs-pro/engine-shape": "0.19.0", + "@univerjs-pro/exchange-client": "0.19.0", + "@univerjs-pro/license": "0.19.0", + "@univerjs-pro/sheets-chart": "0.19.0", + "@univerjs-pro/sheets-chart-ui": "0.19.0", + "@univerjs-pro/sheets-exchange-client": "0.19.0", + "@univerjs-pro/sheets-pivot": "0.19.0", + "@univerjs-pro/sheets-pivot-ui": "0.19.0", + "@univerjs-pro/sheets-print": "0.19.0", + "@univerjs-pro/sheets-shape": "0.19.0", + "@univerjs-pro/sheets-shape-ui": "0.19.0", + "@univerjs-pro/sheets-sparkline": "0.19.0", + "@univerjs-pro/sheets-sparkline-ui": "0.19.0", + "@univerjs/sheets-graphics": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-collaboration": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-collaboration/-/preset-sheets-collaboration-0.19.0.tgz", + "integrity": "sha512-OckwkSwVOihixHShit5dKS9Zc+X+680VgEIYt0D978+xhVxcLKDBJ1Cs6rBJdl080YEC4yu0tZGqZyJN30xxlw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs-pro/collaboration": "0.19.0", + "@univerjs-pro/collaboration-client": "0.19.0", + "@univerjs-pro/collaboration-client-ui": "0.19.0", + "@univerjs-pro/edit-history-loader": "0.19.0", + "@univerjs-pro/edit-history-viewer": "0.19.0", + "@univerjs-pro/thread-comment-datasource": "0.19.0", + "@univerjs/preset-sheets-advanced": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-conditional-formatting": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-conditional-formatting/-/preset-sheets-conditional-formatting-0.19.0.tgz", + "integrity": "sha512-J3WxOeswCkT+p3JdA8bLaQzBoA+e/lS1pq4C3kpOzM55B5jj4bWwjojUh8fEqaZ8/oUuPCsEMaZcU6A3iadMXg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/sheets-conditional-formatting": "0.19.0", + "@univerjs/sheets-conditional-formatting-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-core/-/preset-sheets-core-0.19.0.tgz", + "integrity": "sha512-xXG+7BuJovMWk4EqM2GgXpswt1OoSynnj62Yq1gvPMxs3krxQb3MEVK4jD7Kk+yv4tNWFMJnTu8HZf9j7MdeCw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/network": "0.19.0", + "@univerjs/rpc": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-formula": "0.19.0", + "@univerjs/sheets-formula-ui": "0.19.0", + "@univerjs/sheets-numfmt": "0.19.0", + "@univerjs/sheets-numfmt-ui": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-data-validation": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-data-validation/-/preset-sheets-data-validation-0.19.0.tgz", + "integrity": "sha512-fnSd6wb6YqW+sNv/IUGVyxMPOK986C4npa6kdNDdgc+nbItLiafRMTZs9efKeshrORx1qqynJ0fDm35o2N+GIg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/data-validation": "0.19.0", + "@univerjs/sheets-data-validation": "0.19.0", + "@univerjs/sheets-data-validation-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-drawing": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-drawing/-/preset-sheets-drawing-0.19.0.tgz", + "integrity": "sha512-eBolGb9knHWGVtIcogAbAsQHglJ1qBhagYCMYjktjVpbhQk8NB1V8jTvO2c2T5rZXXkIFy+FwPr4NFt/XfmZHg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/docs-drawing": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/drawing-ui": "0.19.0", + "@univerjs/sheets-drawing": "0.19.0", + "@univerjs/sheets-drawing-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-filter": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-filter/-/preset-sheets-filter-0.19.0.tgz", + "integrity": "sha512-PDFVsIpbaaGDLVOFjYmCFt7la1/0j7T6MGJz8RumcBc0hxTjTsiITdhpkimnsvE7PEaHt8uBagpWSBOeUw6ftQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/sheets-filter": "0.19.0", + "@univerjs/sheets-filter-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-find-replace": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-find-replace/-/preset-sheets-find-replace-0.19.0.tgz", + "integrity": "sha512-+i6Ujb8zdcM1210c1OpU5t/irq1am4tT8Bf6d2kV6MDcDirNAVSSYn637K0be0d1v0nP5W6RCLka+ryb0YF+sA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/find-replace": "0.19.0", + "@univerjs/sheets-find-replace": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-hyper-link": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-hyper-link/-/preset-sheets-hyper-link-0.19.0.tgz", + "integrity": "sha512-sgE64Q4PyX2lLk6gJFZiCHTvNu/eSPr4yYpTwAAK4VhGQGS6wZg0ncl9zGgGvtgsoj4xjG8FWkN9LQFvyjGIAQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/sheets-hyper-link": "0.19.0", + "@univerjs/sheets-hyper-link-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-node-core": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-node-core/-/preset-sheets-node-core-0.19.0.tgz", + "integrity": "sha512-+2AFhOO8easCeaJ2eB+X3eeFIzJW34Ipm74f2DVP70HVuHeqceJi8525pS3spg8hX6YtDAwDs8pgIak4oD37+Q==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/docs": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/rpc-node": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-data-validation": "0.19.0", + "@univerjs/sheets-drawing": "0.19.0", + "@univerjs/sheets-filter": "0.19.0", + "@univerjs/sheets-formula": "0.19.0", + "@univerjs/sheets-hyper-link": "0.19.0", + "@univerjs/sheets-numfmt": "0.19.0", + "@univerjs/sheets-sort": "0.19.0", + "@univerjs/sheets-thread-comment": "0.19.0", + "@univerjs/thread-comment": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-note": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-note/-/preset-sheets-note-0.19.0.tgz", + "integrity": "sha512-HvxM8Nm7Vq6870r/CdXLz0oNmv2f+mlRGnqyK+0Oxt3wedoSswO8OS/SntWk+Xv+5xpgVrUw0GHfHRGoJqoQTQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/sheets-note": "0.19.0", + "@univerjs/sheets-note-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-sort": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-sort/-/preset-sheets-sort-0.19.0.tgz", + "integrity": "sha512-T2ClsxWNlBEI1bHMjCO2RMf3rIPAhbDUedbaG1uAhHPIBHJfEsIMgia/XlDM1rkTooJnV/wI1sJELD0LxqaP4Q==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/sheets-sort": "0.19.0", + "@univerjs/sheets-sort-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-table": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-table/-/preset-sheets-table-0.19.0.tgz", + "integrity": "sha512-Vg4ugCqgoZ+PkWmVjD14RKflv79TwnLK6PuW3MT0Qar8E/n34V4ZeLhEBRnLnT+NN+rfyg2zWpRAde3wTli7mQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/sheets-table": "0.19.0", + "@univerjs/sheets-table-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/preset-sheets-thread-comment": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/preset-sheets-thread-comment/-/preset-sheets-thread-comment-0.19.0.tgz", + "integrity": "sha512-c3fj2OonPJv1RRoC7T/jEcBN+Ep8uiYkdkMOjJZiGLl55ZQHd9YOiKz9Z++fXmIDC0A+W2cpb48N7fKfMf3GRA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/sheets-thread-comment": "0.19.0", + "@univerjs/sheets-thread-comment-ui": "0.19.0", + "@univerjs/thread-comment": "0.19.0", + "@univerjs/thread-comment-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/presets": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/presets/-/presets-0.19.0.tgz", + "integrity": "sha512-8zBdu2MMWfvUnSHiZmJf1hwtVLxtNiqgoiWnDfQRs2vfXLw0uS5emhDW+cN3ZPgobMP4kOJh9jaZOCvxTehd4A==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/preset-docs-advanced": "0.19.0", + "@univerjs/preset-docs-collaboration": "0.19.0", + "@univerjs/preset-docs-core": "0.19.0", + "@univerjs/preset-docs-drawing": "0.19.0", + "@univerjs/preset-docs-hyper-link": "0.19.0", + "@univerjs/preset-docs-node-core": "0.19.0", + "@univerjs/preset-docs-thread-comment": "0.19.0", + "@univerjs/preset-sheets-advanced": "0.19.0", + "@univerjs/preset-sheets-collaboration": "0.19.0", + "@univerjs/preset-sheets-conditional-formatting": "0.19.0", + "@univerjs/preset-sheets-core": "0.19.0", + "@univerjs/preset-sheets-data-validation": "0.19.0", + "@univerjs/preset-sheets-drawing": "0.19.0", + "@univerjs/preset-sheets-filter": "0.19.0", + "@univerjs/preset-sheets-find-replace": "0.19.0", + "@univerjs/preset-sheets-hyper-link": "0.19.0", + "@univerjs/preset-sheets-node-core": "0.19.0", + "@univerjs/preset-sheets-note": "0.19.0", + "@univerjs/preset-sheets-sort": "0.19.0", + "@univerjs/preset-sheets-table": "0.19.0", + "@univerjs/preset-sheets-thread-comment": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/protocol": { + "version": "0.1.48", + "resolved": "https://registry.npmjs.org/@univerjs/protocol/-/protocol-0.1.48.tgz", + "integrity": "sha512-nFHNtGAWOV0u1+IqoznH9K7hV/M9OZ61Vqwy8JMWKlgLLsx12m3vJqodkrVlLkI2YU5WuwjaUT1+J8/nM+kcUg==" + }, + "node_modules/@univerjs/rpc": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/rpc/-/rpc-0.19.0.tgz", + "integrity": "sha512-wn65EcsTHXJ87HmUmBbT607bdd/uxJtk8Jl839Kd+Hy/1fyvsIDexmKCRzkG9f9QB9CxRu0NsN6PCYgTo6LgLA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/rpc-node": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/rpc-node/-/rpc-node-0.19.0.tgz", + "integrity": "sha512-qfmqZVyFHfXLfj319J0MLvemJhu4vmYLXFfuTZv85GpPhUyejZNLKv9xEIw3IZqeGNG1r4VEzecB2+/lIJQ1MQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/rpc": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets/-/sheets-0.19.0.tgz", + "integrity": "sha512-iO2mX4Rh34RpNkHOsCC3h4UrspcKlA9NcR+fRFeArNrqbwwcv9MVyLjsIfZ256/hgoi9ADrKWD6nlqo09rL2mg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/protocol": "0.1.48", + "@univerjs/rpc": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-conditional-formatting": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-conditional-formatting/-/sheets-conditional-formatting-0.19.0.tgz", + "integrity": "sha512-zgbGP2XExIqqDsVkB4I2l8iZzxmnGQkN/BcCneMZEpdyMNF2eJ2c1tjPO4n7xola5MddcKpVKlfmY4S5Z7SOzA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-conditional-formatting-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-conditional-formatting-ui/-/sheets-conditional-formatting-ui-0.19.0.tgz", + "integrity": "sha512-sxlGhbriOt6tpshXOkI3d4Z4hFt8NFmmW2s1up6dY/TpGlj5NJHGwHO1pTJCGm95txnvGV1tkxO9OqGKbtyWZA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-conditional-formatting": "0.19.0", + "@univerjs/sheets-formula": "0.19.0", + "@univerjs/sheets-formula-ui": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-data-validation": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-data-validation/-/sheets-data-validation-0.19.0.tgz", + "integrity": "sha512-Ncvf/Nyu703qrGl00s0pkAC154kpp3g6ViRHccYIA12uQgQjzOb+yz25Hfk7jMyHHtc0Tu3avDHwBemrTRkYdw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/data-validation": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/protocol": "0.1.48", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-formula": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-data-validation-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-data-validation-ui/-/sheets-data-validation-ui-0.19.0.tgz", + "integrity": "sha512-Y1ODnbXgQmh1moQqTWiLIRHUFq7VBB/+7TrfbJJDWS7EhNw1LcujeisvRSGZT2vWOEYjju29dTWCgx+VV+Ra4w==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/data-validation": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-data-validation": "0.19.0", + "@univerjs/sheets-formula-ui": "0.19.0", + "@univerjs/sheets-numfmt": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-drawing": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-drawing/-/sheets-drawing-0.19.0.tgz", + "integrity": "sha512-wGPfZswxYzFfEetvWqDezLGjM8K6PSUxwLaf9mBWf5TgQ5B1ukAuoqyt2ECUo+57eg9rJASYj00yoKARuj3KOQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs/sheets-drawing-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-drawing-ui/-/sheets-drawing-ui-0.19.0.tgz", + "integrity": "sha512-KQYge0rrvVbPAZ8DfvpC0wXTBSuy73vdbO6kSnlUu91mMTzfoslxdfev5X7JScOmYwFwICKA+2OIs00qiYeqHQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs-drawing": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/drawing": "0.19.0", + "@univerjs/drawing-ui": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-drawing": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-filter": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-filter/-/sheets-filter-0.19.0.tgz", + "integrity": "sha512-K2vskrCqW3CoKgt2mZiDv6yASpjTWTdw/BQypxgO/7iEheZuLc/ShRR4O3J5BRfzeZUT/UYgLztY9Ucuhs5J4A==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/rpc": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-filter-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-filter-ui/-/sheets-filter-ui-0.19.0.tgz", + "integrity": "sha512-VJKXycKZMtzrO/qK0B0AybX+AIsL2z0uGEhzjZGNrmcIxEU9WWEQ3rW3wI7DwsrEPE0iP2Z8uykR3ApP/CsjrA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/rpc": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-filter": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-find-replace": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-find-replace/-/sheets-find-replace-0.19.0.tgz", + "integrity": "sha512-kW+IcQebJnMh4WO2JgNMwXAcgpHr+fDLwtL3UoUSAqGuT0Sp2TTR9BZ/pGDCTzaXF9QvXCput5c72YDRB366bw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/find-replace": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-formula": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-formula/-/sheets-formula-0.19.0.tgz", + "integrity": "sha512-p+emJWTs1MwUhVysVt+yt+G7R+sZv59sy+cAnBTAajS4QDxnDZFvGcXNu4fDwzK4zKGnWXn0Vi6mB9f8FLNyug==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/rpc": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-formula-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-formula-ui/-/sheets-formula-ui-0.19.0.tgz", + "integrity": "sha512-E8cLnjuLI1k4egtanklUuTBPUNEO88ezpcTARInbGmTppS7z5a1DdZjNd+QP8gYppiBg5B5BXs7ku5zT8D/AxQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-formula": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-graphics": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-graphics/-/sheets-graphics-0.19.0.tgz", + "integrity": "sha512-lp62+yMCqxzIxZ+CE9nfDFQiCPN6ZxIJd8LZusR63Mr9bd5mTVkLMKNFLJm/d0tGxbF3jQoGMxjclDV6PITorg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/sheets-ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs/sheets-hyper-link": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-hyper-link/-/sheets-hyper-link-0.19.0.tgz", + "integrity": "sha512-y9JVo1KdULSH3LTKOHdd6O05cUZxxJXVv9zhQCyffxjchtpn0uFGzREmwPYlsIm+9nxOXTF4dGbwEdGNFKX4Sg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-hyper-link-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-hyper-link-ui/-/sheets-hyper-link-ui-0.19.0.tgz", + "integrity": "sha512-1my91rooK5yqMt0c1AY7HK5V/FFepOSK9m4Eoo/Rkf0HSHgFr1j3dInORF8qBpYcjX4Pc7Q63LWzcO0Wjkww6Q==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-data-validation": "0.19.0", + "@univerjs/sheets-formula-ui": "0.19.0", + "@univerjs/sheets-hyper-link": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-note": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-note/-/sheets-note-0.19.0.tgz", + "integrity": "sha512-FwsMpEjGAPNmHo3vV33zhQ7WF3D6OGRCPxwqPiiz1HxXG47XxTctnnl6bEIGdHD7134bnOyXdjBufIoa4C1BhA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-note-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-note-ui/-/sheets-note-ui-0.19.0.tgz", + "integrity": "sha512-GeYFDKEnBHD1eZjEeBaqy2T6hvimbBkGR1vQKxxQspSgRZ+kV7pUiGS3mj5+oU9Yj6IV0Sf8Io3XLSCc44qgOA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-note": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-numfmt": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-numfmt/-/sheets-numfmt-0.19.0.tgz", + "integrity": "sha512-89zwgMLYSgOZWWZAg+PQDU7eE9QLsRCM9TBOgTHpfJsqFpY30pzHAA8O6UbwIrcclCMwapA1f0ifY2HDOYm3/Q==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-numfmt-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-numfmt-ui/-/sheets-numfmt-ui-0.19.0.tgz", + "integrity": "sha512-yGjyV8zeZPkhg5OjDkMw/ozTL35Im5ahbJl1apLW+flg5VDHTd+6y4cS7kZWYAixoKpFPkPpg3sRWF8PG3k4vg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-numfmt": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-sort": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-sort/-/sheets-sort-0.19.0.tgz", + "integrity": "sha512-8agzitALs4ale8x0u137hDEElkkpTZrxo0WUk56aoFXZvTtpH1KN6k3GXNonYSDNTYbCSPaDbillLrZCKCHUFQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs/sheets-sort-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-sort-ui/-/sheets-sort-ui-0.19.0.tgz", + "integrity": "sha512-XCqh0v0kqkM8eyehsdR6GLWOu3ogMZrKhiOtA6GTKHBJHHXR3L2/cDJLRoPM1sN+EcTx4kcf12w621sD6RoLrA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-sort": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-table": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-table/-/sheets-table-0.19.0.tgz", + "integrity": "sha512-TivihE6gGN5D+Y6UNEyJwkt26Y5vG8o2nxBklENWfg0ngrMkVBN7bbXNmnnmJiJXEotE/TxNIG2bWoNmQFcwCw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/sheets": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-table-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-table-ui/-/sheets-table-ui-0.19.0.tgz", + "integrity": "sha512-ves8Dz8LmnxQnwgU/XpOP13Vex5ZhzbXRgdtFd/lqnuBAkjujh1rAJ+HSvAk4IOy/MmAQJIJUtupnVjEgfd72w==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-formula-ui": "0.19.0", + "@univerjs/sheets-sort": "0.19.0", + "@univerjs/sheets-table": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-thread-comment": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-thread-comment/-/sheets-thread-comment-0.19.0.tgz", + "integrity": "sha512-ZE9ISSRaKa1vsb6GRE/aLVE7exg1+9wYNyXALuTdABDYkl4+9x3LV3o1HQZLD/373fzG0tPwKBTCicjsmTv7qQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/sheets": "0.19.0", + "@univerjs/thread-comment": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-thread-comment-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-thread-comment-ui/-/sheets-thread-comment-ui-0.19.0.tgz", + "integrity": "sha512-QaDj+U7vA6ze0iLGBA9POwJr1fyUrmvW9GUTxsL5P19TOBRqUkC9CcTkqplfnai3bbQfOJbcd8v/NByuA6QFYA==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/sheets": "0.19.0", + "@univerjs/sheets-thread-comment": "0.19.0", + "@univerjs/sheets-ui": "0.19.0", + "@univerjs/thread-comment": "0.19.0", + "@univerjs/thread-comment-ui": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/sheets-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/sheets-ui/-/sheets-ui-0.19.0.tgz", + "integrity": "sha512-fg4qst7n8YIkaV5UPzGJN4yDocjofR6mIyWS9F+8XdQrUlv/PmJvSZBj0++eBnC7hG6weUFb2wFyrVbKOhcbRw==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/engine-formula": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/protocol": "0.1.48", + "@univerjs/sheets": "0.19.0", + "@univerjs/telemetry": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/telemetry": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/telemetry/-/telemetry-0.19.0.tgz", + "integrity": "sha512-g4EKKB1DPJXo3KuY/DHZmtuLTNJY3nleU88FVXDwcP/V+4IB6wXUlo45PWnbKnQnOvHLAsW0wUu4dMrRvdXoiw==", + "dependencies": { + "@univerjs/core": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs/themes": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/themes/-/themes-0.19.0.tgz", + "integrity": "sha512-lUeJBSOlNbilQ8O3/QHTM++4sgNy3kO9laDOJ0A6DbZBQvI1KBfpQrGHxYLIgbCX4aQwaJDBEcbQE7cM4R3/8A==", + "license": "Apache-2.0", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + } + }, + "node_modules/@univerjs/thread-comment": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/thread-comment/-/thread-comment-0.19.0.tgz", + "integrity": "sha512-U0jwX4ZX5oVGhLj/yc4QVMvfB+SiJzX06ukDq9Y68IDF0tkrqeWiyiHvf6kmN0rbgc3Oc36nG9aWOKF4ErZz3A==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/thread-comment-ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/thread-comment-ui/-/thread-comment-ui-0.19.0.tgz", + "integrity": "sha512-YsXNBfgG4eliyRKbdD5ReQ03aFjvY7CmgK2hI8sH/Xob+VQ9dScnD+dIPLPmRUmrrS+KKISTu+M04u/m8R1lzg==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/docs-ui": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@univerjs/thread-comment": "0.19.0", + "@univerjs/ui": "0.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@univerjs/ui": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@univerjs/ui/-/ui-0.19.0.tgz", + "integrity": "sha512-BRxLAsd+8n6p99KWFoz8hI1vi1B3uE3eD9KjSRmehd22LJ7mysGrov2FwMLlFEP8cWdZpyR+W1I6xNi5QfOHgQ==", + "license": "Apache-2.0", + "dependencies": { + "@univerjs/core": "0.19.0", + "@univerjs/design": "0.19.0", + "@univerjs/engine-render": "0.19.0", + "@univerjs/icons": "^1.1.1", + "@wendellhu/redi": "1.1.1", + "localforage": "^1.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/univer" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "rxjs": ">=7.0.0" + } + }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "license": "MIT", + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@wendellhu/redi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@wendellhu/redi/-/redi-1.1.1.tgz", + "integrity": "sha512-y2fuAgHJ2n8sI8Pe/1QtAuPQ6ZbZ9/Dn3uVQI8cctVqLZzp/0OpLM7DSMOU6vmGYXNsIQwsquR91WcxZ4jrRvA==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antd": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/antd/-/antd-6.6.1.tgz", + "integrity": "sha512-QHIHYoUk9N9nJy1T9fyxWKjY0qApdTEDd/6lzqYng8Uryv9FejNmbhKvYF7obGqB+TuLXQsPVF7fOVgyzM1KrQ==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^8.0.1", + "@ant-design/cssinjs": "^2.1.2", + "@ant-design/cssinjs-utils": "^2.1.2", + "@ant-design/fast-color": "^3.0.1", + "@ant-design/icons": "^6.3.2", + "@ant-design/react-slick": "~2.0.0", + "@babel/runtime": "^7.29.2", + "@rc-component/cascader": "~1.22.0", + "@rc-component/checkbox": "~2.0.0", + "@rc-component/collapse": "~1.2.0", + "@rc-component/color-picker": "~3.1.1", + "@rc-component/dialog": "~1.10.0", + "@rc-component/drawer": "~1.4.2", + "@rc-component/dropdown": "~1.0.3", + "@rc-component/form": "~1.8.6", + "@rc-component/image": "~1.10.0", + "@rc-component/input": "~1.3.1", + "@rc-component/input-number": "~1.6.2", + "@rc-component/listy": "~1.2.3", + "@rc-component/mentions": "~1.11.0", + "@rc-component/menu": "~1.4.1", + "@rc-component/motion": "^1.3.3", + "@rc-component/mutate-observer": "^2.0.1", + "@rc-component/notification": "~2.0.7", + "@rc-component/pagination": "~1.4.0", + "@rc-component/picker": "~1.12.0", + "@rc-component/progress": "~1.0.2", + "@rc-component/qrcode": "~2.0.0", + "@rc-component/rate": "~1.0.1", + "@rc-component/resize-observer": "^1.1.2", + "@rc-component/segmented": "~1.3.0", + "@rc-component/select": "~1.10.1", + "@rc-component/slider": "~1.1.1", + "@rc-component/steps": "~1.2.2", + "@rc-component/switch": "~1.0.3", + "@rc-component/table": "~1.11.1", + "@rc-component/tabs": "~1.12.0", + "@rc-component/tooltip": "~1.5.0", + "@rc-component/tour": "~2.4.0", + "@rc-component/tree": "~1.4.0", + "@rc-component/tree-select": "~1.16.1", + "@rc-component/trigger": "^3.10.1", + "@rc-component/upload": "~1.1.1", + "@rc-component/util": "^1.12.0", + "clsx": "^2.1.1", + "dayjs": "^1.11.11", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.14", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.14.tgz", + "integrity": "sha512-JyJ954WzuIR8/FFzX0o5krdSTrBAkcCSRfWSleRsIHSWV+cZe2FI1PKggVkFke1hBldRs+LRxUczzE9iPmgZww==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001809", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", + "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cjk-regex": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/cjk-regex/-/cjk-regex-3.4.0.tgz", + "integrity": "sha512-m+gbmlIP6gAG7tDvo2kpeSPAz/uh5wY5/zx10ymjdpbbiTHNTNoYnP2lCiyqtmbLxwhEdq8/lsVbsy4GTc9oUw==", + "license": "MIT", + "dependencies": { + "regexp-util": "^2.0.3", + "unicode-regex": "^4.2.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/codemirror": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js": { + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.50.0.tgz", + "integrity": "sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crelt": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", + "integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "deprecated": "Active development of CryptoJS has been discontinued. This library is no longer maintained.", + "license": "MIT" + }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/cytoscape": { + "version": "3.34.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.1.tgz", + "integrity": "sha512-Lr0RvH9H75y9ar8h9Toy6u4lxRSCcxUq+hHcQ26sVWo6BnaQp1gwEZOYqwuYTZhyW7npyKnNLP8oJ2p1/3OZ7g==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.22", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.22.tgz", + "integrity": "sha512-1YRnxzt/AabP3GHxnaB9/b+ZScCKu5TeF+co+BWG+lnWVIwEcTFc1FVE0WLNmNO3sA6GGXL40i5qkHfbLzpwrg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/docx-preview": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/docx-preview/-/docx-preview-0.3.7.tgz", + "integrity": "sha512-Lav69CTA/IYZPJTsKH7oYeoZjyg96N0wEJMNslGJnZJ+dMUZK85Lt5ASC79yUlD48ecWjuv+rkcmFt6EVPV0Xg==", + "license": "Apache-2.0", + "dependencies": { + "jszip": ">=3.0.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dompurify": { + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz", + "integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.408", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.408.tgz", + "integrity": "sha512-SLoprcYpJ/OH2v2ps0+N5biv9H4/KBT3+YmmDew64TwK5y9j2wv7pMOFY7IorVkyMtEyLSCRlXKLsNlakeAlPw==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-toolkit": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.51.0.tgz", + "integrity": "sha512-zC2lQGkM7QX+Gm6iM3+WIdZJzthsEd14LvRNJneSO2hzyz/zNBENR8+YXWo1cKxgPBtV6ksPYHELbcwBRzmdCw==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types", + "tests/browser-compat" + ] + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "license": "MIT", + "dependencies": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/framer-motion": { + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.43.0.tgz", + "integrity": "sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.43.0", + "motion-utils": "^12.39.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/franc-min": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/franc-min/-/franc-min-6.2.0.tgz", + "integrity": "sha512-1uDIEUSlUZgvJa2AKYR/dmJC66v/PvGQ9mWfI9nOr/kPpMFyvswK0gPXOwpYJYiYD008PpHLkGfG58SPjQJFxw==", + "license": "MIT", + "dependencies": { + "trigram-utils": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/highlight.js": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.12.0.tgz", + "integrity": "sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/html2pdf.js": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.14.0.tgz", + "integrity": "sha512-yvNJgE/8yru2UeGflkPdjW8YEY+nDH5X7/2WG4uiuSCwYiCp8PZ8EKNiTAa6HxJ1NjC51fZSIEq6xld5CADKBQ==", + "license": "MIT", + "dependencies": { + "dompurify": "^3.3.1", + "html2canvas": "^1.0.0", + "jspdf": "^4.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-mobile": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-5.0.0.tgz", + "integrity": "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==", + "license": "MIT" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "dependencies": { + "string-convert": "^0.2.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jspdf": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.2.1.tgz", + "integrity": "sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "fast-png": "^6.2.0", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.3.1", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/kdbush": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz", + "integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==", + "license": "ISC" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "license": "Apache-2.0", + "dependencies": { + "lie": "3.1.1" + } + }, + "node_modules/localforage/node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/marked": { + "version": "17.0.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.6.tgz", + "integrity": "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mermaid": { + "version": "11.16.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.16.1.tgz", + "integrity": "sha512-TQsq6u22fAn3rek5VOubrhKPo1g5hwC3FXUN9hiyupTckcYiGuuKGkNQrKYwGJkXUxZdojwRG46gsSCFZMDp4g==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.1.2", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.2.0", + "@types/d3": "^7.4.3", + "@upsetjs/venn.js": "^2.0.0", + "cytoscape": "^3.33.3", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.20", + "dompurify": "^3.3.3", + "es-toolkit": "^1.45.1", + "katex": "^0.16.45", + "khroma": "^2.1.0", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" + } + }, + "node_modules/mermaid/node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/motion": { + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.43.0.tgz", + "integrity": "sha512-BQgQbSa9Hn3/mtbib0MK53y6JSANa+YKUKlaYnWzAVDH424RYQ5LVpV3pNiWH00BA2z4ojsSdMzqT7g2FQwjuQ==", + "license": "MIT", + "dependencies": { + "framer-motion": "^12.43.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.43.0.tgz", + "integrity": "sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.39.0" + } + }, + "node_modules/motion-utils": { + "version": "12.39.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz", + "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/n-gram": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/n-gram/-/n-gram-2.0.2.tgz", + "integrity": "sha512-S24aGsn+HLBxUGVAUFOwGpKs7LBcG4RudKU//eWzt/mQ97/NMKQxDWHyHx63UNWk/OOdihgmzoETn1tf5nQDzQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/nanoid": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", + "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/numfmt": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/numfmt/-/numfmt-3.2.6.tgz", + "integrity": "sha512-MXc2KP3j+2usdHTY5/ENUc2S+3BRF/cJqnR6RHeq6LBqKoIZOAQ62DQw974nnaZOencbfkmkTPyTmMnlkCpjzg==", + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/opentype.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz", + "integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==", + "license": "MIT", + "dependencies": { + "string.prototype.codepointat": "^0.2.1", + "tiny-inflate": "^1.0.3" + }, + "bin": { + "ot": "bin/ot" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ot-json1": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ot-json1/-/ot-json1-1.0.2.tgz", + "integrity": "sha512-IhxkqVWQqlkWULoi/Q2AdzKk0N5vQRbUMUwubFXFCPcY4TsOZjmp2YKrk0/z1TeiECPadWEK060sdFdQ3Grokg==", + "license": "ISC", + "dependencies": { + "ot-text-unicode": "4" + } + }, + "node_modules/ot-text-unicode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ot-text-unicode/-/ot-text-unicode-4.0.0.tgz", + "integrity": "sha512-W7ZLU8QXesY2wagYFv47zErXud3E93FGImmSGJsQnBzE+idcPPyo2u2KMilIrTwBh4pbCizy71qRjmmV6aDhcQ==", + "license": "ISC", + "dependencies": { + "unicount": "1.1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", + "license": "MIT" + }, + "node_modules/pako": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz", + "integrity": "sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT", + "optional": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", + "license": "ISC" + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/rbush": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-4.0.1.tgz", + "integrity": "sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==", + "license": "MIT", + "dependencies": { + "quickselect": "^3.0.0" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-is": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "optional": true + }, + "node_modules/regexp-util": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/regexp-util/-/regexp-util-2.0.3.tgz", + "integrity": "sha512-GP6h9OgJmhAZpb3dbNbXTfRWVnGcoMhWRZv/HxgM4/qCVqs1P9ukQdYxaUhjWBSAs9oJ/uPXUUvGT1VMe0Bs0Q==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, + "node_modules/rollup": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sonner": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.8.tgz", + "integrity": "sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, + "node_modules/string.prototype.codepointat": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", + "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==", + "license": "MIT" + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/tailwind-merge": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trigram-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/trigram-utils/-/trigram-utils-2.0.1.tgz", + "integrity": "sha512-nfWIXHEaB+HdyslAfMxSqWKDdmqY9I32jS7GnqpdWQnLH89r6A5sdk3fDVYqGAZ0CrT8ovAFSAo6HRiWcWNIGQ==", + "license": "MIT", + "dependencies": { + "collapse-white-space": "^2.0.0", + "n-gram": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-dedent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz", + "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", + "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-regex": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unicode-regex/-/unicode-regex-4.2.0.tgz", + "integrity": "sha512-fEYz7CCnvHDAdrb8OYAP7qlQCWzXBO5cHXQ3XI+HoZaBpiAwyC6b2nixMGl91yrDYEIRm7NDskgTvnLZ7mqrKQ==", + "license": "MIT", + "dependencies": { + "regexp-util": "^2.0.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/unicount": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicount/-/unicount-1.1.0.tgz", + "integrity": "sha512-RlwWt1ywVW4WErPGAVHw/rIuJ2+MxvTME0siJ6lk9zBhpDfExDbspe6SRlWT3qU6AucNjotPl9qAJRVjP7guCQ==", + "license": "ISC" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, + "node_modules/uuid": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz", + "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zustand": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.15.tgz", + "integrity": "sha512-MpSEjRiBkA9crSYeOUH32rJC7SVqAbm0Fqcqge/bUi2PPoLcBWKOsG+C8mevmpr8TwXHBVkChbbJiyvkE+i/3A==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/src/frontend/src/api.ts b/src/frontend/src/api.ts index 2dbdfe6f..850deb6d 100644 --- a/src/frontend/src/api.ts +++ b/src/frontend/src/api.ts @@ -4,7 +4,7 @@ * Uses v1 unified response envelope. */ -import type { Catalog, ChatItem, ChatMessage, ChunkPreviewResult, EvolutionSummary, KBChunk, KBIndexMode, KBWikiStatus, WikiConfig, MemoryItem, MemoryProfile, MemoryGraphRelation, ResourceItem, AutomationTask, AutomationRun, AutomationNotification, FileConfirmInfo, FileConfirmDecision, DesignPickInfo, OntologyAssetKind, OntologyTagOption } from './types'; +import type { Catalog, ChatItem, ChatMessage, ChunkPreviewResult, EvolutionSummary, JobBrief, KBChunk, KBIndexMode, KBWikiStatus, WikiConfig, MemoryItem, MemoryProfile, MemoryGraphRelation, ResourceItem, AutomationTask, AutomationRun, AutomationNotification, FileConfirmInfo, FileConfirmDecision, DesignPickInfo, OntologyAssetKind, OntologyTagOption } from './types'; import type { EditionAuthUserFields } from './editionApiTypes'; import type { EditionChatDetailFields, EditionCreateProjectFields } from './editionModelTypes'; import { createEditionAccessError } from './editionAccessError'; @@ -253,6 +253,7 @@ function toChatItem(raw: JsonObject): ChatItem { agentName: typeof metadata.agent_name === 'string' ? metadata.agent_name : undefined, planChat: metadata.plan_chat === true ? true : undefined, batchChat: metadata.batch_chat === true ? true : undefined, + workflowChat: metadata.workflow_chat === true ? true : undefined, projectId: typeof raw.project_id === 'string' && raw.project_id ? raw.project_id : undefined, }; } @@ -2357,6 +2358,24 @@ export async function generatePlanStream( }); } +/* ── 批量作业(工作流模式)────────────────────────────────────────── + 状态条的数据源。只读聚合,逐项明细不走这里——几百上千项读进浏览器毫无意义。 */ + +export async function listChatJobs(chatId: string): Promise { + const res = await apiRequest(`/v1/jobs?chat_id=${encodeURIComponent(chatId)}&live=true`); + const data = unwrapData<{ jobs?: JobBrief[] }>(res); + return data?.jobs ?? []; +} + +export async function getJobApi(jobId: string): Promise { + const res = await apiRequest(`/v1/jobs/${encodeURIComponent(jobId)}`); + return unwrapData(res); +} + +export async function cancelJobApi(jobId: string): Promise { + await apiRequest(`/v1/jobs/${encodeURIComponent(jobId)}/cancel`, { method: 'POST' }); +} + export async function listPlans(): Promise { const res = await apiRequest('/v1/plans'); return unwrapData(res); diff --git a/src/frontend/src/components/chat/ChatArea.tsx b/src/frontend/src/components/chat/ChatArea.tsx index dc5acfa5..e64fadeb 100644 --- a/src/frontend/src/components/chat/ChatArea.tsx +++ b/src/frontend/src/components/chat/ChatArea.tsx @@ -53,6 +53,7 @@ const MOBILE_QUICK_TASKS = [ ] as const; import { InputArea } from './InputArea'; import { PlanProgressStrip } from './PlanProgressStrip'; +import { JobProgressStrip } from './JobProgressStrip'; import { FileConfirmBar } from './FileConfirmBar'; import { DesignPickerCard } from './DesignPickerCard'; import { ChatShareBanner } from './ChatShareBanner'; @@ -593,6 +594,8 @@ export function ChatArea({
{!planMode && } + {/* 后台作业状态条:工作流模式下作业跑在后台,没有它就完全看不出在不在跑 */} + {shareAccessLevel === 'read' ? ( diff --git a/src/frontend/src/components/chat/InputArea.tsx b/src/frontend/src/components/chat/InputArea.tsx index 4ec4d4b8..550bad84 100644 --- a/src/frontend/src/components/chat/InputArea.tsx +++ b/src/frontend/src/components/chat/InputArea.tsx @@ -5,7 +5,7 @@ import { DUR, EASE } from '../../utils/motionTokens'; import { FileImageOutlined, FileTextOutlined, CloudDownloadOutlined, AppstoreOutlined, FolderOutlined, FolderOpenOutlined, FolderAddOutlined, RobotOutlined, - OrderedListOutlined, ThunderboltOutlined, ApiOutlined, SyncOutlined, + OrderedListOutlined, ThunderboltOutlined, ApiOutlined, SyncOutlined, PartitionOutlined, LaptopOutlined, CloseOutlined, } from '@ant-design/icons'; import { useChatStore, useFileStore, useUIStore, useCatalogStore, useAuthStore, usePluginStore, useEditionStore } from '../../stores'; @@ -20,7 +20,7 @@ import type { InstalledPluginItem } from '../../types'; import { AgentMentionPopup, useAgentMention } from '../agent'; import { SkillSlashPopup, useSkillSlash, type SlashEntry } from './SkillSlashPopup'; import LoopPlanBar from '../loop/LoopPlanBar'; -import { resolveBatchModeActive } from '../../utils/chatMode'; +import { resolveBatchModeActive, resolveWorkflowModeActive } from '../../utils/chatMode'; import { useFileDropZone } from '../../hooks/useFileDropZone'; import { DropOverlay } from '../common/DropOverlay'; import { ChipChevron } from '../common/ChipChevron'; @@ -60,9 +60,9 @@ interface InputAreaProps { /** Custom "enter plan/batch mode" behavior. The project page passes this in: defer * chat creation until send, no navigation; when omitted, falls back to the default * enterChatMode (switches the current chat in place). */ - onEnterMode?: (mode: 'plan' | 'batch') => void; + onEnterMode?: (mode: 'plan' | 'batch' | 'workflow') => void; /** Currently selected mode (projectComposer project page only; drives the "selected" marker and the indicator pill). */ - activeMode?: 'plan' | 'batch' | null; + activeMode?: 'plan' | 'batch' | 'workflow' | null; } // ── Attachment card keys ──────────────────────────────────────────────── @@ -282,6 +282,7 @@ export function InputArea({ // Batch mode as the composer currently runs it — the persistent batchChat marker is only its // default, so a chat the user took out of batch mode no longer counts as one here. const batchModeOn = resolveBatchModeActive(_currentChat); + const workflowModeOn = resolveWorkflowModeActive(_currentChat); const isSiteChat = !!_currentChat?.siteChat; // Whether the "autonomous loop" entry is shown: normal chat (not plan/batch/project page) // + has the loop capability bit + has lab permission. When eligible it no longer occupies @@ -336,13 +337,20 @@ export function InputArea({ // popup rendering, keeping selectedIndex consistent. const slashEntries = useMemo(() => { const q = input.startsWith('/') ? input.slice(1).toLowerCase() : ''; + // 模式命令排在最前:它切换的是这段对话怎么跑,比挑一个技能更"重",也更常被找。 + // 工作流模式必须由用户显式触发——不触发就不注册 run_job、不注入批量提示词。 + const modeEntries: SlashEntry[] = ( + [{ id: 'workflow', name: 'workflow', hint: t('工作流模式:批量作业') }] as const + ) + .filter((m) => !q || m.id.includes(q) || m.hint.toLowerCase().includes(q)) + .map((m) => ({ kind: 'mode' as const, id: m.id, name: m.name, hint: m.hint })); const pluginEntries: SlashEntry[] = installedPlugins .filter((p) => !q || p.name.toLowerCase().includes(q)) .map((p) => ({ kind: 'plugin' as const, id: p.install_id, name: p.name, plugin: p })); const skillEntries: SlashEntry[] = (skills || []) .filter((s) => s.enabled && (!q || s.name.toLowerCase().includes(q))) .map((s) => ({ kind: 'skill' as const, id: s.id, name: s.name })); - return [...pluginEntries, ...skillEntries]; + return [...modeEntries, ...pluginEntries, ...skillEntries]; }, [input, installedPlugins, skills]); // Object URLs for uploaded image files — revoked when files change @@ -498,6 +506,17 @@ export function InputArea({ applySkill(skillId, skillName); } + /** 选中 / 面板里的模式命令(如 /workflow):把已输入的 "/xxx" 抹掉,直接开启该模式。 + * 与技能/插件不同——模式不插 chip,它改的是这段对话怎么跑,开启状态由输入框上方的模式条表示。 */ + function onSlashSelectMode(modeId: string) { + const ed = editorRef.current; + if (ed) removeQueryAtCursor(ed, '/'); + setInput(''); + if (ed) setEditorPlainText(ed, ''); + setSlashVisible(false); + if (modeId === 'workflow') onEnterMode('workflow'); + } + /** Pick a skill from the "+" menu: move the caret to the end first, then insert the chip (the editor may not have focus when the menu closes). */ function onPickSkillFromMenu(skillId: string, skillName: string) { const ed = editorRef.current; @@ -546,16 +565,18 @@ export function InputArea({ /** Whether the composer currently runs in this mode (main composer: live composer state; * project composer: the pending selection passed in via activeMode). */ - function isModeOn(mode: 'plan' | 'batch') { + function isModeOn(mode: 'plan' | 'batch' | 'workflow') { if (projectComposer) return activeMode === mode; - return mode === 'plan' ? planMode : batchModeOn; + if (mode === 'plan') return planMode; + if (mode === 'workflow') return workflowModeOn; + return batchModeOn; } /** Enter plan / batch-execution mode from the "+" menu. The project page customizes this via * the onEnterMode prop (defer chat creation until send, no navigation); the default switches * the current chat to that mode in place — no new chat, no navigation (avoids bouncing the * whole chat back to the home page). */ - function onEnterMode(mode: 'plan' | 'batch') { + function onEnterMode(mode: 'plan' | 'batch' | 'workflow') { if (onEnterModeProp) { onEnterModeProp(mode); return; @@ -566,7 +587,7 @@ export function InputArea({ /** Close a running mode from the ✕ on its composer chip — the single, always-visible way out. * On the project page the pending selection is owned by the parent, so hand the toggle back * to it (onEnterModeProp flips the already-selected mode off). */ - function onCloseMode(mode: 'plan' | 'batch') { + function onCloseMode(mode: 'plan' | 'batch' | 'workflow') { if (onEnterModeProp) { onEnterModeProp(mode); return; @@ -585,7 +606,8 @@ export function InputArea({ e.preventDefault(); const sel = slashEntries[sIdx] || slashEntries[0]; if (sel) { - if (sel.kind === 'plugin' && sel.plugin) onSlashSelectPlugin(sel.plugin); + if (sel.kind === 'mode') onSlashSelectMode(sel.id); + else if (sel.kind === 'plugin' && sel.plugin) onSlashSelectPlugin(sel.plugin); else onSlashSelect(sel.id, sel.name); } return; @@ -770,7 +792,8 @@ export function InputArea({ visible={slashVisible} selectedIndex={sIdx} onSelect={(entry) => { - if (entry.kind === 'plugin' && entry.plugin) onSlashSelectPlugin(entry.plugin); + if (entry.kind === 'mode') onSlashSelectMode(entry.id); + else if (entry.kind === 'plugin' && entry.plugin) onSlashSelectPlugin(entry.plugin); else onSlashSelect(entry.id, entry.name); }} onHover={setSIdx} @@ -820,6 +843,7 @@ export function InputArea({ // off is the job of the ✕ on the mode chip down in the composer bar. const planActive = isModeOn('plan'); const batchActive = isModeOn('batch'); + const workflowActive = isModeOn('workflow'); const activeSuffix = projectComposer ? t('(已选)') : t('(已开启)'); const modeItems = [ ...(isAppAllowed('plan_mode') ? [{ @@ -834,6 +858,15 @@ export function InputArea({ label: batchActive ? t('批量执行{suffix}', { suffix: activeSuffix }) : t('批量执行'), onClick: () => onEnterMode('batch'), }] : []), + // 工作流模式:面对成百上千个同构工作项时,让智能体写一段作业脚本交后台并发跑。 + // 与计划模式/批量执行一样是**用户显式触发**的模式——不进入就不注册 run_job、 + // 不注入批量提示词,普通问答完全不受影响。 + { + key: 'mode-workflow', + icon: , + label: workflowActive ? t('工作流模式{suffix}', { suffix: activeSuffix }) : t('工作流模式'), + onClick: () => onEnterMode('workflow'), + }, ]; const items = [ { key: 'image', icon: , label: t('上传图片'), onClick: () => imageInputRef.current?.click() }, @@ -1096,6 +1129,16 @@ export function InputArea({ /> )} + {!projectComposer && workflowModeOn && ( + } + label={t('工作流模式')} + title={t('工作流模式:面对成百上千个同类工作项时,AI 会写一段作业脚本交给后台并发处理,进度记在台账上,中断可续跑')} + closeLabel={t('关闭工作流模式:切换为普通对话')} + onClose={() => onCloseMode('workflow')} + /> + )} + {showLoopEntry && loopMode && ( } diff --git a/src/frontend/src/components/chat/JobProgressStrip.tsx b/src/frontend/src/components/chat/JobProgressStrip.tsx new file mode 100644 index 00000000..58a6b7e7 --- /dev/null +++ b/src/frontend/src/components/chat/JobProgressStrip.tsx @@ -0,0 +1,250 @@ +import { useEffect, useRef, useState } from 'react'; +import { AnimatePresence, motion } from 'motion/react'; +import { CloseOutlined, LoadingOutlined, StopOutlined, WarningOutlined } from '@ant-design/icons'; +import { DUR, EASE } from '../../utils/motionTokens'; +import { listChatJobs, cancelJobApi, getJobApi } from '../../api'; +import type { JobBrief } from '../../types'; +import { t } from '../../i18n'; + +/* ─────────────────────────────────────────── + Job bar — 工作流模式下钉在输入框上方的后台作业状态条。 + + 为什么必须有:作业跑在后台,主对话是安静的。没有这条,用户根本判断不出 + "工作流模式到底有没有在跑"——只能干等或反复追问,而每次追问都是一轮真实推理。 + 这条走 REST 轮询(一次 SQL 聚合),和模型完全无关,所以看进度是零推理成本的。 + + 与 PlanProgressStrip 的分工:那条是模型自己报的计划步骤,这条是后台作业的 + 真实台账。两条可以同时出现,互不干扰。 + ─────────────────────────────────────────── */ + +/** 作业活着时轮询要跟得上肉眼;不活跃就彻底停表,别给后端凭空加常驻负载。 */ +const POLL_MS = 5000; + +function ProgressRing({ settled, total }: { settled: number; total: number }) { + const R = 7; + const C = 2 * Math.PI * R; + const frac = total > 0 ? Math.min(1, settled / total) : 0; + return ( + + + + + ); +} + +/** 后端给的 ISO 串按 UTC 解释。 + * + * 不能直接 `new Date(s)`:JS 规范里**不带时区偏移**的日期时间串按**本地时区**解析, + * 同一个后端时刻在 UTC+8 的浏览器上会平移 8 小时。后端现在都发带偏移的串,这里做的是 + * 兜底——历史数据或别的入口漏了偏移时,按 UTC 补齐比按本地时区猜要稳。 */ +function parseServerTime(raw?: string | null): number { + if (!raw) return NaN; + const hasZone = /(?:Z|[+-]\d{2}:?\d{2})$/.test(raw.trim()); + return new Date(hasZone ? raw : `${raw.replace(' ', 'T')}Z`).getTime(); +} + +function elapsedLabel(startedAt?: string | null): string { + const ts = parseServerTime(startedAt); + if (!Number.isFinite(ts)) return ''; + // 时钟漂移(浏览器比服务端慢一点)会让刚提交的作业算出负数——按刚开始处理,别显示空白 + const ms = Math.max(0, Date.now() - ts); + const min = Math.floor(ms / 60000); + if (min < 1) return t('不到 1 分钟'); + if (min < 60) return t('{n} 分钟', { n: min }); + return t('{h} 小时 {m} 分', { h: Math.floor(min / 60), m: min % 60 }); +} + +/** 台账还没建起来时的阶段说明 —— 只给一个转圈的菊花,用户没法判断是在启动还是已经僵住。 */ +function phaseLabel(job: JobBrief): string { + if (job.status === 'pending') return t('启动中'); + return t('正在建立工作项台账'); +} + +/** 作业没能善终时的一句话说明(状态条会带着它多留一会儿再消失)。 */ +function endedLabel(job: JobBrief): string { + if (job.status === 'failed') return t('作业失败'); + if (job.status === 'interrupted') return t('作业已失联,可续跑'); + if (job.status === 'cancelled') return t('作业已取消'); + return ''; +} + +export function JobProgressStrip({ chatId }: { chatId: string }) { + const [jobs, setJobs] = useState([]); + const [ended, setEnded] = useState([]); + const [cancelling, setCancelling] = useState(''); + // 轮询在 effect 外也要能读到最新会话,避免切会话后旧定时器把旧数据写回来 + const chatRef = useRef(chatId); + chatRef.current = chatId; + // 见过的在跑作业。列表接口只给未结束的,作业一旦进终态就直接从列表里消失—— + // 没有这份记录,失败的作业就是「状态条忽然不见了」,用户永远不知道它是跑完了还是崩了。 + const seenRef = useRef>(new Set()); + + useEffect(() => { + seenRef.current = new Set(); + setEnded([]); + if (!chatId) { + setJobs([]); + return; + } + let alive = true; + let timer: number | undefined; + + const tick = async () => { + try { + const rows = await listChatJobs(chatId); + if (!alive || chatRef.current !== chatId) return; + setJobs(rows); + + const liveIds = new Set(rows.map((r) => r.job_id)); + const vanished = [...seenRef.current].filter((id) => !liveIds.has(id)); + seenRef.current = liveIds; + // 消失的作业补查一次终态:跑完了就安静收场,没善终就把原因摆到台面上 + for (const id of vanished) { + try { + const final = await getJobApi(id); + if (!alive || chatRef.current !== chatId) return; + if (final.status !== 'completed') { + setEnded((prev) => (prev.some((j) => j.job_id === id) ? prev : [...prev, final])); + } + } catch { + // 查不到就算了:宁可少说一句,也不要编一个结局 + } + } + } catch { + // 轮询失败保持上一帧:状态条抖成空白比慢一拍更糟 + } + if (alive) timer = window.setTimeout(tick, POLL_MS); + }; + void tick(); + + return () => { + alive = false; + if (timer) window.clearTimeout(timer); + }; + }, [chatId]); + + const onCancel = async (jobId: string) => { + setCancelling(jobId); + try { + await cancelJobApi(jobId); + setJobs((prev) => prev.filter((j) => j.job_id !== jobId)); + } catch { + // 取消失败就让下一轮轮询把真实状态刷回来 + } finally { + setCancelling(''); + } + }; + + return ( + + {jobs.map((job) => { + const s = job.stats || ({} as JobBrief['stats']); + const total = s.total ?? 0; + const settled = s.settled ?? 0; + const pct = total > 0 ? Math.floor((settled * 100) / total) : 0; + const elapsed = elapsedLabel(job.started_at || job.created_at); + return ( + + {total > 0 + ? + : } + + {t('后台作业')} + {job.name || t('批量作业')} + + {total > 0 ? ( + <> + · + {settled}/{total}({pct}%) + + ) : ( + /* 台账还是空的:说清楚现在在哪一步,别让用户对着一个菊花猜 */ + <> + · + {phaseLabel(job)} + + )} + {/* 失败数是最该被看见的数:它决定用户要不要现在就叫停 */} + {s.failed > 0 && ( + <> + · + {t('失败 {n}', { n: s.failed })} + + )} + {elapsed && ( + <> + · + {t('已运行 {d}', { d: elapsed })} + + )} + + + + ); + })} + + {/* 没善终的作业:不能就这么无声消失——用户得知道它停了、停在哪、怎么接着跑 */} + {ended.map((job) => { + const s = job.stats || ({} as JobBrief['stats']); + const total = s.total ?? 0; + const settled = s.settled ?? 0; + return ( + + + {t('后台作业')} + {job.name || t('批量作业')} + · + {endedLabel(job)} + {total > 0 && ( + <> + · + {t('已完成 {n}/{m}', { n: settled, m: total })} + + )} + {job.error && {job.error}} + + + + ); + })} + + ); +} diff --git a/src/frontend/src/components/chat/SkillSlashPopup.tsx b/src/frontend/src/components/chat/SkillSlashPopup.tsx index 73d1bbcb..d9d3b5ef 100644 --- a/src/frontend/src/components/chat/SkillSlashPopup.tsx +++ b/src/frontend/src/components/chat/SkillSlashPopup.tsx @@ -1,15 +1,18 @@ import { useEffect, useRef, useState } from 'react'; import { AnimatePresence, motion } from 'motion/react'; -import { AppstoreOutlined, BulbOutlined } from '@ant-design/icons'; +import { AppstoreOutlined, BulbOutlined, PartitionOutlined } from '@ant-design/icons'; import { usePopupFlip } from '../../hooks/usePopupFlip'; import { t } from '../../i18n'; import type { InstalledPluginItem } from '../../types'; export type SlashEntry = { - kind: 'skill' | 'plugin'; + /** mode:切换对话模式的命令(如 /workflow 进入工作流模式),选中即开启,不插入 chip。 */ + kind: 'skill' | 'plugin' | 'mode'; id: string; name: string; plugin?: InstalledPluginItem; + /** mode 条目的说明,渲染在名称右侧(技能/插件条目不用)。 */ + hint?: string; }; interface SkillSlashPopupProps { @@ -57,9 +60,13 @@ export function SkillSlashPopup({ entries, visible, selectedIndex, onSelect, onH > {entry.kind === 'plugin' ? - : } + : entry.kind === 'mode' + ? + : } {entry.name} + {entry.hint && {entry.hint}} {entry.kind === 'plugin' && {t('插件')}} + {entry.kind === 'mode' && {t('模式')}}
))} diff --git a/src/frontend/src/components/chat/index.ts b/src/frontend/src/components/chat/index.ts index 961ca58d..9b192488 100644 --- a/src/frontend/src/components/chat/index.ts +++ b/src/frontend/src/components/chat/index.ts @@ -8,6 +8,7 @@ export { DesignPickerCard } from './DesignPickerCard'; export { PromptHubPanel } from './PromptHubPanel'; export { PlanCard } from './PlanCard'; export { PlanProgressStrip } from './PlanProgressStrip'; +export { JobProgressStrip } from './JobProgressStrip'; export { OntologyRevisionPanel } from './OntologyRevisionPanel'; export { OntologyReviewTrigger } from './OntologyReviewTrigger'; export { EvolutionCard } from './EvolutionCard'; diff --git a/src/frontend/src/components/projects/ProjectDetailPanel.tsx b/src/frontend/src/components/projects/ProjectDetailPanel.tsx index 7f858061..d3d282f3 100644 --- a/src/frontend/src/components/projects/ProjectDetailPanel.tsx +++ b/src/frontend/src/components/projects/ProjectDetailPanel.tsx @@ -48,7 +48,8 @@ export default function ProjectDetailPanel({ projectId, onBack, handleFileSelect const setCurrentChatId = useChatStore((state) => state.setCurrentChatId); const updateStore = useChatStore((state) => state.updateStore); const setPendingFirstMessage = useChatStore((state) => state.setPendingFirstMessage); - const [pendingMode, setPendingMode] = useState<'plan' | 'batch' | null>(null); + // 'workflow' 是工作流模式(批量作业)——少了它,InputArea 的 onEnterMode 回调类型对不上,tsc 直接编不过 + const [pendingMode, setPendingMode] = useState<'plan' | 'batch' | 'workflow' | null>(null); const inputRef = useRef(null); const fileInputRef = useRef(null); diff --git a/src/frontend/src/hooks/useChatInit.ts b/src/frontend/src/hooks/useChatInit.ts index 6f24ec53..d26e844b 100644 --- a/src/frontend/src/hooks/useChatInit.ts +++ b/src/frontend/src/hooks/useChatInit.ts @@ -422,6 +422,10 @@ export function useChatInit() { ...(typeof localSnapshot.chats[id]?.batchModeActive === 'boolean' ? { batchModeActive: localSnapshot.chats[id].batchModeActive } : {}), + workflowChat: meta.workflow_chat === true ? true : undefined, + ...(typeof localSnapshot.chats[id]?.workflowModeActive === 'boolean' + ? { workflowModeActive: localSnapshot.chats[id].workflowModeActive } + : {}), automationTaskId: typeof meta.automation_task_id === 'string' ? meta.automation_task_id : undefined, automationRun: meta.automation_run === true ? true : undefined, // When the backend session hasn't bound project_id (e.g. bound locally via the input-box dropdown, not yet persisted with a message), diff --git a/src/frontend/src/hooks/useStreaming.ts b/src/frontend/src/hooks/useStreaming.ts index b854925e..02c0a4ca 100644 --- a/src/frontend/src/hooks/useStreaming.ts +++ b/src/frontend/src/hooks/useStreaming.ts @@ -5,7 +5,7 @@ import { authFetch, getFollowUpQuestions, regenerateMessage, editAndRegenerate, import { processPlanExecuteStream, processPlanGenerateStream } from './usePlanMode'; import { uploadFileToOSS } from '../utils/fileParser'; import { inferBusinessTopic } from '../utils/history'; -import { resolveBatchModeActive } from '../utils/chatMode'; +import { resolveBatchModeActive, resolveWorkflowModeActive } from '../utils/chatMode'; import { useChatStore, useAuthStore, useCatalogStore, useChatModeStore, useFileStore, useUIStore, useBatchStore, useModelCapabilitiesStore } from '../stores'; import { useProjectStore } from '../stores/projectStore'; import { isThinkingMode } from '../stores/chatStore'; @@ -340,6 +340,7 @@ export function useStreaming( ...(chat.agentName ? { agent_name: chat.agentName } : {}), ...(chat.planChat ? { plan_chat: true } : {}), ...(chat.batchChat ? { batch_chat: true } : {}), + ...(chat.workflowChat ? { workflow_chat: true } : {}), title_manually_set: true, }, }), @@ -513,6 +514,7 @@ export function useStreaming( const currentChat = useChatStore.getState().store.chats[currentChatId]; const agentId = (currentChat as any)?.agentId || undefined; const batchChat = resolveBatchModeActive(currentChat); + const workflowChat = resolveWorkflowModeActive(currentChat); const modelCaps = useModelCapabilitiesStore.getState(); const selectedModelProviderId = modelCaps.capabilities.user_model_switch_enabled ? modelCaps.selectedModelProviderId @@ -572,6 +574,7 @@ export function useStreaming( mention_name: currentMention.name, } : {}), ...(batchChat ? { batch_chat: true } : {}), + ...(workflowChat ? { workflow_chat: true } : {}), // Project mount: read from the chat's own projectId (the frontend binds it when // creating/fetching the session). When the chat has no bound project, fall back to // useProjectStore.currentProjectId — this only applies to the first message sent while diff --git a/src/frontend/src/i18n/en/chat.ts b/src/frontend/src/i18n/en/chat.ts index 8557e893..233e6716 100644 --- a/src/frontend/src/i18n/en/chat.ts +++ b/src/frontend/src/i18n/en/chat.ts @@ -69,6 +69,12 @@ export const CHAT_DICT: Record = { '批量执行模式': 'Batch Mode', '批量执行模式:描述要批量处理的对象与任务,AI 会自动生成可确认的执行计划': 'Batch mode: describe objects and tasks, AI will generate a confirmable execution plan', '批量执行': 'Batch Execution', + '工作流模式': 'Workflow Mode', + '工作流模式{suffix}': 'Workflow Mode{suffix}', + '工作流模式:批量作业': 'Workflow mode: batch jobs', + '工作流模式:面对成百上千个同类工作项时,AI 会写一段作业脚本交给后台并发处理,进度记在台账上,中断可续跑': + 'Workflow mode: for hundreds of similar work items, AI writes a job script that runs concurrently in the background; progress is tracked in a ledger and can resume after an interruption', + '关闭工作流模式:切换为普通对话': 'Exit workflow mode: back to a normal conversation', '站点建站模式:描述你想要的网站,AI 会生成静态站点并一键发布上线': 'Site mode: describe the website you want and AI will generate a static site and publish it in one click', '本对话属于项目「{name}」': 'This chat belongs to project "{name}"', '查看项目「{name}」': 'View project "{name}"', @@ -276,4 +282,20 @@ export const CHAT_DICT: Record = { '修改失败,请重试': 'Edit failed — please retry', '删除失败,请重试': 'Delete failed — please retry', '松开即可添加为附件': 'Release to attach the files', + // Job bar: the background batch job running above the composer. + '模式': 'Mode', + '后台作业': 'Background job', + '批量作业': 'Batch job', + '失败 {n}': '{n} failed', + '已运行 {d}': 'Running {d}', + '取消后台作业': 'Cancel background job', + '{n} 分钟': '{n} min', + '{h} 小时 {m} 分': '{h}h {m}m', + '不到 1 分钟': 'under 1 min', + '启动中': 'Starting', + '正在建立工作项台账': 'Building the work ledger', + '作业失败': 'Job failed', + '作业已失联,可续跑': 'Job lost contact — resumable', + '作业已取消': 'Job cancelled', + '已完成 {n}/{m}': '{n}/{m} done', }; diff --git a/src/frontend/src/stores/chatStore.ts b/src/frontend/src/stores/chatStore.ts index 6b83610c..71ed90d5 100644 --- a/src/frontend/src/stores/chatStore.ts +++ b/src/frontend/src/stores/chatStore.ts @@ -306,11 +306,11 @@ interface ChatState { * place — no new chat, no navigation — avoiding "the whole chat jumping back to home". * - Default (app center): reuse the current chat in place if it's empty, otherwise create a * new chat in that mode. */ - enterChatMode: (mode: 'plan' | 'batch', opts?: { inPlace?: boolean }) => void; + enterChatMode: (mode: 'plan' | 'batch' | 'workflow', opts?: { inPlace?: boolean }) => void; /** Leave plan / batch mode on the current chat and continue as an ordinary conversation. * The historical planChat / batchChat classification is kept (plan cards and batch history * stay recognisable); only the composer/routing flag is turned off. */ - exitChatMode: (mode: 'plan' | 'batch') => void; + exitChatMode: (mode: 'plan' | 'batch' | 'workflow') => void; /** Enter "site building" mode (Lab → Sites): create a new siteChat session and switch to the * main chat, fully reusing the main-chat composer (attachments / projects / "+" menu). * Mutually exclusive with plan / batch. */ @@ -780,17 +780,22 @@ export const useChatStore = create((set, get) => { : {}), }; const nextChat: ChatItem = { ...base, id: targetId, updatedAt: now }; - // Plan / batch are mutually exclusive - if (planChat) { + // Plan / batch / workflow 三者互斥:进入一个就清掉另外两个的标记 + delete nextChat.planChat; + delete nextChat.planModeActive; + delete nextChat.batchChat; + delete nextChat.batchModeActive; + delete nextChat.workflowChat; + delete nextChat.workflowModeActive; + if (mode === 'plan') { nextChat.planChat = true; nextChat.planModeActive = true; - delete nextChat.batchChat; - delete nextChat.batchModeActive; + } else if (mode === 'workflow') { + nextChat.workflowChat = true; + nextChat.workflowModeActive = true; } else { nextChat.batchChat = true; nextChat.batchModeActive = true; - delete nextChat.planChat; - delete nextChat.planModeActive; } const next: ChatStoreData = { chats: { ...store.chats, [targetId]: nextChat }, @@ -822,11 +827,13 @@ export const useChatStore = create((set, get) => { const { currentChatId, currentUserId, store } = get(); const chat = store.chats[currentChatId]; if (!chat) return; + const patch = + mode === 'workflow' ? { workflowModeActive: false } : { batchModeActive: false }; const next: ChatStoreData = { ...store, chats: { ...store.chats, - [currentChatId]: { ...chat, batchModeActive: false }, + [currentChatId]: { ...chat, ...patch }, }, }; set({ store: next, storeRef: next }); diff --git a/src/frontend/src/styles/chat.css b/src/frontend/src/styles/chat.css index eb849002..473c1b25 100644 --- a/src/frontend/src/styles/chat.css +++ b/src/frontend/src/styles/chat.css @@ -3229,6 +3229,16 @@ textarea.jx-composer{ } .jx-slashPopup-icon--plugin { color: var(--color-tint-purple); } .jx-slashPopup-icon--skill { color: var(--color-warning); } +/* 模式命令(/workflow 等):换一个色系,和技能/插件在一眼之内就能分开 */ +.jx-slashPopup-icon--mode { color: var(--color-tint-teal); } +/* 模式条目的说明文字,跟在名称后面;badge 仍然靠 margin-left:auto 顶到最右 */ +.jx-slashPopup-hint { + font-size: 11px; + color: var(--color-text-tertiary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} /* ── ContentEditable editor — replaces textarea ── */ diff --git a/src/frontend/src/styles/plan.css b/src/frontend/src/styles/plan.css index 58f88644..b54aec0c 100644 --- a/src/frontend/src/styles/plan.css +++ b/src/frontend/src/styles/plan.css @@ -606,3 +606,102 @@ align-items:center; gap:6px; } + +/* ═══════════════════════════════════════════ + Job bar (JobProgressStrip) — 后台批量作业状态条, + 与 Plan bar 同族、同高度,可与之并存(上计划、下作业)。 + 颜色全部走令牌:深色模式靠 variables.css 的令牌覆盖生效, + 这里写死任何 hex 都会在深色下变成盲区。 + ═══════════════════════════════════════════ */ +.jx-jobStrip{ + display:flex; + align-items:center; + gap:8px; + height:40px; + padding:0 8px 0 12px; + margin:0 0 8px; + background:var(--color-bg-container); + border:1px solid var(--color-border); + border-radius:var(--radius-md); + box-shadow:0 1px 4px rgba(38,38,38,.04); + overflow:hidden; +} +.jx-jobStrip-ring{ flex:none; } +.jx-jobStrip-spin{ flex:none; font-size:14px; color:var(--color-primary); } + +/* 「后台作业」胶囊:这条状态条最重要的信息是"有东西在后台跑",先把它说清楚 */ +.jx-jobStrip-badge{ + flex:none; + padding:1px 7px; + border-radius:var(--radius-sm); + background:var(--color-primary-bg); + color:var(--color-primary); + font-size:11px; + font-weight:600; + line-height:18px; +} +.jx-jobStrip-title{ + flex:none; + max-width:34%; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + font-size:13px; + font-weight:600; + color:var(--color-text); +} +.jx-jobStrip-sep{ flex:none; color:var(--color-text-placeholder); } +.jx-jobStrip-count, +.jx-jobStrip-elapsed{ + flex:none; + font-family:var(--font-family-number); + font-size:12px; + color:var(--color-text-tertiary); + font-variant-numeric:tabular-nums; +} +.jx-jobStrip-fail{ + flex:none; + font-size:12px; + color:var(--color-error); + font-variant-numeric:tabular-nums; +} +.jx-jobStrip-cancel{ + flex:none; + margin-left:auto; + width:28px; + height:28px; + display:flex; + align-items:center; + justify-content:center; + border:none; + border-radius:var(--radius-sm); + background:none; + color:var(--color-text-tertiary); + cursor:pointer; +} +.jx-jobStrip-cancel:hover:not(:disabled){ + background:var(--color-bg-gray); + color:var(--color-error); +} +.jx-jobStrip-cancel:disabled{ opacity:.5; cursor:default; } +.jx-jobStrip-cancel:focus-visible{ + outline:2px solid var(--color-primary); + outline-offset:-2px; +} + +/* 没善终的作业:同一条形态,只换成告警色底——它已经不动了,别再假装在跑 */ +.jx-jobStrip--ended{ + background:var(--color-error-light); + border-color:var(--color-error-bg); +} +.jx-jobStrip-warn{ flex:none; font-size:14px; color:var(--color-error); } +/* 失败原因可能很长:让它独占剩余宽度并截断,完整文案交给 title 悬浮 */ +.jx-jobStrip-reason{ + flex:1 1 auto; + min-width:0; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + font-size:12px; + color:var(--color-text-tertiary); +} diff --git a/src/frontend/src/types.ts b/src/frontend/src/types.ts index 9fbbd985..f41b535d 100755 --- a/src/frontend/src/types.ts +++ b/src/frontend/src/types.ts @@ -474,6 +474,12 @@ export interface ChatItem { * (batch chats start in batch mode); false records that the user closed the mode from * the composer chip / "+" menu and continues as an ordinary conversation. */ batchModeActive?: boolean; + /** 历史标记:这段对话用过「工作流模式」(批量作业)。与 planChat/batchChat 同性质—— + * 用户离开模式后仍保留,便于在列表里认出这类会话;它本身不决定下一条消息怎么发。 */ + workflowChat?: boolean; + /** 这段对话当前的工作流模式开关(用户在 / 命令或「+」菜单里选的)。undefined 走历史默认, + * false 表示用户显式关掉了。只有它为 true 才会注册 run_job 并注入批量作业提示词。 */ + workflowModeActive?: boolean; /** Whether this chat was created via the site-building ("站点建站") entry (Lab → Sites) */ siteChat?: boolean; /** Automation task ID — set on virtual sidebar entries for automation tasks */ @@ -1330,6 +1336,35 @@ export interface Plan { updated_at: string; } +/* ───── 批量作业(工作流模式)───── */ + +/** 台账聚合:分母是 total,**别用 done 当分母**——查无/失败也是结算掉的。 */ +export interface JobStats { + total: number; + done: number; + pending: number; + failed: number; + not_found: number; + needs_review: number; + running: number; + settled: number; + remaining: number; +} + +export interface JobBrief { + job_id: string; + chat_id: string; + name: string; + status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'interrupted'; + stats: JobStats; + usage: { calls?: number; tokens?: number }; + budget_left: { calls_left?: number; tokens_left?: number; seconds_left?: number }; + error: string; + created_at?: string | null; + started_at?: string | null; + completed_at?: string | null; +} + /* ───── Config platform types ───── */ export interface UsageLogEntry { diff --git a/src/frontend/src/utils/chatMode.ts b/src/frontend/src/utils/chatMode.ts index 99a1731c..67e38609 100644 --- a/src/frontend/src/utils/chatMode.ts +++ b/src/frontend/src/utils/chatMode.ts @@ -2,6 +2,7 @@ import type { ChatItem } from '../types'; type PlanModeChat = Pick; type BatchModeChat = Pick; +type WorkflowModeChat = Pick; /** Resolve the composer routing mode independently from the chat's historical plan marker. */ export function resolvePlanModeActive(chat?: PlanModeChat): boolean { @@ -18,6 +19,14 @@ export function resolveBatchModeActive(chat?: BatchModeChat): boolean { return chat.batchChat === true; } +/** 工作流模式(批量作业)同理:历史标记只是默认值,显式 workflowModeActive === false 表示用户关掉了。 + * 这是**用户显式触发**的模式——不触发就不注册 run_job、不注入批量提示,普通问答完全不受影响。 */ +export function resolveWorkflowModeActive(chat?: WorkflowModeChat): boolean { + if (!chat) return false; + if (typeof chat.workflowModeActive === 'boolean') return chat.workflowModeActive; + return chat.workflowChat === true; +} + /** History loading may restore the legacy default, but must respect an explicit user opt-out. */ export function shouldRestorePlanModeFromHistory(chat?: PlanModeChat): boolean { return chat?.planModeActive !== false;