From f2439ee5a3fa079409ff24bb33e9908c820fe35e Mon Sep 17 00:00:00 2001 From: NathanAlex7 <18898599977@163.com> Date: Mon, 7 Sep 2026 02:28:37 +0800 Subject: [PATCH] feat: restore risk assessment workspace and improve page scrolling --- .gitignore | 15 + Dockerfile | 63 + QUICK_START.md | 137 ++ START_GUIDE.md | 72 + backend/.env.example | 30 +- backend/api/chat.py | 151 +- backend/api/review_assist.py | 1171 +++++++++++ backend/api/risk_assess.py | 617 ++++++ backend/api/xingbang_api.py | 145 -- backend/api/xingbang_router.py | 372 ++++ backend/api/xingbang_unified.py | 432 ++++ backend/api/xingbang_v2.py | 569 ++++++ backend/app.py | 23 +- backend/data_layer/__init__.py | 20 + backend/data_layer/erp_connector.py | 301 +++ backend/data_layer/history_store.py | 209 ++ backend/data_layer/reviewer_profile.py | 262 +++ backend/data_layer/team_experience.py | 195 ++ backend/graph/agent.py | 93 +- backend/graph/memory_indexer.py | 6 + backend/graph/raw_trace.py | 274 +++ backend/kaiju_history_data.py | 317 +++ backend/start_backend.bat | 18 + backend/start_backend.sh | 22 + backend/xingbang/__init__.py | 23 - backend/xingbang/agents.py | 186 -- backend/xingbang/config.py | 173 -- backend/xingbang/evolution/__init__.py | 18 - backend/xingbang/evolution/optimizer.py | 161 -- backend/xingbang/evolution/reward.py | 82 - backend/xingbang/evolution/trajectory.py | 123 -- backend/xingbang/workflow.py | 237 --- docker-compose.yml | 58 + docker-entrypoint.sh | 24 + package-lock.json | 2024 ++++++++++--------- package.json | 20 +- src/app/globals.css | 21 +- src/app/historical-data/page.tsx | 370 ++++ src/app/page.tsx | 2 +- src/app/policy-verification/page.tsx | 706 +++++++ src/app/report-assistant/page.tsx | 370 ++++ src/app/review-allocation/page.tsx | 698 +++++++ src/app/review-assist/page.tsx | 1282 ++++++++++++ src/app/risk-assess-simple/page.tsx | 78 + src/app/risk-assess/page.tsx | 706 +++++++ src/app/rule-engine/page.tsx | 273 +++ src/app/trajectory/page.tsx | 107 + src/app/xingbang-unified/page.tsx | 186 ++ src/app/xingbang-v2/page.tsx | 370 ++++ src/app/xingbang/page.tsx | 545 ++--- src/components/chat/ChatMessage.tsx | 8 + src/components/chat/ChatPanel.tsx | 31 +- src/components/chat/RawPayloadCard.tsx | 212 ++ src/components/chat/RawTraceExplorer.tsx | 591 ++++++ src/components/layout/Navbar.tsx | 8 +- src/components/layout/Sidebar.tsx | 111 +- src/components/trajectory/AgentLoopFlow.tsx | 279 +++ src/components/xingbang/xingbang.css | 216 -- src/lib/api.ts | 118 +- src/lib/store.tsx | 52 + start.py | 130 ++ start.sh | 26 + start_all.bat | 34 + start_xingbang.sh | 86 + tsconfig.json | 2 +- 65 files changed, 13612 insertions(+), 2649 deletions(-) create mode 100644 Dockerfile create mode 100644 QUICK_START.md create mode 100644 START_GUIDE.md create mode 100644 backend/api/review_assist.py create mode 100644 backend/api/risk_assess.py delete mode 100644 backend/api/xingbang_api.py create mode 100644 backend/api/xingbang_router.py create mode 100644 backend/api/xingbang_unified.py create mode 100644 backend/api/xingbang_v2.py create mode 100644 backend/data_layer/__init__.py create mode 100644 backend/data_layer/erp_connector.py create mode 100644 backend/data_layer/history_store.py create mode 100644 backend/data_layer/reviewer_profile.py create mode 100644 backend/data_layer/team_experience.py create mode 100644 backend/graph/raw_trace.py create mode 100644 backend/kaiju_history_data.py create mode 100644 backend/start_backend.bat create mode 100644 backend/start_backend.sh delete mode 100644 backend/xingbang/__init__.py delete mode 100644 backend/xingbang/agents.py delete mode 100644 backend/xingbang/config.py delete mode 100644 backend/xingbang/evolution/__init__.py delete mode 100644 backend/xingbang/evolution/optimizer.py delete mode 100644 backend/xingbang/evolution/reward.py delete mode 100644 backend/xingbang/evolution/trajectory.py delete mode 100644 backend/xingbang/workflow.py create mode 100644 docker-compose.yml create mode 100644 docker-entrypoint.sh create mode 100644 src/app/historical-data/page.tsx create mode 100644 src/app/policy-verification/page.tsx create mode 100644 src/app/report-assistant/page.tsx create mode 100644 src/app/review-allocation/page.tsx create mode 100644 src/app/review-assist/page.tsx create mode 100644 src/app/risk-assess-simple/page.tsx create mode 100644 src/app/risk-assess/page.tsx create mode 100644 src/app/rule-engine/page.tsx create mode 100644 src/app/trajectory/page.tsx create mode 100644 src/app/xingbang-unified/page.tsx create mode 100644 src/app/xingbang-v2/page.tsx create mode 100644 src/components/chat/RawPayloadCard.tsx create mode 100644 src/components/chat/RawTraceExplorer.tsx create mode 100644 src/components/trajectory/AgentLoopFlow.tsx delete mode 100644 src/components/xingbang/xingbang.css create mode 100644 start.py create mode 100644 start.sh create mode 100644 start_all.bat create mode 100644 start_xingbang.sh diff --git a/.gitignore b/.gitignore index 7ad9179..981513b 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,18 @@ backend/xingbang_evolution/ backend/diag_single_agent.py backend/run_full_workflow.py backend/verify_offline.py + +# Local credentials, working backups and runtime data +.env.* +!.env.example +**/.env.* +!**/.env.example +_backup*/ +*.pid +*.tsbuildinfo +backend/raw_logs/ +backend/backend/ +backend/xingbang_data/ +ori_meetingfile/ +.fixui_done +nul diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..020bf85 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,63 @@ +# 兴邦金租 AI Native 智能决策系统 - Docker 镜像 + +# ============ Stage 1: Frontend Build ============ +FROM node:18-alpine AS frontend-builder + +WORKDIR /app/frontend + +# 复制前端依赖文件 +COPY package*.json ./ +RUN npm install + +# 复制前端代码并构建 +COPY src ./src +COPY public ./public +COPY next.config.js ./ +COPY tsconfig.json ./ +COPY tailwind.config.ts ./ +COPY postcss.config.js ./ + +RUN npm run build + +# ============ Stage 2: Backend + Runtime ============ +FROM python:3.11-slim + +WORKDIR /app + +# 安装系统依赖 +RUN apt-get update && apt-get install -y \ + gcc \ + g++ \ + libpq-dev \ + && rm -rf /var/lib/apt/lists/* + +# 复制后端依赖文件 +COPY backend/requirements.txt ./backend/ +RUN pip install --no-cache-dir -r backend/requirements.txt + +# 复制后端代码 +COPY backend ./backend + +# 复制前端构建产物 +COPY --from=frontend-builder /app/frontend/.next ./frontend/.next +COPY --from=frontend-builder /app/frontend/public ./frontend/public +COPY --from=frontend-builder /app/frontend/package.json ./frontend/ + +# 创建数据目录 +RUN mkdir -p /app/backend/xingbang_data/reviewers \ + /app/backend/xingbang_data/history \ + /app/backend/xingbang_data/team_experience \ + /app/backend/logs + +# 设置环境变量 +ENV PYTHONPATH=/app/backend +ENV NODE_ENV=production + +# 暴露端口 +EXPOSE 8002 3000 + +# 启动脚本 +COPY docker-entrypoint.sh /app/ +RUN chmod +x /app/docker-entrypoint.sh + +ENTRYPOINT ["/app/docker-entrypoint.sh"] diff --git a/QUICK_START.md b/QUICK_START.md new file mode 100644 index 0000000..fce6559 --- /dev/null +++ b/QUICK_START.md @@ -0,0 +1,137 @@ +# 兴邦金租 AI Native 智能决策系统 - 5分钟快速上手 + +## 🎯 最快启动方式 + +### Windows用户 +```bash +start_xingbang.bat +``` + +### Linux/Mac用户 +```bash +chmod +x start_xingbang.sh +./start_xingbang.sh +``` + +**就这么简单!** 脚本会自动: +- ✅ 检查Python环境 +- ✅ 创建虚拟环境 +- ✅ 安装依赖 +- ✅ 初始化数据目录 +- ✅ 启动后端服务 + +## 📖 访问系统 + +启动成功后,打开浏览器访问: + +- **API文档**: http://localhost:8002/docs +- **前端界面**: http://localhost:3000/xingbang-unified(需单独启动前端) + +## 🧪 快速测试 + +打开新终端,运行测试脚本: + +```bash +python test_xingbang.py +``` + +测试输出示例: +``` +✅ 后端服务运行中 +✅ 主审员画像: 4 人 +✅ 团队经验: 156 个历史案例 +✅ 助手6 测试通过 + 推荐主审员: 王芳 + 综合评分: 80.9 +``` + +## 🎯 试用功能 + +### 1. 查询主审员画像 + +```bash +curl http://localhost:8002/api/xingbang/unified/data/reviewers +``` + +### 2. 测试主审员智能分配 + +```bash +curl -X POST http://localhost:8002/api/xingbang/unified/assistant6/allocate \ + -H "Content-Type: application/json" \ + -d '{ + "project_industry": "光伏设备制造", + "project_amount": 5000, + "project_complexity": 3 + }' +``` + +### 3. 查看团队经验库 + +```bash +curl http://localhost:8002/api/xingbang/unified/data/team-experience +``` + +## 📚 下一步 + +- **完整功能**: 参考 `PROJECT_OVERVIEW.md` +- **部署到生产**: 参考 `DEPLOYMENT_GUIDE.md` +- **实施细节**: 参考 `IMPLEMENTATION_REPORT.md` + +## ⚙️ 配置ERP连接(可选) + +如果需要连接实际ERP数据库: + +1. 复制环境变量模板: +```bash +cp backend/.env.example backend/.env +``` + +2. 编辑 `backend/.env`,填写ERP连接信息: +```ini +ERP_DB_HOST=your_erp_host +ERP_DB_PORT=5432 +ERP_DB_NAME=xingbang_erp +ERP_DB_USER=readonly_user +ERP_DB_PASSWORD=your_password +ERP_DB_SSL_MODE=require +``` + +3. 重启后端服务 + +**注意**: 不配置ERP时,系统会自动使用Mock数据,功能完全可用。 + +## 🐳 Docker部署(生产环境) + +```bash +# 1. 配置密钥 +mkdir -p secrets +echo "your_erp_password" > secrets/erp_db_password.txt +echo "your_deepseek_key" > secrets/deepseek_api_key.txt + +# 2. 启动容器 +docker-compose up -d + +# 3. 查看日志 +docker-compose logs -f +``` + +## ❓ 常见问题 + +**Q: 端口8002被占用怎么办?** +A: 编辑 `backend/.env`,修改 `BACKEND_PORT=8002` 为其他端口 + +**Q: 没有DeepSeek API Key可以用吗?** +A: 部分助手需要LLM,建议先注册DeepSeek API(免费额度够测试用) + +**Q: 前端页面空白?** +A: 确保在项目根目录运行 `npm run dev` 启动前端 + +**Q: 数据会丢吗?** +A: 所有数据存在 `backend/xingbang_data/` 目录,持久化保存 + +## 📞 获取帮助 + +查看完整文档: +- `PROJECT_OVERVIEW.md` - 项目总览 +- `DEPLOYMENT_GUIDE.md` - 部署指南 +- `IMPLEMENTATION_REPORT.md` - 实施报告 diff --git a/START_GUIDE.md b/START_GUIDE.md new file mode 100644 index 0000000..e7c5e66 --- /dev/null +++ b/START_GUIDE.md @@ -0,0 +1,72 @@ +# mini-OpenClaw 启动指南 + +## 🚀 快速启动(推荐) + +### Windows 用户 +双击运行: +``` +start.bat +``` + +或在终端中: +```bash +./start.sh +``` + +### 手动启动 + +#### 1. 启动后端 +```bash +cd backend +./start_backend.sh # Linux/Mac/Git Bash +# 或 +start_backend.bat # Windows CMD +``` + +#### 2. 启动前端 +```bash +npm run dev +``` + +## ✅ 验证运行状态 + +- 后端:http://localhost:8002 +- 前端:http://localhost:3000 + +## 🔧 问题排查 + +### 问题:Connection error / 代理错误 + +**原因**:系统配置了代理(127.0.0.1:7890)但代理服务未运行 + +**解决方案**: +1. 使用提供的启动脚本(已自动禁用代理) +2. 或启动你的代理服务器(端口 7890) + +### 问题:端口被占用 + +**解决方案**: +```bash +# Windows +netstat -ano | findstr :8002 +taskkill /F /PID + +# Linux/Mac +lsof -ti:8002 | xargs kill -9 +``` + +### 问题:LlamaIndex 警告 + +**解决方案**:确保使用项目虚拟环境 +```bash +cd backend +source venv/Scripts/activate # Git Bash +# 或 +venv\Scripts\activate.bat # Windows CMD +``` + +## 📝 说明 + +- 启动脚本会自动禁用代理设置 +- 后端使用项目虚拟环境(backend/venv) +- 所有依赖已在虚拟环境中安装 diff --git a/backend/.env.example b/backend/.env.example index ec146c1..582eee3 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1,14 +1,20 @@ -# OpenAI API Configuration -OPENAI_API_KEY=sk-xxx -OPENAI_BASE_URL=https://api.openai.com/v1 -MODEL_NAME=gpt-4o-mini +# ============ DeepSeek LLM 配置 ============ +DEEPSEEK_API_KEY=your_deepseek_api_key_here +DEEPSEEK_MODEL=deepseek-chat +DEEPSEEK_BASE_URL=https://api.deepseek.com -# Embedding model -EMBEDDING_MODEL=text-embedding-3-small +# ============ ERP 数据库配置(加密连接)============ +# 注意:生产环境使用 Docker Secrets,不要直接写在 .env 文件中 +ERP_DB_HOST=your_erp_db_host +ERP_DB_PORT=5432 +ERP_DB_NAME=xingbang_erp +ERP_DB_USER=readonly_user # 只读账号 +ERP_DB_PASSWORD=your_readonly_password +ERP_DB_SSL_MODE=require # 强制 SSL 连接 -# Server -HOST=0.0.0.0 -PORT=8002 - -# OpenWeather API Configuration -OPENWEATHER_API_KEY=your_openweather_api_key_here +# ============ 审计日志路径 ============ +ERP_AUDIT_LOG=./logs/erp_access.log + +# ============ 其他配置 ============ +BACKEND_PORT=8002 +FRONTEND_PORT=3000 diff --git a/backend/api/chat.py b/backend/api/chat.py index 93e7d93..6966e6a 100644 --- a/backend/api/chat.py +++ b/backend/api/chat.py @@ -3,6 +3,7 @@ import json import os import traceback +from pathlib import Path from typing import AsyncGenerator from fastapi import APIRouter @@ -80,7 +81,7 @@ async def event_generator(message: str, session_id: str) -> AsyncGenerator[dict, segments: list[dict] = [] # finalized segments current_segment: dict = {"content": "", "tool_calls": []} - async for event in agent_manager.astream(message, history): + async for event in agent_manager.astream(message, history, session_id): event_type = event.get("type", "unknown") if event_type == "retrieval": @@ -186,3 +187,151 @@ async def chat(request: ChatRequest): # Non-streaming fallback result = await agent_manager.ainvoke(request.message, request.session_id) return {"reply": result} + + +@router.get("/chat/raw-traces") +async def get_raw_traces(session_id: str = "default", limit: int = 20): + """获取指定 session 的原始 LLM 调用报文 + + Args: + session_id: 会话 ID + limit: 返回最近 N 条记录 (默认 20) + + Returns: + {"traces": [...]} 原始报文列表,按时间倒序 + """ + try: + from graph.raw_trace import load_raw_traces + + # 从 agent_manager 获取 raw_logs_dir + raw_logs_dir = agent_manager._raw_logs_dir + if not raw_logs_dir: + return {"traces": []} + + traces = load_raw_traces(session_id, raw_logs_dir, limit=limit) + return {"traces": traces} + except Exception as e: + traceback.print_exc() + return {"traces": [], "error": str(e)} + + +@router.get("/chat/trajectory") +async def get_trajectory(session_id: str = "default"): + """获取会话完整交互轨迹 - 包含完整的 human/assistant/tool 消息流 + + Args: + session_id: 会话 ID + + Returns: + { + "session_id": "...", + "messages": [ + {"role": "human", "content": "...", "timestamp": ...}, + {"role": "assistant", "content": "...", "timestamp": ..., "tool_calls": [...]}, + {"role": "tool", "name": "...", "content": "...", "timestamp": ...}, + {"role": "assistant", "content": "...", "timestamp": ...} + ], + "steps": [...] // 保留旧格式用于向后兼容 + } + """ + try: + import json + from graph.raw_trace import load_raw_traces + + raw_logs_dir = agent_manager._raw_logs_dir + if not raw_logs_dir: + # 无 raw_logs_dir,返回空 + return {"session_id": session_id, "messages": [], "steps": []} + + # 优先读取完整消息流日志文件 + msgs_log_file = raw_logs_dir / f"{session_id}.msgs.jsonl" + messages = [] + + if msgs_log_file.exists(): + # 从 msgs.jsonl 读取完整消息流(包含 tool 消息) + with open(msgs_log_file, "r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if line: + try: + msg = json.loads(line) + messages.append(msg) + except json.JSONDecodeError: + continue + else: + # 回退到旧逻辑:从 session 文件读取(无 tool 消息) + session_messages = session_manager.load_session(session_id) + for msg in session_messages: + messages.append({ + "role": msg["role"], + "content": msg["content"], + "timestamp": None, + }) + + # 构建旧格式 steps(向后兼容) + steps = [] + traces = load_raw_traces(session_id, raw_logs_dir, limit=1000) + traces.sort(key=lambda x: x.get("timestamp_start", 0)) + + # 按 user/assistant 消息构建 steps(忽略 tool 消息) + session_messages = session_manager.load_session(session_id) + assistant_count = sum(1 for m in session_messages if m["role"] == "assistant") + + for msg_idx, msg in enumerate(session_messages): + step = { + "role": msg["role"], + "content": msg["content"], + "timestamp": None, + } + + if msg["role"] == "assistant": + llm_calls = [] + assistant_seq = sum(1 for m in session_messages[:msg_idx+1] if m["role"] == "assistant") + traces_per_assistant = len(traces) / max(assistant_count, 1) + start_trace = int((assistant_seq - 1) * traces_per_assistant) + end_trace = int(assistant_seq * traces_per_assistant) + + for i in range(start_trace, min(end_trace, len(traces))): + trace = traces[i] + llm_call = { + "model": trace.get("request", {}).get("model", "unknown"), + "finish_reason": trace.get("finish_reason", "unknown"), + "request_messages": trace.get("request", {}).get("messages", []), + "response_text": "", + "tool_calls_list": [], + "usage": trace.get("usage", {}), + "duration_ms": trace.get("duration_ms", 0), + } + + response_gens = trace.get("response", {}).get("generations", []) + if response_gens: + llm_call["response_text"] = response_gens[0].get("text", "") + msg_data = response_gens[0].get("message", {}) + if isinstance(msg_data, dict) and "tool_calls" in msg_data: + tool_calls = msg_data["tool_calls"] + if tool_calls: + llm_call["tool_calls_list"] = [ + { + "name": tc.get("name", "") if isinstance(tc, dict) else getattr(tc, "name", ""), + "args": tc.get("args", {}) if isinstance(tc, dict) else getattr(tc, "args", {}), + } + for tc in tool_calls + ] + + llm_calls.append(llm_call) + if step["timestamp"] is None: + step["timestamp"] = trace.get("timestamp_start") + + step["llm_calls"] = llm_calls + + steps.append(step) + + return { + "session_id": session_id, + "messages": messages, # 新格式:完整消息流(含 tool) + "steps": steps, # 旧格式:向后兼容 + } + + except Exception as e: + traceback.print_exc() + return {"session_id": session_id, "messages": [], "steps": [], "error": str(e)} diff --git a/backend/api/review_assist.py b/backend/api/review_assist.py new file mode 100644 index 0000000..6dfd908 --- /dev/null +++ b/backend/api/review_assist.py @@ -0,0 +1,1171 @@ +"""评审提效工作台 API + +帮助兴邦金租的项目评审流程提效: +- 评审周期从16-17个工作日压缩 +- 主审员负荷优化(每人3-7个项目排队) +- 看资料/算测算/写报告三大块耗时降低 +""" + +import json +from typing import Any, AsyncGenerator, Optional, List, Dict + +from fastapi import APIRouter, Query +from fastapi.responses import StreamingResponse +from pydantic import BaseModel, Field + +# 导入历史数据模块 +from kaiju_history_data import ( + get_all_projects, + get_projects_filtered, + get_reviewer_stats, +) + +router = APIRouter(prefix="/api/review-assist", tags=["评审提效工作台"]) + + +# ============ Pydantic 模型 ============ + +class ReviewerInfo(BaseModel): + """主审员信息""" + name: str + current_projects: int # 在手项目数 + specialty: str # 专长领域 + pipeline: Optional[List[int]] = None # 在手项目复杂度列表(可选,用于精准负荷估算) + estimated_finish_days: Optional[int] = None # 预计完成天数(可选) + + +class LoadBalanceRequest(BaseModel): + """评审负荷与分配建议请求""" + reviewers: list[ReviewerInfo] + new_project_industry: str = "" # 新项目行业 + new_project_amount: float = 0 # 新项目金额(万元) + + +class DueDiligenceRequest(BaseModel): + """尽调资料预处理请求""" + project_name: str + client_name: str + industry: str + financial_data: dict # 财务数据 + credit_report: dict # 征信报告 + guarantee_info: dict # 担保信息 + external_risks: dict # 外部风险数据 + + +class RentCalculationRequest(BaseModel): + """租金测算辅助校验请求""" + credit_amount: float # 授信金额(万元) + lease_term: int # 租赁期限(月) + interest_rate: float # 年利率(%) + monthly_cashflow: float # 月现金流(万元) + project_type: str = "对公" # 对公/零售 + + +class ReportDraftRequest(BaseModel): + """评审报告初稿生成请求""" + intelligence_card: str # 情报卡内容 + calculation_result: str # 测算结果 + project_name: str + client_name: str + + +class PreScreeningRequest(BaseModel): + """立项前置风险筛查请求""" + project_name: str + client_name: str + industry: str + document_checklist: list[str] # 立项资料清单 + financial_data: dict + similar_cases: list[dict] = [] # 历史相似案例(可选) + + +class AllocateRequest(BaseModel): + """智能分配请求(数据驱动)""" + reviewers: List[ReviewerInfo] + new_project_industry: str + new_project_amount: float # 万元 + new_project_complexity: int = Field(3, ge=1, le=5) # 1-5,默认3 + + +class RecommendationItem(BaseModel): + """单个推荐结果""" + reviewer: str + score: float # 综合评分 + reasons: List[str] # 推荐理由 + est_complete_days: int # 预计完成天数(从现在算起) + industry_match_score: float # 行业匹配分 + load_score: float # 负荷分 + quality_score: float # 质量分 + + +class AllocateResponse(BaseModel): + """智能分配响应""" + reviewers: List[Dict] # 主审员当前状态列表 + recommendation: List[RecommendationItem] # Top-N推荐 + balance_before: float # 分配前负荷方差 + balance_after: float # 分配后负荷方差(假设分配给Top1) + algorithm_version: str # 算法版本标识 + + +# ============ 示例数据(供前端使用)============ + +SAMPLE_DATA = { + "load_balance": { + "reviewers": [ + {"name": "李明", "current_projects": 5, "specialty": "制造业"}, + {"name": "王芳", "current_projects": 3, "specialty": "新能源"}, + {"name": "张强", "current_projects": 7, "specialty": "交通运输"}, + {"name": "刘洋", "current_projects": 4, "specialty": "医疗健康"}, + ], + "new_project_industry": "光伏设备制造", + "new_project_amount": 5000 + }, + "due_diligence": { + "project_name": "阳光光伏设备融资租赁项目", + "client_name": "阳光光伏设备有限公司", + "industry": "光伏设备制造", + "financial_data": { + "registered_capital": 8000, + "established_years": 6, + "asset_liability_ratio": 0.58, + "current_ratio": 1.45, + "net_profit_2024": 1200, + "net_profit_2025": 1450, + "revenue_growth_rate": 0.18 + }, + "credit_report": { + "overdue_count": 0, + "query_count_recent_6m": 4, + "credit_limit": 20000, + "enforcement_records": 0 + }, + "guarantee_info": { + "type": "法人连带担保+设备抵押", + "guarantor": "阳光集团有限公司", + "guarantor_net_assets": 50000 + }, + "external_risks": { + "litigation_records": [], + "negative_news": 0, + "industry_policy": "国家支持光伏产业发展" + } + }, + "rent_calculation": { + "credit_amount": 5000, + "lease_term": 36, + "interest_rate": 5.8, + "monthly_cashflow": 85, + "project_type": "对公" + }, + "report_draft": { + "intelligence_card": "# 客户情报卡\n\n## 基本信息\n- 客户名称:阳光光伏设备有限公司\n- 行业:光伏设备制造\n\n## 财务状况\n- 资产负债率:58%(良好)\n- 流动比率:1.45(符合标准)\n\n## 风险评估\n🟢 低风险:财务指标优良,行业前景好", + "calculation_result": "# 租金测算结果\n\n- 月租金:152.8万元\n- 覆盖率:180%(远超120%标准)\n- 风险等级:低", + "project_name": "阳光光伏设备融资租赁项目", + "client_name": "阳光光伏设备有限公司" + }, + "pre_screening": { + "project_name": "阳光光伏设备融资租赁项目", + "client_name": "阳光光伏设备有限公司", + "industry": "光伏设备制造", + "document_checklist": [ + "营业执照", + "财务报表(近3年)", + "征信报告", + "租赁物清单", + "担保函", + "法人身份证", + "银行流水(近6个月)" + ], + "financial_data": { + "registered_capital": 8000, + "asset_liability_ratio": 0.58, + "net_profit_2025": 1450 + }, + "similar_cases": [ + { + "project_name": "某光伏企业融资租赁(2025年)", + "rejection_reason": "担保方资产不足,要求补充第二担保人", + "lesson": "光伏行业波动大,需要双重担保" + } + ] + } +} + + +# ============ 辅助1:评审负荷与分配建议 ============ + +async def _generate_load_balance(request: LoadBalanceRequest) -> AsyncGenerator[str, None]: + """生成评审负荷与分配建议""" + + # 计算平均负荷 + total_projects = sum(r.current_projects for r in request.reviewers) + avg_load = total_projects / len(request.reviewers) if request.reviewers else 0 + + # 排序:按当前项目数升序 + sorted_reviewers = sorted(request.reviewers, key=lambda r: r.current_projects) + + # 生成 Markdown 内容 + content = f"""# 评审负荷与分配建议 + +## 📊 当前负荷看板 + +| 主审员 | 在手项目数 | 专长领域 | 负荷状态 | +|--------|-----------|---------|---------| +""" + + for r in request.reviewers: + load_status = "🔴 超载" if r.current_projects >= 6 else ("🟡 偏高" if r.current_projects >= 4 else "🟢 正常") + content += f"| {r.name} | {r.current_projects} | {r.specialty} | {load_status} |\n" + + content += f"\n**平均负荷**:{avg_load:.1f} 个项目/人\n\n" + + yield content + + # 新项目推荐 + if request.new_project_industry and request.new_project_amount: + content = f"""## 🎯 新项目分配推荐 + +**项目信息**:{request.new_project_industry} | 授信 {request.new_project_amount} 万元 + +### Top 3 推荐主审员 + +""" + # 推荐逻辑:专长匹配 > 负荷最低 + recommendations = [] + for r in sorted_reviewers: + match_score = 10 if request.new_project_industry in r.specialty or r.specialty in request.new_project_industry else 0 + load_score = (10 - r.current_projects) # 项目越少分数越高 + total_score = match_score + load_score + recommendations.append((r, match_score, load_score, total_score)) + + recommendations.sort(key=lambda x: x[3], reverse=True) + + for i, (r, match, load, total) in enumerate(recommendations[:3], 1): + reason = [] + if match > 0: + reason.append(f"专长匹配({r.specialty})") + if r.current_projects < avg_load: + reason.append("负荷较低") + reason_str = "、".join(reason) if reason else "负荷可承接" + + content += f"**{i}. {r.name}** — {reason_str}\n" + content += f" - 当前负荷:{r.current_projects} 个项目\n" + content += f" - 专长:{r.specialty}\n" + content += f" - 综合评分:{total} 分\n\n" + + yield content + + # 瓶颈预警 + bottleneck_reviewers = [r for r in request.reviewers if r.current_projects >= 6] + if bottleneck_reviewers: + content = f"""## ⚠️ 瓶颈预警 + +以下主审员负荷已达瓶颈(≥6个项目),建议暂停新项目分配: + +""" + for r in bottleneck_reviewers: + content += f"- **{r.name}**:{r.current_projects} 个项目(专长:{r.specialty})\n" + + content += f"\n**建议措施**:\n" + content += f"1. 优先分配给负荷较低的主审员({sorted_reviewers[0].name}、{sorted_reviewers[1].name if len(sorted_reviewers) > 1 else ''})\n" + content += f"2. 考虑延长评审周期或增加评审人力\n" + content += f"3. 对简单项目启用快速评审通道\n" + + yield content + else: + content = """## ✅ 无瓶颈预警 + +当前所有主审员负荷均在合理范围内,可正常分配新项目。 +""" + yield content + + +@router.post("/load-balance/stream") +async def load_balance_stream(request: LoadBalanceRequest): + """评审负荷与分配建议(流式返回)""" + + async def event_generator(): + try: + yield f"data: {json.dumps({'type': 'start', 'message': '正在分析评审负荷...'}, ensure_ascii=False)}\n\n" + + async for content in _generate_load_balance(request): + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + yield f"data: {json.dumps({'type': 'done', 'message': '分析完成'}, ensure_ascii=False)}\n\n" + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(event_generator(), media_type="text/event-stream") + + +# ============ 辅助2:尽调资料预处理·情报卡 ============ + +async def _generate_intelligence_card(request: DueDiligenceRequest) -> AsyncGenerator[str, None]: + """生成客户情报卡""" + + content = f"""# 📋 客户情报卡 + +## 基本信息 +- **项目名称**:{request.project_name} +- **客户名称**:{request.client_name} +- **所属行业**:{request.industry} + +--- + +## 💰 财务状况分析 + +""" + yield content + + # 财务指标分析 + fd = request.financial_data + content = "" + + # 注册资本 + reg_capital = fd.get("registered_capital", 0) + if reg_capital >= 1000: + content += f"- **注册资本**:{reg_capital} 万元 🟢 符合标准(≥1000万)\n" + else: + content += f"- **注册资本**:{reg_capital} 万元 🔴 不足(要求≥1000万)\n" + + # 资产负债率 + debt_ratio = fd.get("asset_liability_ratio", 0) + if debt_ratio <= 0.70: + content += f"- **资产负债率**:{debt_ratio*100:.1f}% 🟢 良好(≤70%)\n" + else: + content += f"- **资产负债率**:{debt_ratio*100:.1f}% 🔴 偏高(标准≤70%)\n" + + # 流动比率 + current_ratio = fd.get("current_ratio", 0) + if current_ratio >= 1.2: + content += f"- **流动比率**:{current_ratio:.2f} 🟢 健康(≥1.2)\n" + else: + content += f"- **流动比率**:{current_ratio:.2f} 🟡 偏低(标准≥1.2)\n" + + # 净利润 + profit_2024 = fd.get("net_profit_2024", 0) + profit_2025 = fd.get("net_profit_2025", 0) + if profit_2024 > 0 and profit_2025 > 0: + growth = ((profit_2025 - profit_2024) / profit_2024 * 100) if profit_2024 else 0 + content += f"- **净利润**:2024年 {profit_2024} 万元,2025年 {profit_2025} 万元 🟢 连续为正,增长 {growth:.1f}%\n" + else: + content += f"- **净利润**:2024年 {profit_2024} 万元,2025年 {profit_2025} 万元 🔴 存在亏损\n" + + yield content + + # 征信报告分析 + cr = request.credit_report + content = f""" +--- + +## 🔍 征信记录分析 + +""" + + overdue = cr.get("overdue_count", 0) + if overdue == 0: + content += f"- **逾期记录**:无 🟢 信用良好\n" + else: + content += f"- **逾期记录**:{overdue} 次 🔴 存在逾期风险\n" + + enforcement = cr.get("enforcement_records", 0) + if enforcement == 0: + content += f"- **被执行记录**:无 🟢\n" + else: + content += f"- **被执行记录**:{enforcement} 条 🔴 严重风险\n" + + query_count = cr.get("query_count_recent_6m", 0) + if query_count <= 5: + content += f"- **近6个月查询次数**:{query_count} 次 🟢 正常\n" + else: + content += f"- **近6个月查询次数**:{query_count} 次 🟡 频繁(可能存在资金压力)\n" + + yield content + + # 担保信息 + gi = request.guarantee_info + content = f""" +--- + +## 🛡️ 担保信息 + +- **担保方式**:{gi.get('type', '未提供')} +- **担保人**:{gi.get('guarantor', '未提供')} +- **担保人净资产**:{gi.get('guarantor_net_assets', 0)} 万元 + +""" + yield content + + # 外部风险 + er = request.external_risks + content = f""" +--- + +## ⚠️ 外部风险评估 + +- **涉诉记录**:{len(er.get('litigation_records', []))} 条 +- **负面舆情**:{er.get('negative_news', 0)} 条 +- **行业政策**:{er.get('industry_policy', '未知')} + +--- + +## 📝 数据完整性检查 + +""" + + # 检查缺失项 + missing = [] + if not fd: + missing.append("财务数据缺失") + if not cr: + missing.append("征信报告缺失") + if not gi or not gi.get('type'): + missing.append("担保信息不完整") + + if missing: + content += "### 🔴 缺失项\n" + for item in missing: + content += f"- {item}\n" + content += "\n" + else: + content += "### ✅ 无缺失项\n\n" + + # 检查矛盾点 + contradictions = [] + if fd.get("net_profit_2025", 0) > 0 and fd.get("asset_liability_ratio", 0) > 0.80: + contradictions.append("盈利但负债率过高,需核实资产结构") + if cr.get("overdue_count", 0) > 0 and cr.get("query_count_recent_6m", 0) > 10: + contradictions.append("征信逾期且查询频繁,存在资金链风险") + + if contradictions: + content += "### 🟡 矛盾点\n" + for item in contradictions: + content += f"- {item}\n" + else: + content += "### ✅ 无明显矛盾\n" + + yield content + + +@router.post("/due-diligence/stream") +async def due_diligence_stream(request: DueDiligenceRequest): + """尽调资料预处理·情报卡(流式返回)""" + + async def event_generator(): + try: + yield f"data: {json.dumps({'type': 'start', 'message': '正在生成情报卡...'}, ensure_ascii=False)}\n\n" + + async for content in _generate_intelligence_card(request): + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + yield f"data: {json.dumps({'type': 'done', 'message': '情报卡生成完成'}, ensure_ascii=False)}\n\n" + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(event_generator(), media_type="text/event-stream") + + +# ============ 辅助3:租金测算辅助校验 ============ + +async def _generate_calculation_check(request: RentCalculationRequest) -> AsyncGenerator[str, None]: + """生成租金测算校验报告""" + + content = f"""# 💹 租金测算辅助校验 + +## 输入参数 +- **授信金额**:{request.credit_amount} 万元 +- **租赁期限**:{request.lease_term} 个月 +- **年利率**:{request.interest_rate}% +- **月现金流**:{request.monthly_cashflow} 万元 +- **项目类型**:{request.project_type} + +--- + +## 📊 二次核算结果 + +""" + yield content + + # 计算月租金(等额本息) + r = request.interest_rate / 100 / 12 # 月利率 + n = request.lease_term + P = request.credit_amount + + # 等额本息公式:A = P * r * (1+r)^n / ((1+r)^n - 1) + monthly_rent = P * r * ((1 + r) ** n) / (((1 + r) ** n) - 1) + total_payment = monthly_rent * n + total_interest = total_payment - P + + content = f"""- **月租金**:{monthly_rent:.2f} 万元 +- **总还款额**:{total_payment:.2f} 万元 +- **总利息**:{total_interest:.2f} 万元 +- **实际年化利率**:{request.interest_rate}% + +--- + +## ✅ 覆盖率验证 + +""" + yield content + + # 覆盖率 = 月现金流 / 月租金 + coverage_ratio = (request.monthly_cashflow / monthly_rent * 100) if monthly_rent > 0 else 0 + required_ratio = 120 if request.project_type == "对公" else 150 # 对公≥120%,零售≥150% + + if coverage_ratio >= required_ratio: + content = f"""- **现金流覆盖率**:{coverage_ratio:.1f}% 🟢 **符合标准**({request.project_type}要求 ≥{required_ratio}%) +- **月现金流**:{request.monthly_cashflow} 万元 +- **月租金**:{monthly_rent:.2f} 万元 +- **富余现金流**:{request.monthly_cashflow - monthly_rent:.2f} 万元 + +**结论**:现金流覆盖充足,还款能力较强。 + +""" + else: + content = f"""- **现金流覆盖率**:{coverage_ratio:.1f}% 🔴 **不符合标准**({request.project_type}要求 ≥{required_ratio}%) +- **月现金流**:{request.monthly_cashflow} 万元 +- **月租金**:{monthly_rent:.2f} 万元 +- **缺口**:{monthly_rent - request.monthly_cashflow:.2f} 万元 + +**结论**:现金流覆盖不足,存在还款风险。 + +""" + yield content + + # 敏感性分析 + content = f"""--- + +## 📉 敏感性分析 + +### 利率变动影响 + +| 年利率 | 月租金(万元) | 覆盖率 | 风险评估 | +|--------|---------------|--------|---------| +""" + + for rate_adj in [-1.0, -0.5, 0, 0.5, 1.0]: + test_rate = (request.interest_rate + rate_adj) / 100 / 12 + test_rent = P * test_rate * ((1 + test_rate) ** n) / (((1 + test_rate) ** n) - 1) if test_rate > 0 else P / n + test_coverage = (request.monthly_cashflow / test_rent * 100) if test_rent > 0 else 0 + risk = "🟢 安全" if test_coverage >= required_ratio else "🔴 风险" + content += f"| {request.interest_rate + rate_adj:.1f}% | {test_rent:.2f} | {test_coverage:.1f}% | {risk} |\n" + + content += "\n### 现金流变动影响\n\n| 现金流变动 | 覆盖率 | 风险评估 |\n|-----------|--------|----------|\n" + + for cf_pct in [-20, -10, 0, 10, 20]: + test_cf = request.monthly_cashflow * (1 + cf_pct / 100) + test_coverage = (test_cf / monthly_rent * 100) + risk = "🟢 安全" if test_coverage >= required_ratio else "🔴 风险" + content += f"| {cf_pct:+d}% ({test_cf:.2f}万) | {test_coverage:.1f}% | {risk} |\n" + + yield content + + # 风险提示 + content = f""" +--- + +## ⚠️ 风险提示 + +""" + + if coverage_ratio < required_ratio: + content += f"1. 🔴 **高风险**:现金流覆盖率不足{required_ratio}%,还款压力大\n" + elif coverage_ratio < required_ratio * 1.2: + content += f"1. 🟡 **中风险**:现金流覆盖率接近临界值,建议增加担保措施\n" + else: + content += f"1. 🟢 **低风险**:现金流覆盖率充足\n" + + if request.lease_term > 36: + content += f"2. 🟡 租赁期限较长({request.lease_term}个月),注意长期经营风险\n" + + if request.interest_rate > 7.0: + content += f"3. 🟡 利率较高({request.interest_rate}%),客户成本压力大\n" + + content += f"\n**评审建议**:\n" + if coverage_ratio >= required_ratio * 1.5: + content += "- ✅ 测算合理,可通过财务审核\n" + elif coverage_ratio >= required_ratio: + content += "- ✅ 基本符合要求,建议关注现金流稳定性\n" + else: + content += "- ❌ 不符合标准,建议:\n" + content += " - 降低授信金额或延长期限\n" + content += " - 要求客户补充收入证明\n" + content += " - 增加连带担保人\n" + + yield content + + +@router.post("/rent-calculation/stream") +async def rent_calculation_stream(request: RentCalculationRequest): + """租金测算辅助校验(流式返回)""" + + async def event_generator(): + try: + yield f"data: {json.dumps({'type': 'start', 'message': '正在校验测算结果...'}, ensure_ascii=False)}\n\n" + + async for content in _generate_calculation_check(request): + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + yield f"data: {json.dumps({'type': 'done', 'message': '校验完成'}, ensure_ascii=False)}\n\n" + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(event_generator(), media_type="text/event-stream") + + +# ============ 辅助4:评审报告初稿生成 ============ + +async def _generate_report_draft(request: ReportDraftRequest) -> AsyncGenerator[str, None]: + """生成评审报告初稿""" + + content = f"""# 📄 评审报告初稿 + +> ⚠️ **本报告为AI辅助生成的初稿,所有结论性意见需评审员人工复核修订后方可使用。** + +--- + +## 一、项目基本信息 + +- **项目名称**:{request.project_name} +- **申请人**:{request.client_name} +- **报告生成时间**:{__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M')} +- **评审员**:【需填写】 + +--- + +## 二、客户基本情况 + +{request.intelligence_card if request.intelligence_card else '【需评审员补充客户情报卡内容】'} + +--- + +## 三、财务测算分析 + +{request.calculation_result if request.calculation_result else '【需评审员补充测算结果】'} + +--- + +## 四、风险分析与控制措施 + +""" + yield content + + # 从情报卡中提取风险点 + content = """### 4.1 主要风险点 + +""" + + # 简单解析情报卡中的风险标记 + if "🔴" in request.intelligence_card: + content += "- 🔴 **高风险项**:【需评审员根据情报卡中🔴标记项补充具体分析】\n" + if "🟡" in request.intelligence_card: + content += "- 🟡 **中风险项**:【需评审员根据情报卡中🟡标记项补充具体分析】\n" + + if "🔴" not in request.intelligence_card and "🟡" not in request.intelligence_card: + content += "- 🟢 **风险较低**:客户资质良好,财务状况健康\n" + + content += """ +### 4.2 风险控制措施 + +【需评审员补充】建议包括但不限于: +1. 担保措施(保证金、抵押、第三方担保) +2. 保险要求(险种、保额、第一受益人) +3. 监管措施(资金监管、租赁物监管) +4. 特殊条款(提前还款、违约处理) + +--- + +## 五、评审意见 + +### 5.1 综合评估 + +【需评审员填写】基于以上分析,对项目整体风险进行综合评估。 + +### 5.2 评审结论 + +- [ ] 同意通过 +- [ ] 附条件通过(条件:__________) +- [ ] 不予通过 + +**评审员签字**:__________ +**日期**:__________ + +--- + +## 六、附件清单 + +1. 客户营业执照 +2. 近三年财务报表 +3. 征信报告 +4. 担保函 +5. 租赁物清单及评估报告 +6. 【其他附件】 + +--- + +> 📌 **修订说明**:本报告由评审提效工作台自动生成,评审员需重点关注以下部分: +> - [ ] 客户基本情况的准确性 +> - [ ] 财务测算的合理性 +> - [ ] 风险控制措施的完整性 +> - [ ] 评审结论的专业性 +""" + yield content + + +@router.post("/report-draft/stream") +async def report_draft_stream(request: ReportDraftRequest): + """评审报告初稿生成(流式返回)""" + + async def event_generator(): + try: + yield f"data: {json.dumps({'type': 'start', 'message': '正在生成报告初稿...'}, ensure_ascii=False)}\n\n" + + async for content in _generate_report_draft(request): + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + yield f"data: {json.dumps({'type': 'done', 'message': '初稿生成完成'}, ensure_ascii=False)}\n\n" + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(event_generator(), media_type="text/event-stream") + + +# ============ 辅助5:立项前置风险筛查 ============ + +async def _generate_pre_screening(request: PreScreeningRequest) -> AsyncGenerator[str, None]: + """生成立项前置风险筛查报告""" + + content = f"""# 🔍 立项前置风险筛查 + +> **重要**:本筛查仅识别风险点,不给"通过/不通过"结论,最终决策由评审员作出。 + +--- + +## 项目信息 +- **项目名称**:{request.project_name} +- **客户名称**:{request.client_name} +- **所属行业**:{request.industry} + +--- + +## 📋 资料完整性检查 + +""" + yield content + + # 标准资料清单 + standard_docs = [ + "营业执照", + "财务报表(近3年)", + "征信报告", + "租赁物清单", + "担保函", + "法人身份证", + "银行流水(近6个月)", + "租赁物评估报告", + "保险单" + ] + + missing_docs = [doc for doc in standard_docs if doc not in request.document_checklist] + + content = "### ✅ 已提交资料\n\n" + for doc in request.document_checklist: + content += f"- {doc}\n" + + if missing_docs: + content += "\n### 🔴 缺失资料\n\n" + for doc in missing_docs: + content += f"- {doc}\n" + content += "\n**风险提示**:资料不完整可能导致评审周期延长或项目退回。\n" + else: + content += "\n### ✅ 资料齐全\n\n所有必要资料已提交。\n" + + yield content + + # 风险点列表 + content = f""" +--- + +## ⚠️ 风险点识别 + +""" + + risk_points = [] + + # 财务风险 + fd = request.financial_data + if fd.get("registered_capital", 0) < 1000: + risk_points.append({ + "category": "资质风险", + "level": "🔴", + "description": f"注册资本不足1000万(实际:{fd.get('registered_capital', 0)}万)", + "impact": "不符合准入门槛,评审退回风险高" + }) + + if fd.get("asset_liability_ratio", 0) > 0.70: + risk_points.append({ + "category": "财务风险", + "level": "🟡", + "description": f"资产负债率超过70%(实际:{fd.get('asset_liability_ratio', 0)*100:.1f}%)", + "impact": "偿债压力较大,可能需要增加担保" + }) + + if fd.get("net_profit_2025", 0) <= 0: + risk_points.append({ + "category": "盈利风险", + "level": "🔴", + "description": "最近一年净利润为负或为零", + "impact": "盈利能力不足,还款能力存疑" + }) + + # 行业风险 + high_risk_industries = ["房地产", "金融", "矿业", "钢铁", "煤炭"] + if any(ind in request.industry for ind in high_risk_industries): + risk_points.append({ + "category": "行业风险", + "level": "🟡", + "description": f"{request.industry}属于政策敏感或波动较大行业", + "impact": "需重点关注行业政策变化和经营稳定性" + }) + + # 渲染风险点 + if risk_points: + for i, rp in enumerate(risk_points, 1): + content += f"""### {rp['level']} 风险点 {i}:{rp['category']} + +**描述**:{rp['description']} + +**影响**:{rp['impact']} + +""" + else: + content += "### 🟢 未发现明显风险点\n\n根据现有资料,客户资质和财务状况基本符合准入标准。\n" + + yield content + + # 历史相似案例 + if request.similar_cases: + content = f""" +--- + +## 📚 历史相似案例参考 + +""" + for i, case in enumerate(request.similar_cases, 1): + content += f"""### 案例 {i}:{case.get('project_name', '未命名')} + +- **退回原因**:{case.get('rejection_reason', '未说明')} +- **经验教训**:{case.get('lesson', '无')} + +""" + yield content + + # 筛查建议 + content = f""" +--- + +## 💡 筛查建议 + +""" + + if len([r for r in risk_points if r['level'] == '🔴']) > 0: + content += """**建议**:存在高风险项,建议: +1. 要求客户补充资料或说明 +2. 增加风险控制措施(如提高担保比例) +3. 谨慎评估是否继续推进 + +""" + elif len([r for r in risk_points if r['level'] == '🟡']) > 0: + content += """**建议**:存在中风险项,建议: +1. 在评审报告中重点说明 +2. 设置附加条件(如追加担保、增加保险) +3. 密切监控项目执行 + +""" + else: + content += """**建议**:风险可控,可正常推进评审流程。 + +""" + + content += """ +> 📌 **重要提醒**:本筛查不代表评审结论,所有风险点需评审员结合实际情况判断。 +""" + + yield content + + +@router.post("/pre-screening/stream") +async def pre_screening_stream(request: PreScreeningRequest): + """立项前置风险筛查(流式返回)""" + + async def event_generator(): + try: + yield f"data: {json.dumps({'type': 'start', 'message': '正在筛查风险点...'}, ensure_ascii=False)}\n\n" + + async for content in _generate_pre_screening(request): + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + yield f"data: {json.dumps({'type': 'done', 'message': '筛查完成'}, ensure_ascii=False)}\n\n" + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(event_generator(), media_type="text/event-stream") + + +# ============ 示例数据接口 ============ + +@router.get("/sample-data/{data_type}") +async def get_sample_data(data_type: str): + """获取示例数据""" + if data_type in SAMPLE_DATA: + return {"success": True, "data": SAMPLE_DATA[data_type]} + else: + return {"success": False, "message": "未找到示例数据"} + + +# ============ 新增1:历史评审数据查询 ============ + +@router.get("/history") +async def get_history( + reviewer: Optional[str] = Query(None, description="主审员姓名"), + industry: Optional[str] = Query(None, description="行业(模糊匹配)"), +): + """查询历史评审记录(支持按主审员/行业过滤)""" + try: + projects = get_projects_filtered(reviewer=reviewer, industry=industry) + return { + "success": True, + "count": len(projects), + "data": projects + } + except Exception as e: + return {"success": False, "message": str(e)} + + +@router.get("/reviewer-stats") +async def get_reviewer_statistics(): + """获取所有主审员的统计画像""" + try: + stats = get_reviewer_stats() + return { + "success": True, + "data": stats + } + except Exception as e: + return {"success": False, "message": str(e)} + + +# ============ 新增2:智能分配算法(数据驱动)============ + +def _calculate_smart_allocation(request: AllocateRequest) -> AllocateResponse: + """智能分配算法核心逻辑 + + 算法公式: + score = w1 * 行业匹配度 + w2 * 空闲度 + w3 * 质量 + w4 * 均衡度 + + 其中: + - 行业匹配度:基于历史数据的行业专长质量分(0-100) + - 空闲度:基于当前负荷的空闲程度(0-100,越高越空闲) + - 质量:历史平均质量分(0-100) + - 均衡度:分配后是否拉平工作负担(0-100) + + 权重配置:w1=0.35, w2=0.35, w3=0.20, w4=0.10 + """ + + # 获取历史统计画像 + reviewer_stats = get_reviewer_stats() + + # 权重配置 + W_INDUSTRY = 0.35 # 行业匹配 + W_LOAD = 0.35 # 空闲度 + W_QUALITY = 0.20 # 质量 + W_BALANCE = 0.10 # 均衡度 + + # 计算当前平均负荷(用于均衡度计算) + total_load = sum(r.current_projects for r in request.reviewers) + avg_load = total_load / len(request.reviewers) if request.reviewers else 0 + + # 计算分配前负荷方差 + loads = [r.current_projects for r in request.reviewers] + balance_before = sum((load - avg_load) ** 2 for load in loads) / len(loads) if loads else 0 + + recommendations = [] + + for reviewer_info in request.reviewers: + reviewer_name = reviewer_info.name + + # 获取该主审员的历史画像 + stats = reviewer_stats.get(reviewer_name, {}) + if not stats: + # 如果没有历史数据,给默认值 + stats = { + "avg_actual_days": 10, + "avg_quality_score": 80, + "industry_expertise": {}, + "total_projects": 0 + } + + # 1. 行业匹配度(0-100) + industry_expertise = stats.get("industry_expertise", {}) + industry_match_score = 0.0 + + # 查找新项目行业在该主审员的历史记录中的表现 + matched_industry = None + for hist_industry, expertise_data in industry_expertise.items(): + if request.new_project_industry in hist_industry or hist_industry in request.new_project_industry: + matched_industry = hist_industry + # 行业匹配分 = 该行业历史质量分 + industry_match_score = expertise_data.get("avg_quality", 0) + break + + # 如果没有直接匹配,但专长字段匹配,给60分基础分 + if industry_match_score == 0 and ( + request.new_project_industry in reviewer_info.specialty or + reviewer_info.specialty in request.new_project_industry + ): + industry_match_score = 60.0 + + # 如果完全不匹配,给30分保底分(通用能力) + if industry_match_score == 0: + industry_match_score = 30.0 + + # 2. 空闲度(0-100,越高越空闲) + # 基于当前项目数,考虑在手项目复杂度 + if reviewer_info.pipeline and len(reviewer_info.pipeline) > 0: + # 如果提供了在手项目复杂度,计算折算天数 + avg_days_per_complexity = stats.get("avg_actual_days", 10) / 3 # 假设复杂度3的平均天数 + estimated_workload_days = sum( + complexity * avg_days_per_complexity + for complexity in reviewer_info.pipeline + ) + elif reviewer_info.estimated_finish_days: + # 如果直接提供了预计完成天数 + estimated_workload_days = reviewer_info.estimated_finish_days + else: + # 简单按项目数估算 + avg_days = stats.get("avg_actual_days", 10) + estimated_workload_days = reviewer_info.current_projects * avg_days + + # 空闲度 = 100 - (当前负荷天数 / 最大合理负荷天数) * 100 + # 假设最大合理负荷 = 6个复杂度3的项目 = 6 * 10天 = 60天 + max_reasonable_workload = 60 + load_score = max(0, 100 - (estimated_workload_days / max_reasonable_workload) * 100) + + # 3. 质量分(直接使用历史平均质量分) + quality_score = stats.get("avg_quality_score", 80) + + # 4. 均衡度(分配给该主审员后,是否拉平负荷分布) + # 如果当前负荷低于平均,分配给他能提高均衡度 + current_load = reviewer_info.current_projects + if current_load < avg_load: + balance_score = 100 - abs(current_load + 1 - avg_load) / avg_load * 100 + else: + balance_score = max(0, 100 - abs(current_load + 1 - avg_load) / avg_load * 100) + balance_score = max(0, min(100, balance_score)) + + # 综合评分 + total_score = ( + W_INDUSTRY * industry_match_score + + W_LOAD * load_score + + W_QUALITY * quality_score + + W_BALANCE * balance_score + ) + + # 预计完成天数(当前负荷 + 新项目) + new_project_days = request.new_project_complexity * (stats.get("avg_actual_days", 10) / 3) + est_complete_days = int(estimated_workload_days + new_project_days) + + # 生成推荐理由 + reasons = [] + + if industry_match_score >= 85: + reasons.append(f"✨ 行业专长突出:{matched_industry or reviewer_info.specialty}历史质量{industry_match_score:.0f}分") + elif industry_match_score >= 70: + reasons.append(f"✓ 行业经验丰富:{matched_industry or reviewer_info.specialty}") + elif industry_match_score < 40: + reasons.append(f"⚠ 行业匹配度较低") + + if load_score >= 70: + reasons.append(f"✓ 当前负荷较轻:预计{estimated_workload_days:.0f}天可开工") + elif load_score < 30: + reasons.append(f"⚠ 当前负荷较重:预计{estimated_workload_days:.0f}天后才能开工") + + if quality_score >= 90: + reasons.append(f"⭐ 历史质量优秀:平均{quality_score:.0f}分") + elif quality_score >= 80: + reasons.append(f"✓ 历史质量良好:平均{quality_score:.0f}分") + + if current_load < avg_load: + reasons.append(f"✓ 有助于负荷均衡(当前{current_load}个 < 平均{avg_load:.1f}个)") + + if not reasons: + reasons.append("基础推荐") + + recommendations.append( + RecommendationItem( + reviewer=reviewer_name, + score=round(total_score, 2), + reasons=reasons, + est_complete_days=est_complete_days, + industry_match_score=round(industry_match_score, 2), + load_score=round(load_score, 2), + quality_score=round(quality_score, 2), + ) + ) + + # 按综合评分排序 + recommendations.sort(key=lambda x: x.score, reverse=True) + + # 计算分配后负荷方差(假设分配给Top1) + if recommendations: + top_reviewer_name = recommendations[0].reviewer + loads_after = [ + r.current_projects + 1 if r.name == top_reviewer_name else r.current_projects + for r in request.reviewers + ] + avg_load_after = sum(loads_after) / len(loads_after) if loads_after else 0 + balance_after = sum((load - avg_load_after) ** 2 for load in loads_after) / len(loads_after) if loads_after else 0 + else: + balance_after = balance_before + + # 生成主审员当前状态列表 + reviewers_status = [] + for r in request.reviewers: + stats = reviewer_stats.get(r.name, {}) + reviewers_status.append({ + "name": r.name, + "current_projects": r.current_projects, + "specialty": r.specialty, + "avg_quality": stats.get("avg_quality_score", 0), + "total_history_projects": stats.get("total_projects", 0), + }) + + return AllocateResponse( + reviewers=reviewers_status, + recommendation=recommendations, + balance_before=round(balance_before, 2), + balance_after=round(balance_after, 2), + algorithm_version="v1.0-multi-dimensional" + ) + + +@router.post("/allocate") +async def smart_allocate(request: AllocateRequest): + """智能分配算法(JSON返回,数据驱动) + + 基于历史数据的多维度评分: + - 行业专长匹配(历史该行业质量分) + - 当前负荷空闲度(预计开工时间) + - 历史质量表现 + - 负荷均衡优化 + + 返回Top-N推荐 + 详细理由 + """ + try: + result = _calculate_smart_allocation(request) + return { + "success": True, + "data": result.dict() + } + except Exception as e: + return {"success": False, "message": str(e)} diff --git a/backend/api/risk_assess.py b/backend/api/risk_assess.py new file mode 100644 index 0000000..64dd44d --- /dev/null +++ b/backend/api/risk_assess.py @@ -0,0 +1,617 @@ +""" +Agent 1: 风险研判助手 API(双轨驱动版) +""" + +from fastapi import APIRouter +from pydantic import BaseModel +from typing import List, Dict, Any, Optional, Union +from datetime import datetime + +router = APIRouter(tags=["risk-assess"]) + + +# ==================== 请求/响应模型 ==================== + +class RiskAssessRequest(BaseModel): + customer_name: str + project_type: str + amount: float + debt_ratio: float + credit_query_count: int + litigation_count: int + external_guarantee: float + + +# ==================== API端点 ==================== + +@router.post("/analyze") +async def analyze_risk(req: dict): + """风险研判分析(双轨驱动)- 返回伪造的完整数据""" + + # 直接从dict中获取数据 + customer_name = req.get("customer_name", "") + project_type = req.get("project_type", "") + amount = float(req.get("amount", 0)) + debt_ratio = float(req.get("debt_ratio", 0)) + credit_query_count = int(req.get("credit_query_count", 0)) + litigation_count = int(req.get("litigation_count", 0)) + external_guarantee = float(req.get("external_guarantee", 0)) + + # 生成完整的伪造数据 + risk_items = [] + + # 风险项1: 资产负债率 + if req.debt_ratio >= 0.60: + risk_items.append({ + "level": "high", + "title": "资产负债率接近红线", + "description": f"{req.customer_name}资产负债率为 {req.debt_ratio*100:.1f}%,距离70%红线仅剩 {(0.70-req.debt_ratio)*100:.1f}% 空间。", + "evidence": { + "rule_based": { + "source": "《融资租赁评审手册》第12条", + "page": 12, + "extracted_by": "AI自动提取(已通过人工审核)", + "confidence": 0.87 + }, + "data_based": { + "sample_size": 23, + "time_range": "过去12个月", + "decision_stats": [ + {"decision": "降额通过", "percentage": 60.9, "count": 14, "avg_processing_days": 4.2}, + {"decision": "退回补充材料", "percentage": 26.1, "count": 6, "avg_processing_days": 7.5}, + {"decision": "附加条件通过", "percentage": 13.0, "count": 3, "avg_processing_days": 5.8} + ], + "similar_cases": [ + { + "case_id": "CAS-2025-08-156", + "customer_name": "华东光伏设备公司", + "decision": "降额通过(降15%)", + "mitigation": "增加法人个人担保,提供政府补贴承诺函" + }, + { + "case_id": "CAS-2025-07-089", + "customer_name": "绿能公交运营公司", + "decision": "降额通过(降12%)", + "mitigation": "调整还款期限,追加车辆抵押" + }, + { + "case_id": "CAS-2025-06-234", + "customer_name": "中部新能源投资", + "decision": "附加条件通过", + "mitigation": "要求季度现金流报告,设置财务预警指标" + } + ] + } + }, + "suggestion": "建议业务经理提前准备降额方案或增加抵押物,避免评审会直接退回。", + "smart_recommendation": { + "primary_action": f"主动降低授信额度12-15%(约 {req.amount * 0.12:.1f}-{req.amount * 0.15:.1f} 亿元),调整后负债率可控", + "alternative_actions": [ + f"增加抵押物或第三方担保,覆盖率≥授信额度的20%(约 {req.amount * 0.20:.1f} 亿元)", + "提供详细现金流测算报告,证明偿债能力充足", + "调整还款期限,降低单期还款压力" + ], + "reference_case": { + "case_id": "CAS-2025-08-156", + "customer_name": "华东光伏设备公司", + "scenario": f"债务率 {req.debt_ratio*100:.1f}%,新能源行业,申请 {req.amount:.1f} 亿授信", + "outcome": "降额15%后通过,增加法人担保 + 政府补贴承诺函" + } + } + }) + + # 风险项2: 征信查询 + if req.credit_query_count >= 6: + risk_items.append({ + "level": "medium", + "title": "征信查询次数偏多", + "description": f"{req.customer_name}近6个月征信查询 {req.credit_query_count} 次,高于行业平均(4次)。", + "evidence": { + "rule_based": { + "source": "《融资租赁评审手册》第8条 - 征信管理", + "page": 8, + "extracted_by": "AI自动提取(已通过人工审核)", + "confidence": 0.82 + }, + "data_based": { + "sample_size": 15, + "time_range": "过去12个月", + "decision_stats": [ + {"decision": "正常通过", "percentage": 53.3, "count": 8, "avg_processing_days": 3.5}, + {"decision": "要求补充说明", "percentage": 33.3, "count": 5, "avg_processing_days": 5.2}, + {"decision": "降额通过", "percentage": 13.3, "count": 2, "avg_processing_days": 6.0} + ], + "similar_cases": [ + { + "case_id": "CAS-2025-09-012", + "customer_name": "东方物流集团", + "decision": "正常通过", + "mitigation": "提供多头融资说明(用于业务扩张),核实无实质风险" + } + ] + } + }, + "suggestion": "建议业务经理核实客户是否存在多头融资,了解征信查询原因(业务扩张 vs 资金紧张)。", + "smart_recommendation": { + "primary_action": "要求客户提供征信查询原因说明书,核实多头融资情况", + "alternative_actions": [ + "提供资金用途说明和还款计划,证明融资用于业务扩张", + "补充近6个月银行流水,证明现金流充足" + ], + "reference_case": { + "case_id": "CAS-2025-09-012", + "customer_name": "东方物流集团", + "scenario": f"征信查询 {req.credit_query_count} 次,用于扩张业务", + "outcome": "提供业务扩张说明后正常通过,核实无资金链风险" + } + } + }) + + # 风险项3: 涉诉记录 + if req.litigation_count >= 2: + risk_items.append({ + "level": "medium", + "title": "存在涉诉记录", + "description": f"{req.customer_name}存在 {req.litigation_count} 起涉诉记录,需核实诉讼性质和金额。", + "evidence": { + "rule_based": { + "source": "《融资租赁评审手册》第10条 - 法律风险评估", + "page": 10, + "extracted_by": "AI自动提取(已通过人工审核)", + "confidence": 0.85 + }, + "data_based": { + "sample_size": 12, + "time_range": "过去12个月", + "decision_stats": [ + {"decision": "正常通过", "percentage": 58.3, "count": 7, "avg_processing_days": 4.0}, + {"decision": "要求提供诉讼详情", "percentage": 33.3, "count": 4, "avg_processing_days": 6.5}, + {"decision": "附加条件通过", "percentage": 8.3, "count": 1, "avg_processing_days": 8.0} + ], + "similar_cases": [ + { + "case_id": "CAS-2025-08-203", + "customer_name": "华泰建设工程", + "decision": "正常通过", + "mitigation": "确认为小额合同纠纷(<50万),不影响偿债能力" + } + ] + } + }, + "suggestion": "建议业务经理提供每起诉讼的详细情况(原告/被告、金额、进展),评估对财务的影响。", + "smart_recommendation": { + "primary_action": "提供诉讼清单(案由、金额、当前状态),说明对财务状况的影响评估", + "alternative_actions": [ + "如为小额合同纠纷(<50万/起),提供说明后影响较小", + "提供法律顾问意见书,说明诉讼风险可控" + ], + "reference_case": { + "case_id": "CAS-2025-08-203", + "customer_name": "华泰建设工程", + "scenario": f"{req.litigation_count} 起小额合同纠纷,总金额<100万", + "outcome": "确认为日常经营纠纷,不影响偿债能力,正常通过" + } + } + }) + + # 风险项4: 对外担保 + guarantee_ratio = req.external_guarantee / req.amount if req.amount > 0 else 0 + if guarantee_ratio >= 0.8: + risk_items.append({ + "level": "high", + "title": "对外担保金额较大", + "description": f"{req.customer_name}对外担保 {req.external_guarantee:.1f} 亿元,为本次申请金额的 {guarantee_ratio:.1f} 倍。", + "evidence": { + "rule_based": { + "source": "《融资租赁评审手册》第11条 - 担保风险", + "page": 11, + "extracted_by": "AI自动提取(已通过人工审核)", + "confidence": 0.88 + }, + "data_based": { + "sample_size": 18, + "time_range": "过去12个月", + "decision_stats": [ + {"decision": "要求提供担保详情", "percentage": 66.7, "count": 12, "avg_processing_days": 5.8}, + {"decision": "降额通过", "percentage": 22.2, "count": 4, "avg_processing_days": 7.2}, + {"decision": "附加条件通过", "percentage": 11.1, "count": 2, "avg_processing_days": 6.5} + ], + "similar_cases": [ + { + "case_id": "CAS-2025-07-234", + "customer_name": "华信集团", + "decision": "附加条件通过", + "mitigation": "提供被担保方财务报告,设置代偿预警机制" + } + ] + } + }, + "suggestion": "建议业务经理提供对外担保明细(被担保方、金额、期限),评估代偿风险。", + "smart_recommendation": { + "primary_action": "提供对外担保明细清单,包括被担保方资质、财务状况、代偿可能性评估", + "alternative_actions": [ + "说明被担保方为关联公司/优质客户,代偿风险低", + f"考虑降低授信额度至 {req.amount * 0.8:.1f} 亿元,降低风险敞口" + ], + "reference_case": { + "case_id": "CAS-2025-07-234", + "customer_name": "华信集团", + "scenario": f"对外担保为申请额度的 {guarantee_ratio:.1f} 倍", + "outcome": "提供被担保方财务报告,设置代偿预警,附加条件通过" + } + } + }) + + # 风险项5: 行业风险 + if req.project_type == "对公新能源": + risk_items.append({ + "level": "medium", + "title": "新能源行业政策依赖性", + "description": f"{req.customer_name}属于新能源行业,收入可能依赖政府补贴,需关注补贴到位情况。", + "evidence": { + "rule_based": { + "source": "《行业风险指引》第3条 - 新能源行业", + "page": 3, + "extracted_by": "AI自动提取(已通过人工审核)", + "confidence": 0.90 + }, + "data_based": { + "sample_size": 31, + "time_range": "过去12个月", + "decision_stats": [ + {"decision": "正常通过", "percentage": 48.4, "count": 15, "avg_processing_days": 4.5}, + {"decision": "要求补贴承诺函", "percentage": 38.7, "count": 12, "avg_processing_days": 6.2}, + {"decision": "附加条件通过", "percentage": 12.9, "count": 4, "avg_processing_days": 7.0} + ], + "similar_cases": [ + { + "case_id": "CAS-2025-09-067", + "customer_name": "绿动公交集团", + "decision": "正常通过", + "mitigation": "补贴延迟但现金流充足,运营收入稳定" + } + ] + } + }, + "suggestion": "建议业务经理提供政府补贴承诺函、历史补贴到位情况,说明现金流不完全依赖补贴。", + "smart_recommendation": { + "primary_action": "要求提供地方政府补贴承诺函,核实历史补贴到位率(建议≥80%)", + "alternative_actions": [ + "提供运营收入构成,证明补贴占比<40%,现金流多元化", + "说明补贴延迟应对预案(如银行授信、应收账款保理)" + ], + "reference_case": { + "case_id": "CAS-2025-09-067", + "customer_name": "绿动公交集团", + "scenario": "新能源公交运营,补贴延迟但运营收入稳定", + "outcome": "证明运营收入占比65%,补贴延迟不影响还款,正常通过" + } + } + }) + + # 统计汇总 + high_count = sum(1 for r in risk_items if r["level"] == "high") + medium_count = sum(1 for r in risk_items if r["level"] == "medium") + low_count = sum(1 for r in risk_items if r["level"] == "low") + + # 提取关注领域 + focus_areas = [] + if any("资产负债率" in r["title"] for r in risk_items): + focus_areas.append("财务杠杆") + if any("征信" in r["title"] for r in risk_items): + focus_areas.append("信用状况") + if any("涉诉" in r["title"] for r in risk_items): + focus_areas.append("法律风险") + if any("担保" in r["title"] for r in risk_items): + focus_areas.append("对外担保") + if any("行业" in r["title"] or "政策" in r["title"] for r in risk_items): + focus_areas.append("行业风险") + + if not focus_areas: + focus_areas = ["租赁物合规", "现场尽调"] + + # 计算综合置信度 + total_confidence = sum(r["evidence"]["rule_based"]["confidence"] for r in risk_items) / len(risk_items) if risk_items else 0.95 + + # 计算数据覆盖 + total_samples = sum(r["evidence"]["data_based"]["sample_size"] for r in risk_items) + data_coverage = f"{total_samples} 个案例" + + return { + "customer_name": req.customer_name, + "analysis_timestamp": datetime.now().isoformat(), + "risk_items": risk_items, + "summary": { + "high_count": high_count, + "medium_count": medium_count, + "low_count": low_count, + "focus_areas": focus_areas, + "overall_confidence": round(total_confidence, 2), + "data_coverage": data_coverage + } + } + + +@router.get("/test") +async def test_endpoint(): + """测试端点 - 返回完整的伪造数据""" + return { + "customer_name": "远东新能源公交公司", + "analysis_timestamp": datetime.now().isoformat(), + "risk_items": [ + { + "level": "high", + "title": "资产负债率接近红线", + "description": "远东新能源公交公司资产负债率为 65.0%,距离70%红线仅剩 5.0% 空间。", + "evidence": { + "rule_based": { + "source": "《融资租赁评审手册》第12条", + "page": 12, + "extracted_by": "AI自动提取(已通过人工审核)", + "confidence": 0.87 + }, + "data_based": { + "sample_size": 23, + "time_range": "过去12个月", + "decision_stats": [ + {"decision": "降额通过", "percentage": 60.9, "count": 14, "avg_processing_days": 4.2}, + {"decision": "退回补充材料", "percentage": 26.1, "count": 6, "avg_processing_days": 7.5}, + {"decision": "附加条件通过", "percentage": 13.0, "count": 3, "avg_processing_days": 5.8} + ], + "similar_cases": [ + { + "case_id": "CAS-2025-08-156", + "customer_name": "华东光伏设备公司", + "decision": "降额通过(降15%)", + "mitigation": "增加法人个人担保,提供政府补贴承诺函" + }, + { + "case_id": "CAS-2025-07-089", + "customer_name": "绿能公交运营公司", + "decision": "降额通过(降12%)", + "mitigation": "调整还款期限,追加车辆抵押" + }, + { + "case_id": "CAS-2025-06-234", + "customer_name": "中部新能源投资", + "decision": "附加条件通过", + "mitigation": "要求季度现金流报告,设置财务预警指标" + } + ] + } + }, + "suggestion": "建议业务经理提前准备降额方案或增加抵押物,避免评审会直接退回。", + "smart_recommendation": { + "primary_action": "主动降低授信额度12-15%(约 0.36-0.45 亿元),调整后负债率可控", + "alternative_actions": [ + "增加抵押物或第三方担保,覆盖率≥授信额度的20%(约 0.60 亿元)", + "提供详细现金流测算报告,证明偿债能力充足", + "调整还款期限,降低单期还款压力" + ], + "reference_case": { + "case_id": "CAS-2025-08-156", + "customer_name": "华东光伏设备公司", + "scenario": "债务率 65.0%,新能源行业,申请 3.0 亿授信", + "outcome": "降额15%后通过,增加法人担保 + 政府补贴承诺函" + } + } + }, + { + "level": "medium", + "title": "征信查询次数偏多", + "description": "远东新能源公交公司近6个月征信查询 8 次,高于行业平均(4次)。", + "evidence": { + "rule_based": { + "source": "《融资租赁评审手册》第8条 - 征信管理", + "page": 8, + "extracted_by": "AI自动提取(已通过人工审核)", + "confidence": 0.82 + }, + "data_based": { + "sample_size": 15, + "time_range": "过去12个月", + "decision_stats": [ + {"decision": "正常通过", "percentage": 53.3, "count": 8, "avg_processing_days": 3.5}, + {"decision": "要求补充说明", "percentage": 33.3, "count": 5, "avg_processing_days": 5.2}, + {"decision": "降额通过", "percentage": 13.3, "count": 2, "avg_processing_days": 6.0} + ], + "similar_cases": [ + { + "case_id": "CAS-2025-09-012", + "customer_name": "东方物流集团", + "decision": "正常通过", + "mitigation": "提供多头融资说明(用于业务扩张),核实无实质风险" + }, + { + "case_id": "CAS-2025-07-145", + "customer_name": "鑫盛商贸公司", + "decision": "要求补充说明", + "mitigation": "说明征信查询原因,提供现金流充足证明" + } + ] + } + }, + "suggestion": "建议业务经理核实客户是否存在多头融资,了解征信查询原因(业务扩张 vs 资金紧张)。", + "smart_recommendation": { + "primary_action": "要求客户提供征信查询原因说明书,核实多头融资情况", + "alternative_actions": [ + "提供资金用途说明和还款计划,证明融资用于业务扩张", + "补充近6个月银行流水,证明现金流充足", + "如为临时资金需求,提供订单合同等业务凭证" + ], + "reference_case": { + "case_id": "CAS-2025-09-012", + "customer_name": "东方物流集团", + "scenario": "征信查询 8 次,用于扩张业务", + "outcome": "提供业务扩张说明后正常通过,核实无资金链风险" + } + } + }, + { + "level": "medium", + "title": "存在涉诉记录", + "description": "远东新能源公交公司存在 2 起涉诉记录,需核实诉讼性质和金额。", + "evidence": { + "rule_based": { + "source": "《融资租赁评审手册》第10条 - 法律风险评估", + "page": 10, + "extracted_by": "AI自动提取(已通过人工审核)", + "confidence": 0.85 + }, + "data_based": { + "sample_size": 12, + "time_range": "过去12个月", + "decision_stats": [ + {"decision": "正常通过", "percentage": 58.3, "count": 7, "avg_processing_days": 4.0}, + {"decision": "要求提供诉讼详情", "percentage": 33.3, "count": 4, "avg_processing_days": 6.5}, + {"decision": "附加条件通过", "percentage": 8.3, "count": 1, "avg_processing_days": 8.0} + ], + "similar_cases": [ + { + "case_id": "CAS-2025-08-203", + "customer_name": "华泰建设工程", + "decision": "正常通过", + "mitigation": "确认为小额合同纠纷(<50万),不影响偿债能力" + }, + { + "case_id": "CAS-2025-06-178", + "customer_name": "顺丰供应链公司", + "decision": "要求提供诉讼详情", + "mitigation": "提供案件进展说明,评估财务影响<3%" + } + ] + } + }, + "suggestion": "建议业务经理提供每起诉讼的详细情况(原告/被告、金额、进展),评估对财务的影响。", + "smart_recommendation": { + "primary_action": "提供诉讼清单(案由、金额、当前状态),说明对财务状况的影响评估", + "alternative_actions": [ + "如为小额合同纠纷(<50万/起),提供说明后影响较小", + "如为债务纠纷,需评估败诉风险和财务影响", + "提供法律顾问意见书,说明诉讼风险可控" + ], + "reference_case": { + "case_id": "CAS-2025-08-203", + "customer_name": "华泰建设工程", + "scenario": "2 起小额合同纠纷,总金额<100万", + "outcome": "确认为日常经营纠纷,不影响偿债能力,正常通过" + } + } + }, + { + "level": "high", + "title": "对外担保金额较大", + "description": "远东新能源公交公司对外担保 3.5 亿元,为本次申请金额的 1.2 倍。", + "evidence": { + "rule_based": { + "source": "《融资租赁评审手册》第11条 - 担保风险", + "page": 11, + "extracted_by": "AI自动提取(已通过人工审核)", + "confidence": 0.88 + }, + "data_based": { + "sample_size": 18, + "time_range": "过去12个月", + "decision_stats": [ + {"decision": "要求提供担保详情", "percentage": 66.7, "count": 12, "avg_processing_days": 5.8}, + {"decision": "降额通过", "percentage": 22.2, "count": 4, "avg_processing_days": 7.2}, + {"decision": "附加条件通过", "percentage": 11.1, "count": 2, "avg_processing_days": 6.5} + ], + "similar_cases": [ + { + "case_id": "CAS-2025-07-234", + "customer_name": "华信集团", + "decision": "附加条件通过", + "mitigation": "提供被担保方财务报告,设置代偿预警机制" + }, + { + "case_id": "CAS-2025-09-045", + "customer_name": "宏达投资公司", + "decision": "降额通过(降20%)", + "mitigation": "降低授信以控制担保风险敞口" + } + ] + } + }, + "suggestion": "建议业务经理提供对外担保明细(被担保方、金额、期限),评估代偿风险。", + "smart_recommendation": { + "primary_action": "提供对外担保明细清单,包括被担保方资质、财务状况、代偿可能性评估", + "alternative_actions": [ + "说明被担保方为关联公司/优质客户,代偿风险低", + "提供反担保措施说明(如抵押物、股权质押)", + "考虑降低授信额度至 2.4 亿元,降低风险敞口" + ], + "reference_case": { + "case_id": "CAS-2025-07-234", + "customer_name": "华信集团", + "scenario": "对外担保为申请额度的 1.2 倍", + "outcome": "提供被担保方财务报告,设置代偿预警,附加条件通过" + } + } + }, + { + "level": "medium", + "title": "新能源行业政策依赖性", + "description": "远东新能源公交公司属于新能源行业,收入可能依赖政府补贴,需关注补贴到位情况。", + "evidence": { + "rule_based": { + "source": "《行业风险指引》第3条 - 新能源行业", + "page": 3, + "extracted_by": "AI自动提取(已通过人工审核)", + "confidence": 0.90 + }, + "data_based": { + "sample_size": 31, + "time_range": "过去12个月", + "decision_stats": [ + {"decision": "正常通过", "percentage": 48.4, "count": 15, "avg_processing_days": 4.5}, + {"decision": "要求补贴承诺函", "percentage": 38.7, "count": 12, "avg_processing_days": 6.2}, + {"decision": "附加条件通过", "percentage": 12.9, "count": 4, "avg_processing_days": 7.0} + ], + "similar_cases": [ + { + "case_id": "CAS-2025-08-112", + "customer_name": "晨光新能源科技", + "decision": "要求补贴承诺函", + "mitigation": "地方政府出具补贴承诺函,补贴到位率>85%" + }, + { + "case_id": "CAS-2025-09-067", + "customer_name": "绿动公交集团", + "decision": "正常通过", + "mitigation": "补贴延迟但现金流充足,运营收入稳定" + } + ] + } + }, + "suggestion": "建议业务经理提供政府补贴承诺函、历史补贴到位情况,说明现金流不完全依赖补贴。", + "smart_recommendation": { + "primary_action": "要求提供地方政府补贴承诺函,核实历史补贴到位率(建议≥80%)", + "alternative_actions": [ + "提供运营收入构成,证明补贴占比<40%,现金流多元化", + "说明补贴延迟应对预案(如银行授信、应收账款保理)", + "提供同行业可比公司补贴情况,证明行业普遍性" + ], + "reference_case": { + "case_id": "CAS-2025-09-067", + "customer_name": "绿动公交集团", + "scenario": "新能源公交运营,补贴延迟但运营收入稳定", + "outcome": "证明运营收入占比65%,补贴延迟不影响还款,正常通过" + } + } + } + ], + "summary": { + "high_count": 2, + "medium_count": 3, + "low_count": 0, + "focus_areas": ["财务杠杆", "信用状况", "法律风险", "对外担保", "行业风险"], + "overall_confidence": 0.86, + "data_coverage": "99 个案例" + } + } + + +@router.get("/health") +async def health_check(): + return {"status": "ok", "service": "风险研判助手(双轨驱动版)"} diff --git a/backend/api/xingbang_api.py b/backend/api/xingbang_api.py deleted file mode 100644 index 8ce3785..0000000 --- a/backend/api/xingbang_api.py +++ /dev/null @@ -1,145 +0,0 @@ -"""Xingbang API — 兴邦金租评审工作流 + 自进化模块的 HTTP 接口。 - -端: - POST /api/xingbang/workflow 运行五智能体审批流水线(返回全部产出) - POST /api/xingbang/workflow/stream 流式运行(SSE, 逐步展示) - GET /api/xingbang/evolution 查看自进化洞察 - GET /api/xingbang/trajectories 列出历史轨迹 - GET /api/xingbang/trajectories/{id} 读取单条轨迹 - -复用 mini-OpenClaw 的 AgentManager.llm 与后端基础设施。 -""" - -from __future__ import annotations - -import json -import os -from pathlib import Path -from typing import Any - -from fastapi import APIRouter -from pydantic import BaseModel -from sse_starlette.sse import EventSourceResponse - -from graph.agent import agent_manager -from xingbang.workflow import XingbangWorkflow -from xingbang.config import XINGBANG_CONFIG - -router = APIRouter(prefix="/api/xingbang", tags=["xingbang"]) - - -# 懒加载单例工作流(复用 agent_manager.llm) -_workflow: XingbangWorkflow | None = None - - -def _get_workflow() -> XingbangWorkflow: - global _workflow - if _workflow is None: - if agent_manager._llm is None: - # 若 agent 未初始化, 手动初始化(需要 BASE_DIR) - from pathlib import Path as _P - _workflow = XingbangWorkflow( - llm=None, - base_dir=_P(__file__).resolve().parent.parent, - evolution_enabled=True, - ) - raise RuntimeError("Agent 未初始化, 请先启动后端(会自动初始化 agent_manager)") - _workflow = XingbangWorkflow( - llm=agent_manager._llm, - base_dir=Path(__file__).resolve().parent.parent, - evolution_enabled=True, - ) - return _workflow - - -class WorkflowRequest(BaseModel): - task: str # 客户/项目描述 - customer: str = "" # 客户名(用于轨迹标注) - - -@router.get("/config") -async def get_config(): - """返回兴邦项目配置(数据源、五智能体、档位)。""" - return XINGBANG_CONFIG - - -@router.post("/workflow") -async def run_workflow(req: WorkflowRequest): - """运行完整五智能体审批流水线。""" - wf = _get_workflow() - try: - result = await wf.run(req.task, customer=req.customer) - return result - except RuntimeError as e: - from fastapi import HTTPException - raise HTTPException(status_code=500, detail=str(e)) - - -@router.post("/workflow/stream") -async def run_workflow_stream(req: WorkflowRequest): - """流式运行(SSE), 逐步展示五智能体进度与产出。""" - wf = _get_workflow() - - async def gen(): - async for event in wf.run_stream(req.task, customer=req.customer): - yield { - "event": event.get("event", "event"), - "data": json.dumps( - {k: v for k, v in event.items() if k != "event"}, - ensure_ascii=False, - ), - } - - return EventSourceResponse(gen()) - - -class FeedbackRequest(BaseModel): - trajectory_id: str - feedback: str # "positive" | "negative" - - -@router.post("/evolution/feedback") -async def submit_feedback(req: FeedbackRequest): - """提交用户反馈(Level 2 奖励信号), 追加进轨迹。""" - try: - traj_file = ( - Path(__file__).resolve().parent.parent - / "xingbang_evolution" / "trajectories" / f"{req.trajectory_id}.json" - ) - if not traj_file.exists(): - return {"error": f"轨迹不存在: {req.trajectory_id}"} - data = json.loads(traj_file.read_text(encoding="utf-8")) - data.setdefault("user_feedback", req.feedback) - data["user_feedback"] = req.feedback - traj_file.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") - return {"status": "ok", "trajectory_id": req.trajectory_id, "feedback": req.feedback} - except Exception as e: - return {"error": str(e)} - - -@router.get("/evolution") -async def get_evolution(): - """查看自进化洞察报告。""" - wf = _get_workflow() - report = wf.evolution.generate_insights() - return {"insights": report, "config": XINGBANG_CONFIG} - - -@router.get("/trajectories") -async def list_trajectories(): - """列出所有已保存轨迹。""" - wf = _get_workflow() - paths = wf.recorder.list_trajectories() - return {"count": len(paths), - "trajectories": [p.stem for p in paths]} - - -@router.get("/trajectories/{trajectory_id}") -async def get_trajectory(trajectory_id: str): - """读取单条轨迹详情。""" - wf = _get_workflow() - traj = wf.recorder.load(trajectory_id) - if not traj: - from fastapi import HTTPException - raise HTTPException(status_code=404, detail="轨迹不存在") - return traj diff --git a/backend/api/xingbang_router.py b/backend/api/xingbang_router.py new file mode 100644 index 0000000..5670f54 --- /dev/null +++ b/backend/api/xingbang_router.py @@ -0,0 +1,372 @@ +""" +兴邦金租智能体系统 API 路由 +基于2026-08-18会议纪要真实痛点设计 +""" + +from fastapi import APIRouter, HTTPException +from fastapi.responses import StreamingResponse +from pydantic import BaseModel +from typing import Optional, Dict, Any +import json +import asyncio +from pathlib import Path + +from graph.agent import agent_manager + +router = APIRouter(prefix="/api/xingbang", tags=["xingbang"]) + + +async def _xingbang_llm(user_prompt: str, system_prompt: str) -> str: + """调用 mini-OpenClaw 核心已初始化的 DeepSeek LLM,构建带差异化 system prompt 的临时 agent。 + + 兼容核心 AgentManager:只复用其 LLM,不依赖不存在的 get_response 等方法。 + 启用后可替换为本地模型(Qwen)以支持"数据不出域"。 + """ + from langchain.agents import create_agent + from langchain_core.messages import HumanMessage + + if agent_manager._llm is None: + from graph.agent import agent_manager as _am + _am.initialize(Path(__file__).resolve().parent.parent) + agent = create_agent( + model=agent_manager._llm, + tools=[], # 兴邦各 Agent 为纯分析角色,暂不绑定核心工具 + system_prompt=system_prompt, + ) + try: + result = await agent.ainvoke({"messages": [HumanMessage(content=user_prompt)]}) + msgs = result.get("messages", []) + for m in reversed(msgs): + if hasattr(m, "content") and m.type == "ai" and m.content: + return m.content + return str(result.get("output", "")) + except Exception as e: # noqa + return f"[Agent 调用失败] {e}" + +# ==================== 请求/响应模型 ==================== + +class WorkflowRequest(BaseModel): + """完整五Agent流水线请求""" + customer_name: str + project_type: str = "对公新能源" # 对公新能源/零售/其他 + amount: float # 申请金额(亿元) + +class AgentRequest(BaseModel): + """单个Agent执行请求""" + agent_type: str # data_gather / risk_screen / case_search / approval_advice / risk_monitor + input_data: Dict[str, Any] + + +# ==================== 五大Agent System Prompts ==================== + +AGENT_PROMPTS = { + "data_gather": """你是【数据汇聚Agent】,负责融资租赁项目的多源数据整理与情报卡生成。 + +**职责:** +1. 从多源异构数据(财报PDF、工商信息、征信、中诚信评级模板、租赁物估值等)整理生成统一的"客户情报卡" +2. 进行数据校验:交叉核对工商信息、财报数据、征信记录的一致性 +3. 标注风险标签:现金流压力、关联交易、对外担保、涉诉情况 +4. 标明"人工待核实项":数据缺失、矛盾、需现场尽调确认的项 + +**输出格式(Markdown):** +# 客户情报卡 - {客户名称} + +## 基本信息 +- 注册资本、成立时间、法人、股东结构 +- 主营业务、所属行业 + +## 财务概况 +- 近三年营收/净利润/现金流 +- 资产负债率、流动比率 +- 数据来源:{财报年份} + +## 征信情况 +- 对外担保金额、涉诉记录 +- 信用评级(中诚信模板结果) + +## 租赁物信息 +- 租赁物类型、估值、覆盖率 + +## 风险标签 +- 🔴 高风险:{具体事项} +- 🟡 中风险:{具体事项} + +## 人工待核实项 +- [ ] {事项1} +- [ ] {事项2} + +**原则:只汇聚和标注,不做结论性判断。** +""", + + "risk_screen": """你是【预审参谋Agent】,负责生成风险筛查清单。 + +**核心原则(对齐会议要求):** +- **给风险点,不给结论**:列出所有潜在风险项及制度依据,但不给"通过/不通过"的结论性建议 +- **降低业务评审对立**:前置风险筛查,让业务部门提前知晓评审会关注的点 +- **标注置信度**:每个风险点标注置信度(高/中/低),基于数据完整性和历史经验 + +**职责:** +1. 基于"客户情报卡",按公司评审手册逐项筛查风险点 +2. 引用制度条款和历史案例作为依据 +3. 提出"建议核实方向"(不是建议结论) + +**输出格式(Markdown):** +# 风险筛查清单 - {客户名称} + +## 风险点1:{风险事项} +- **置信度**:⚠️ 高 +- **制度依据**:《评审手册》第X条 - {具体条款} +- **现象描述**:{客户情报卡中的具体数据/情况} +- **建议核实方向**:需现场核实{具体方向},或要求客户补充{资料} + +## 风险点2:... + +## 汇总 +- 高风险项:{数量} +- 中风险项:{数量} +- 建议重点关注领域:{现金流/担保/租赁物合规/...} + +**不要输出"建议通过/不通过"等结论性意见。** +""", + + "case_search": """你是【经验检索Agent】,负责从历史案例库中检索相似案例。 + +**职责:** +1. 基于客户行业、规模、风险特征检索历史案例(成功+失败) +2. 提取案例的关键决策依据和风险缓释措施 +3. 帮助"拉平评审标准差异":展示同类风险在历史上的不同评审员如何判断 + +**输出格式(Markdown):** +# 相似案例检索 - {客户名称} + +## 案例1:{历史客户名称}({通过/被否}) +- **相似度**:85% +- **项目概况**:{行业、金额、时间} +- **关键风险点**:{当时识别的风险} +- **评审决策**:{通过/被否/附加条件} +- **后续表现**:{是否逾期、结清情况} +- **经验提示**:{对当前项目的参考价值} + +## 案例2:... + +## 经验总结 +- 同行业历史不良率:{%} +- 关键风控措施:{担保增信/租赁物覆盖率/定期检查} + +**目标:让评审员看到"过去怎么做的",作为当前决策参考。** +""", + + "approval_advice": """你是【审批意见Agent】,负责生成标准化审批参考意见。 + +**职责:** +1. 汇总前三个Agent(数据汇聚、风险筛查、经验检索)的产出 +2. 生成标准化审批参考文档,包含:建议结论、证据引用、风险缓释建议 +3. **明确标注"AI参考意见,最终决策权在评审员"** + +**输出格式(Markdown):** +# 审批参考意见 - {客户名称} + +> ⚠️ **重要提示**:本意见由AI生成,仅供评审员参考,最终决策权在评审员及审批委员会。评审员可一票否决。 + +## 一、项目概要 +- 客户:{名称} +- 申请金额:{金额}亿元 +- 租赁物:{类型} +- 期限:{年限} + +## 二、综合评估 +- **数据完整性**:✅ 完整 / ⚠️ 部分缺失 +- **主要风险**:{列出Top 3风险点及来源} +- **历史案例参考**:{相似案例结论} + +## 三、AI参考建议 +- **倾向性意见**:🟢 建议通过 / 🟡 建议附加条件通过 / 🔴 建议审慎评估 +- **建议条件**(如通过): + 1. {风险缓释措施1,如追加担保} + 2. {风险缓释措施2,如提高租赁物覆盖率} +- **重点关注事项**:{评审员需额外核实的点} + +## 四、证据链 +- 数据来源:{财报/征信/工商/中诚信} +- 风险依据:{制度条款引用} +- 历史案例:{案例编号} + +**评审员有权基于独立判断修改或否决本意见。** +""", + + "risk_monitor": """你是【风险监控Agent】,负责审批空窗期与租后动态监控。 + +**职责:** +1. 审批空窗期(立项→上会,约17工作日):动态监控客户征信、涉诉、舆情变化 +2. 租后阶段:定期检查还款记录、保险覆盖、租赁物状态 +3. 预警触发:发现风险信号时立即推送给评审员/租后管理岗 + +**输出格式(Markdown):** +# 风险监控报告 - {客户名称} + +## 监控周期 +- 起始:{立项日期} +- 当前:{今日日期} +- 距上会:{剩余天数}工作日 + +## 动态风险扫描 +### 征信变化 +- ✅ 无新增逾期记录 +- 🔴 新增涉诉:{案件概要} [需立即关注] + +### 舆情监控 +- 🟡 负面舆情:{媒体报道/社交媒体} + +### 保险覆盖(租后) +- 保单状态:✅ 有效 / ⚠️ 即将到期 +- 覆盖率:{保额/租赁物价值} = {%} +- 条款检查:{是否足额覆盖风险,如泡水、盗抢等} + +## 预警触发 +- 🚨 高优先级:{需立即处理的风险} +- ⚠️ 中优先级:{需关注的异常} + +## 建议行动 +- [ ] 要求客户说明{具体事项} +- [ ] 现场核查{租赁物/经营状况} +- [ ] 补充{担保/保险} + +**对齐会议痛点:"17天动态风险无预警" → 现在有了实时监控。** +""" +} + + +# ==================== 单Agent执行 ==================== + +async def execute_single_agent(agent_type: str, input_data: Dict[str, Any]) -> Dict[str, Any]: + """执行单个Agent""" + if agent_type not in AGENT_PROMPTS: + raise HTTPException(400, f"Unknown agent type: {agent_type}") + + system_prompt = AGENT_PROMPTS[agent_type] + user_prompt = f"""请基于以下输入数据执行任务: + +{json.dumps(input_data, ensure_ascii=False, indent=2)} + +严格按照你的角色职责和输出格式生成报告。""" + + # 调用核心Agent(复用 mini-OpenClaw 的 agent_manager 及其 LLM) + messages = [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_prompt} + ] + + # 复用 mini-OpenClaw 核心已初始化的 DeepSeek LLM,构建带差异化 system prompt 的临时 agent + response = await _xingbang_llm(user_prompt, system_prompt) + + return { + "agent_type": agent_type, + "output": response, + "status": "completed" + } + + +# ==================== 完整流水线(SSE流式) ==================== + +async def run_workflow_stream(customer_name: str, project_type: str, amount: float): + """五Agent流水线 SSE流式执行""" + + # 模拟数据包(实际应从数据库/文件读取) + mock_data = { + "customer_name": customer_name, + "project_type": project_type, + "amount": amount, + "financial_data": { + "revenue_2025": 15.2, + "revenue_2024": 13.8, + "revenue_2023": 12.1, + "net_profit_2025": 1.8, + "cash_flow_2025": 2.3, + "debt_ratio": 0.62 + }, + "credit_info": { + "external_guarantee": 3.5, + "litigation_count": 2, + "credit_rating": "AA-" + }, + "lease_asset": { + "type": "新能源公交车50台", + "valuation": 4.2, + "coverage_ratio": 1.4 + } + } + + # Agent执行顺序 + agents = [ + ("data_gather", "数据汇聚"), + ("risk_screen", "预审参谋"), + ("case_search", "经验检索"), + ("approval_advice", "审批意见"), + ("risk_monitor", "风险监控") + ] + + context = {"initial_data": mock_data} + + for agent_type, agent_name in agents: + # 发送agent_start事件 + yield f"event: agent_start\ndata: {json.dumps({'agent': agent_name, 'type': agent_type}, ensure_ascii=False)}\n\n" + + await asyncio.sleep(0.5) # 模拟处理延迟 + + try: + result = await execute_single_agent(agent_type, context) + context[agent_type] = result["output"] # 传递给下一个Agent + + # 发送agent_end事件 + yield f"event: agent_end\ndata: {json.dumps({'agent': agent_name, 'type': agent_type, 'output': result['output']}, ensure_ascii=False)}\n\n" + + except Exception as e: + yield f"event: error\ndata: {json.dumps({'agent': agent_name, 'error': str(e)}, ensure_ascii=False)}\n\n" + break + + # 完成 + yield f"event: done\ndata: {json.dumps({'status': 'completed'}, ensure_ascii=False)}\n\n" + + +# ==================== API端点 ==================== + +@router.post("/workflow/stream") +async def workflow_stream(req: WorkflowRequest): + """运行完整五Agent流水线(SSE)""" + return StreamingResponse( + run_workflow_stream(req.customer_name, req.project_type, req.amount), + media_type="text/event-stream" + ) + + +@router.post("/agent/execute") +async def execute_agent(req: AgentRequest): + """执行单个Agent""" + result = await execute_single_agent(req.agent_type, req.input_data) + return result + + +@router.get("/config") +async def get_config(): + """获取系统配置""" + return { + "agents": [ + {"id": "data_gather", "name": "数据汇聚Agent", "description": "多源数据整理生成情报卡"}, + {"id": "risk_screen", "name": "预审参谋Agent", "description": "给风险点不给结论,前置筛查"}, + {"id": "case_search", "name": "经验检索Agent", "description": "历史案例检索与经验复用"}, + {"id": "approval_advice", "name": "审批意见Agent", "description": "生成标准化审批参考意见"}, + {"id": "risk_monitor", "name": "风险监控Agent", "description": "审批空窗期+租后动态监控"} + ], + "project_types": ["对公新能源", "对公传统", "零售乘用车", "零售二手车"], + "model_config": { + "provider": "DeepSeek", + "model": "deepseek-v4-flash", + "base_url": "http://49.232.133.151/v1" + } + } + + +@router.get("/health") +async def health_check(): + """健康检查""" + return {"status": "ok", "service": "兴邦智能体系统"} diff --git a/backend/api/xingbang_unified.py b/backend/api/xingbang_unified.py new file mode 100644 index 0000000..ee035c0 --- /dev/null +++ b/backend/api/xingbang_unified.py @@ -0,0 +1,432 @@ +"""兴邦金租智能评审助手 - 统一API(7 个助手完整版) + +基于 2026.8.18 会议痛点的完整解决方案: +1. 立项前置风控助手 🔍 - 业务经理风险自查 +2. 中诚信智能填报助手 📊 - 激活十几万投资的评级模板 +3. 空窗期动态监控助手 🚨 - 17天空窗期风险重扫 +4. 保单条款合规核验助手 🛡️ - OCR+条款解析 +5. 评审报告辅助生成助手 📄 - 看资料+写报告提效 +6. 主审员智能分配助手 🎯 - 多维度负荷优化 +7. AI预审过滤+加速助手 🤖 - 团队经验预判+加速 +""" + +import asyncio +import json +from datetime import datetime +from typing import AsyncGenerator, Dict, List, Optional + +from fastapi import APIRouter, HTTPException +from fastapi.responses import StreamingResponse +from pydantic import BaseModel, Field + +# 导入数据层 +from data_layer.erp_connector import get_erp_connector +from data_layer.history_store import get_history_store +from data_layer.reviewer_profile import get_reviewer_store +from data_layer.team_experience import get_team_experience_store + +router = APIRouter(prefix="/api/xingbang/unified", tags=["兴邦智能评审统一接口"]) + + +# ============ Pydantic 模型 ============ + +class ProjectBasicInfo(BaseModel): + """项目基础信息""" + project_id: str = "" + project_name: str + client_name: str + industry: str + credit_amount: float # 万元 + lease_object: str = "" + lease_value: float = 0 # 万元 + + +class FinancialData(BaseModel): + """财务数据""" + registered_capital: float = 0 + established_years: int = 0 + asset_liability_ratio: float = 0 + current_ratio: float = 0 + quick_ratio: float = 0 + net_profit_2024: float = 0 + net_profit_2025: float = 0 + revenue_growth_rate: float = 0 + + +class CreditReport(BaseModel): + """征信报告""" + overdue_count: int = 0 + query_count_recent_6m: int = 0 + credit_limit: float = 0 + enforcement_records: int = 0 + + +class InsurancePolicyData(BaseModel): + """保单数据""" + policy_number: str + insurance_company: str + policy_type: str + coverage_amount: float + exclusions: List[str] = [] + policy_pdf_path: str = "" + + +# ============ 助手 1:立项前置风控 ============ + +class Assistant1Request(BaseModel): + """立项前置风控请求""" + project: ProjectBasicInfo + financial: FinancialData + credit: CreditReport + + +@router.post("/assistant1/risk-check") +async def assistant1_risk_check(request: Assistant1Request): + """助手1:立项前置风控自查(只给风险点,不给结论)""" + + async def generate(): + try: + # 获取团队经验 + team_exp = get_team_experience_store() + thresholds = team_exp.get_approval_thresholds() + risk_patterns = team_exp.get_risk_patterns(request.project.industry) + red_flags = team_exp.get_industry_red_flags(request.project.industry) + + yield f"data: {json.dumps({'type': 'start'}, ensure_ascii=False)}\n\n" + + # 生成 Markdown 报告 + content = f"""# 立项前置风险自查清单 + +## 📋 项目基本信息 +- **项目名称**:{request.project.project_name} +- **客户名称**:{request.project.client_name} +- **行业**:{request.project.industry} +- **授信金额**:{request.project.credit_amount} 万元 +- **租赁物**:{request.project.lease_object or '未填写'} +- **租赁物价值**:{request.project.lease_value} 万元 + +--- + +## 🔍 风险自查结果 + +""" + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + # 检查高风险项 + high_risks = [] + medium_risks = [] + low_risks = [] + + # 1. 客户资质检查 + if request.financial.registered_capital < 1000: + high_risks.append("**注册资本不足**:注册资本 {:.0f} 万元 < 1000 万元(标准要求)\n - **建议**:考虑提高授信门槛或要求集团担保".format(request.financial.registered_capital)) + + if request.financial.established_years < 3: + medium_risks.append("**成立时间较短**:成立 {} 年 < 3 年(标准要求)\n - **建议**:补充实控人其他企业经营情况".format(request.financial.established_years)) + + # 2. 财务指标检查 + asset_liability = request.financial.asset_liability_ratio + threshold_al = thresholds.get("资产负债率", {}) + + if asset_liability > threshold_al.get("critical", 0.8): + high_risks.append(f"**资产负债率过高**:{asset_liability:.1%} > {threshold_al.get('critical', 0.8):.0%}(警戒线)\n - **团队共识**:{threshold_al.get('team_consensus', '需要额外担保')}") + elif asset_liability > threshold_al.get("warning", 0.7): + medium_risks.append(f"**资产负债率偏高**:{asset_liability:.1%}(位于 {threshold_al.get('warning', 0.7):.0%}-{threshold_al.get('critical', 0.8):.0%} 区间)\n - **建议**:补充负债结构说明,关注短期偿债压力") + else: + low_risks.append(f"✅ **资产负债率**:{asset_liability:.1%}(良好)") + + current_ratio = request.financial.current_ratio + threshold_cr = thresholds.get("流动比率", {}) + + if current_ratio < threshold_cr.get("critical", 1.0): + high_risks.append(f"**流动比率不足**:{current_ratio:.2f} < {threshold_cr.get('critical', 1.0):.2f}(红线)\n - **团队共识**:{threshold_cr.get('team_consensus', '短期偿债能力堪忧')}") + elif current_ratio < threshold_cr.get("warning", 1.2): + medium_risks.append(f"**流动比率偏低**:{current_ratio:.2f}\n - **建议**:关注应收账款周转情况") + else: + low_risks.append(f"✅ **流动比率**:{current_ratio:.2f}(符合标准)") + + # 3. 征信检查 + if request.credit.overdue_count > 0: + high_risks.append(f"**征信逾期记录**:近 2 年有 {request.credit.overdue_count} 次逾期\n - **建议**:要求客户提供逾期说明及还款证明") + else: + low_risks.append("✅ **征信记录**:近 2 年无逾期(良好)") + + if request.credit.enforcement_records > 0: + high_risks.append(f"**被执行记录**:存在 {request.credit.enforcement_records} 条被执行记录\n - **重要**:评审大概率退回,需先解决执行案件") + else: + low_risks.append("✅ **被执行记录**:无(良好)") + + if request.credit.query_count_recent_6m > 10: + medium_risks.append(f"**征信查询频繁**:近 6 个月查询 {request.credit.query_count_recent_6m} 次\n - **建议**:了解客户融资需求是否过于迫切") + + # 4. 租赁物检查 + if request.project.lease_value > 0: + coverage_ratio = request.project.lease_value / request.project.credit_amount + if coverage_ratio < 1.2: + high_risks.append(f"**租赁物价值不足**:租赁物价值 {request.project.lease_value} 万元,覆盖率 {coverage_ratio:.1%} < 120%(标准要求)\n - **建议**:补充其他担保措施") + else: + low_risks.append(f"✅ **租赁物覆盖率**:{coverage_ratio:.1%}(符合要求)") + + # 5. 行业红线检查 + if red_flags: + content_flags = f"\n### ⚠️ {request.project.industry} 行业常见风险点\n\n" + for flag in red_flags: + content_flags += f"- {flag}\n" + content_flags += "\n**建议**:核实客户是否触及以上风险点\n\n" + yield f"data: {json.dumps({'type': 'content', 'data': content_flags}, ensure_ascii=False)}\n\n" + + # 输出风险项 + if high_risks: + content_high = "### 🔴 高风险项(必须处理,否则评审大概率退回)\n\n" + for i, risk in enumerate(high_risks, 1): + content_high += f"{i}. {risk}\n\n" + yield f"data: {json.dumps({'type': 'content', 'data': content_high}, ensure_ascii=False)}\n\n" + + if medium_risks: + content_medium = "### 🟡 中风险项(建议补充材料或说明)\n\n" + for i, risk in enumerate(medium_risks, 1): + content_medium += f"{i}. {risk}\n\n" + yield f"data: {json.dumps({'type': 'content', 'data': content_medium}, ensure_ascii=False)}\n\n" + + if low_risks: + content_low = "### 🟢 低风险项(符合要求)\n\n" + for risk in low_risks: + content_low += f"- {risk}\n" + content_low += "\n" + yield f"data: {json.dumps({'type': 'content', 'data': content_low}, ensure_ascii=False)}\n\n" + + # 整体评估 + if len(high_risks) == 0 and len(medium_risks) == 0: + suggestion = "✅ **直接提交**:项目风险可控,可直接提交评审" + elif len(high_risks) == 0: + suggestion = "🟡 **补充材料后提交**:建议先处理中风险项,提高通过率" + else: + suggestion = "🔴 **重大调整后提交**:存在高风险项,建议先与客户沟通调整方案" + + content_final = f"""--- + +## 💡 整体评估 + +{suggestion} + +**说明**:本清单基于团队历史评审经验生成,仅供参考,不代表评审结论。最终是否通过由主审员独立判断。 +""" + yield f"data: {json.dumps({'type': 'content', 'data': content_final}, ensure_ascii=False)}\n\n" + yield f"data: {json.dumps({'type': 'done'}, ensure_ascii=False)}\n\n" + + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(generate(), media_type="text/event-stream") + + +# ============ 助手 6:主审员智能分配 ============ + +class Assistant6Request(BaseModel): + """主审员智能分配请求""" + project_industry: str + project_amount: float # 万元 + project_complexity: int = Field(3, ge=1, le=5, description="项目复杂度 1-5") + + +@router.post("/assistant6/allocate") +async def assistant6_allocate(request: Assistant6Request): + """助手6:主审员智能分配(多维度评分)""" + + try: + reviewer_store = get_reviewer_store() + + # 获取推荐列表 + recommendations = reviewer_store.get_recommended_reviewers( + project_industry=request.project_industry, + project_complexity=request.project_complexity, + top_n=3 + ) + + # 获取所有主审员当前状态 + all_profiles = reviewer_store.get_all_profiles() + reviewer_status = [ + { + "name": name, + "current_projects": profile["current_projects"], + "specialty": profile["specialty"], + "workload_capacity": profile["workload_capacity"], + "status": "🔴 超载" if profile["current_projects"] >= profile["workload_capacity"] + else ("🟡 偏高" if profile["current_projects"] >= profile["workload_capacity"] * 0.8 else "🟢 正常") + } + for name, profile in all_profiles.items() + ] + + return { + "reviewers": reviewer_status, + "recommendations": recommendations, + "project_info": { + "industry": request.project_industry, + "amount": request.project_amount, + "complexity": request.project_complexity, + }, + "algorithm_version": "v1.0_multidim_scoring", + } + + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +# ============ 助手 7:AI预审过滤+加速 ============ + +class Assistant7Request(BaseModel): + """AI预审请求""" + project: ProjectBasicInfo + financial: FinancialData + credit: CreditReport + + +@router.post("/assistant7/pre-screen") +async def assistant7_pre_screen(request: Assistant7Request): + """助手7:AI预审过滤(基于团队经验,快速判断是否值得进入正式评审)""" + + async def generate(): + try: + yield f"data: {json.dumps({'type': 'start'}, ensure_ascii=False)}\n\n" + + # 获取团队经验和历史案例 + team_exp = get_team_experience_store() + history_store = get_history_store() + + thresholds = team_exp.get_approval_thresholds() + rejection_reasons = team_exp.get_common_rejection_reasons(top_n=5) + + # 相似案例检索 + similar_cases = history_store.search_similar_cases( + industry=request.project.industry, + credit_amount=request.project.credit_amount, + limit=3 + ) + + content = f"""# AI预审报告(团队经验模式) + +## 📊 项目信息 +- **项目名称**:{request.project.project_name} +- **客户名称**:{request.project.client_name} +- **行业**:{request.project.industry} +- **授信金额**:{request.project.credit_amount} 万元 + +--- + +## 🤖 AI预审判断(基于团队 {team_exp.get_metadata().get('total_cases_analyzed', 0)} 个历史案例) + +""" + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + # 快速红线检查 + red_line_hit = [] + warning_items = [] + + # 1. 财务红线 + if request.financial.asset_liability_ratio > thresholds.get("资产负债率", {}).get("critical", 0.8): + red_line_hit.append("资产负债率超过红线") + + if request.financial.current_ratio < thresholds.get("流动比率", {}).get("critical", 1.0): + red_line_hit.append("流动比率低于红线") + + # 2. 征信红线 + if request.credit.enforcement_records > 0: + red_line_hit.append(f"存在 {request.credit.enforcement_records} 条被执行记录") + + if request.credit.overdue_count > 2: + red_line_hit.append(f"征信逾期次数过多({request.credit.overdue_count} 次)") + + # 3. 警告项 + if request.financial.revenue_growth_rate < 0: + warning_items.append("营收负增长") + + if request.credit.query_count_recent_6m > 10: + warning_items.append("征信查询频繁") + + # AI预审结论 + if red_line_hit: + conclusion = "🔴 **不建议进入正式评审**" + reason_text = "\n".join([f"- {item}" for item in red_line_hit]) + content_conclusion = f"""### {conclusion} + +**触及红线项**: +{reason_text} + +**建议**:与客户沟通调整方案,或直接退回立项申请,避免浪费评审资源。 + +""" + elif len(warning_items) >= 2: + conclusion = "🟡 **建议谨慎评审**" + reason_text = "\n".join([f"- {item}" for item in warning_items]) + content_conclusion = f"""### {conclusion} + +**警告项**: +{reason_text} + +**建议**:可以进入正式评审,但需重点关注以上风险点,建议增加尽调深度。 + +""" + else: + conclusion = "🟢 **建议进入正式评审**" + content_conclusion = f"""### {conclusion} + +**初步判断**:项目风险可控,符合基本准入标准,建议正常推进评审流程。 + +""" + + yield f"data: {json.dumps({'type': 'content', 'data': content_conclusion}, ensure_ascii=False)}\n\n" + + # 相似案例参考 + if similar_cases: + content_cases = "\n---\n\n## 📚 相似案例参考\n\n" + for i, case in enumerate(similar_cases, 1): + result_emoji = "✅" if case.get("review_result") == "approved" else "❌" + content_cases += f"""### 案例 {i}:{case.get('project_name', '未知项目')} +- **行业**:{case.get('industry', '未知')} +- **金额**:{case.get('credit_amount', 0)} 万元 +- **评审结果**:{result_emoji} {case.get('review_result', '未知')} +- **关键风险点**:{', '.join([r.get('type', '未知') for r in case.get('identified_risks', [])])} + +""" + yield f"data: {json.dumps({'type': 'content', 'data': content_cases}, ensure_ascii=False)}\n\n" + + # 高频退回原因提示 + content_tips = "\n---\n\n## 💡 团队经验提示(高频退回原因)\n\n" + for reason in rejection_reasons: + content_tips += f"- **{reason['reason']}**({reason['count']} 次) → {reason['solution']}\n" + + yield f"data: {json.dumps({'type': 'content', 'data': content_tips}, ensure_ascii=False)}\n\n" + + yield f"data: {json.dumps({'type': 'done'}, ensure_ascii=False)}\n\n" + + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(generate(), media_type="text/event-stream") + + +# ============ 数据管理接口 ============ + +@router.get("/data/reviewers") +async def get_all_reviewers(): + """获取所有主审员画像""" + reviewer_store = get_reviewer_store() + return reviewer_store.get_all_profiles() + + +@router.get("/data/team-experience") +async def get_team_experience(): + """获取团队集体经验""" + team_exp = get_team_experience_store() + return { + "approval_thresholds": team_exp.get_approval_thresholds(), + "common_rejections": team_exp.get_common_rejection_reasons(), + "metadata": team_exp.get_metadata(), + } + + +@router.get("/data/history-stats") +async def get_history_statistics(): + """获取历史案例统计""" + history_store = get_history_store() + return history_store.get_statistics() diff --git a/backend/api/xingbang_v2.py b/backend/api/xingbang_v2.py new file mode 100644 index 0000000..c2753c1 --- /dev/null +++ b/backend/api/xingbang_v2.py @@ -0,0 +1,569 @@ +"""兴邦金租智能评审助手 API v2 — 全新设计,推翻旧五智能体框架 + +核心痛点: +1. 中诚信评级模板闲置(线下Excel手工录入) +2. 保单条款覆盖度盲区(只看形式不看内容) +3. 空窗期风险失控(17天立项到上会,期间风险恶化) +4. 评审逻辑因人而异(同一事项30%/60%/90%都可能) +5. 业务与评审对立(风险考量不一样) + +新架构:4个专项助手 +- 立项前置风控助手:业务经理立项前风险自查(只给风险点不给结论) +- 中诚信模板智能填报助手:激活闲置的中诚信模板,自动填充+评级 +- 空窗期动态监控助手:每3天重扫征信/涉诉/舆情,上会前强制重扫 +- 保单条款合规核验助手:OCR+条款解析,核验足额覆盖 +""" + +import asyncio +import json +from datetime import datetime +from pathlib import Path +from typing import AsyncGenerator + +from fastapi import APIRouter, HTTPException +from fastapi.responses import StreamingResponse +from pydantic import BaseModel + +router = APIRouter(prefix="/api/xingbang/v2", tags=["兴邦智能评审助手V2"]) + + +# ============ Pydantic 模型 ============ + +class ProjectData(BaseModel): + """项目基础数据""" + project_name: str + client_name: str + industry: str + credit_amount: float # 授信金额(万元) + lease_object: str # 租赁物 + lease_value: float # 租赁物价值(万元) + financial_data: dict # 财务数据 + credit_report: dict # 征信报告 + litigation_records: list # 涉诉记录 + sentiment_info: dict # 舆情信息 + + +class InsurancePolicy(BaseModel): + """保单数据""" + policy_number: str + insurance_company: str + coverage_amount: float # 保额(万元) + policy_type: str # 险种 + exclusions: list[str] # 除外责任 + policy_pdf_path: str = "" + + +class RiskCheckRequest(BaseModel): + """立项前置风控请求""" + project_data: ProjectData + + +class ZhongchengxinRequest(BaseModel): + """中诚信模板填报请求""" + project_data: ProjectData + + +class MonitoringRequest(BaseModel): + """空窗期监控请求""" + project_id: str + project_name: str + client_name: str + initial_data: ProjectData + + +class PolicyCheckRequest(BaseModel): + """保单核验请求""" + project_name: str + client_name: str + credit_amount: float + lease_value: float + policy: InsurancePolicy + + +# ============ 辅助函数:创建专用 LLM 实例 ============ + +def _create_assistant_llm(system_prompt: str): + """创建专用助手 LLM(复用核心 DeepSeek 配置)""" + import os + from langchain_deepseek import ChatDeepSeek + + return ChatDeepSeek( + model=os.getenv("DEEPSEEK_MODEL", "deepseek-chat"), + api_key=os.getenv("DEEPSEEK_API_KEY"), + api_base=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"), + temperature=0.3, # 降低温度,提高稳定性 + streaming=True, + ) + + +# ============ 助手1:立项前置风控助手 ============ + +SYSTEM_PROMPT_RISK_CHECK = """你是兴邦金租的"立项前置风控助手"。 + +**你的职责**: +帮助业务经理在立项前做风险自查,**只标识风险点,不给审批结论**,避免与评审员对立。 + +**工作流程**: +1. 读取项目基础数据(客户、财务、征信、涉诉、舆情) +2. 对照评审手册逐条自查 +3. 输出风险自查清单,标记: + - 🔴 高风险:必须处理,否则评审大概率退回 + - 🟡 中风险:建议补充材料或说明 + - 🟢 低风险:符合要求,可直接提交 + +**评审手册核心要点**(简化版): +- 客户资质:注册资本≥1000万、成立时间≥3年、行业准入白名单 +- 财务指标:资产负债率≤70%、流动比率≥1.2、净利润连续两年为正 +- 征信记录:近2年无逾期、无被执行记录 +- 租赁物:价值必须≥授信金额×120%、符合适格性要求 +- 涉诉舆情:无重大诉讼、无负面舆情 + +**输出格式**(Markdown): +```markdown +# 立项前置风险自查清单 + +## 项目基本信息 +- 项目名称:xxx +- 客户名称:xxx +- 授信金额:xxx万元 + +## 风险自查结果 + +### 🔴 高风险项(必须处理) +1. [风险点] 具体描述 + 建议措施 + +### 🟡 中风险项(建议处理) +1. [风险点] 具体描述 + 建议措施 + +### 🟢 低风险项(符合要求) +1. [检查项] 符合标准 + +## 整体评估 +- 建议:[直接提交 / 补充材料后提交 / 重大调整后提交] +``` + +**重要**:不要给"通过/不通过"结论,只客观列出风险点,让业务经理自己决策。 +""" + + +async def _stream_risk_check(project_data: ProjectData) -> AsyncGenerator[str, None]: + """流式生成立项前置风控报告""" + llm = _create_assistant_llm(SYSTEM_PROMPT_RISK_CHECK) + + # 构造用户消息 + user_message = f"""请对以下项目进行立项前置风控自查: + +**项目名称**:{project_data.project_name} +**客户名称**:{project_data.client_name} +**行业**:{project_data.industry} +**授信金额**:{project_data.credit_amount}万元 +**租赁物**:{project_data.lease_object} +**租赁物价值**:{project_data.lease_value}万元 + +**财务数据**: +{json.dumps(project_data.financial_data, ensure_ascii=False, indent=2)} + +**征信报告**: +{json.dumps(project_data.credit_report, ensure_ascii=False, indent=2)} + +**涉诉记录**: +{json.dumps(project_data.litigation_records, ensure_ascii=False, indent=2)} + +**舆情信息**: +{json.dumps(project_data.sentiment_info, ensure_ascii=False, indent=2)} + +请生成风险自查清单。 +""" + + async for chunk in llm.astream(user_message): + if hasattr(chunk, 'content'): + yield chunk.content + + +@router.post("/risk-check/stream") +async def risk_check_stream(request: RiskCheckRequest): + """立项前置风控自查(流式返回)""" + + async def event_generator(): + try: + yield f"data: {json.dumps({'type': 'start', 'message': '正在分析项目风险...'}, ensure_ascii=False)}\n\n" + + async for content in _stream_risk_check(request.project_data): + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + yield f"data: {json.dumps({'type': 'done', 'message': '风险自查完成'}, ensure_ascii=False)}\n\n" + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(event_generator(), media_type="text/event-stream") + + +# ============ 助手2:中诚信模板智能填报助手 ============ + +SYSTEM_PROMPT_ZHONGCHENGXIN = """你是兴邦金租的"中诚信模板智能填报助手"。 + +**背景**: +兴邦金租花了十几万咨询费购买中诚信评级模型(Excel模板),但因线下手工录入繁琐,一直闲置未用。 + +**你的职责**: +1. 从项目数据自动提取关键指标 +2. 填充中诚信评级模板 +3. 计算评级结果(3A/2A/A/BBB/BB/B/C) +4. 生成评级报告嵌入评审底稿 + +**中诚信评级模型简化版**(泛行业): +- 财务指标(40%):资产负债率、流动比率、净资产收益率、营收增长率 +- 经营能力(30%):行业地位、市场份额、客户集中度、盈利稳定性 +- 偿债能力(20%):速动比率、利息保障倍数、现金流覆盖率 +- 发展前景(10%):行业景气度、技术创新能力、政策支持 + +**评级标准**: +- 3A(90-100分):信用极好,违约风险极低 +- 2A(80-89分):信用优良,违约风险很低 +- A(70-79分):信用良好,违约风险较低 +- BBB(60-69分):信用一般,违约风险中等 +- BB(50-59分):信用较差,违约风险较高 +- B(40-49分):信用差,违约风险高 +- C(<40分):信用极差,违约风险极高 + +**输出格式**(Markdown): +```markdown +# 中诚信评级报告 + +## 项目基本信息 +- 项目名称:xxx +- 客户名称:xxx +- 行业:xxx + +## 评级指标填充 + +### 一、财务指标(40%) +| 指标 | 数值 | 得分 | +|------|------|------| +| 资产负债率 | xx% | xx分 | +| ... | ... | ... | +**小计**:xx分 + +### 二、经营能力(30%) +... + +### 三、偿债能力(20%) +... + +### 四、发展前景(10%) +... + +## 评级结果 +- **综合得分**:xx分 +- **评级等级**:2A +- **评级说明**:信用优良,违约风险很低 + +## 风险提示 +1. [关键风险点1] +2. [关键风险点2] +``` +""" + + +async def _stream_zhongchengxin_report(project_data: ProjectData) -> AsyncGenerator[str, None]: + """流式生成中诚信评级报告""" + llm = _create_assistant_llm(SYSTEM_PROMPT_ZHONGCHENGXIN) + + user_message = f"""请为以下项目生成中诚信评级报告: + +**项目名称**:{project_data.project_name} +**客户名称**:{project_data.client_name} +**行业**:{project_data.industry} +**授信金额**:{project_data.credit_amount}万元 + +**财务数据**: +{json.dumps(project_data.financial_data, ensure_ascii=False, indent=2)} + +**征信报告**: +{json.dumps(project_data.credit_report, ensure_ascii=False, indent=2)} + +请填充中诚信评级模板并计算评级。 +""" + + async for chunk in llm.astream(user_message): + if hasattr(chunk, 'content'): + yield chunk.content + + +@router.post("/zhongchengxin/stream") +async def zhongchengxin_stream(request: ZhongchengxinRequest): + """中诚信模板智能填报(流式返回)""" + + async def event_generator(): + try: + yield f"data: {json.dumps({'type': 'start', 'message': '正在填充中诚信评级模板...'}, ensure_ascii=False)}\n\n" + + async for content in _stream_zhongchengxin_report(request.project_data): + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + yield f"data: {json.dumps({'type': 'done', 'message': '中诚信评级完成'}, ensure_ascii=False)}\n\n" + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(event_generator(), media_type="text/event-stream") + + +# ============ 助手3:空窗期动态监控助手 ============ + +SYSTEM_PROMPT_MONITORING = """你是兴邦金租的"空窗期动态监控助手"。 + +**背景**: +立项到上会需要17个工作日,期间客户征信/涉诉/舆情可能恶化,但无人重扫,导致上会时信息已过时。 + +**你的职责**: +1. 对比初始数据与最新数据 +2. 识别异常变化(新增被执行、新增诉讼、舆情负面、信用卡逾期等) +3. 评估风险等级(严重/一般/轻微) +4. 生成红色预警报告 + +**监控维度**: +- 征信变化:新增逾期、新增查询记录、信用卡额度变化 +- 涉诉变化:新增诉讼、新增被执行、执行金额变化 +- 舆情变化:负面新闻、行业风险、关联企业风险 +- 消费行为:淘宝消费骤降、信用卡逾期 + +**风险等级**: +- 🔴 严重:新增被执行/重大诉讼/信用卡逾期,建议暂停审批 +- 🟡 一般:征信查询次数增多/轻微负面舆情,建议重点关注 +- 🟢 轻微:无明显异常 + +**输出格式**(Markdown): +```markdown +# 空窗期动态监控报告 + +## 项目基本信息 +- 项目名称:xxx +- 监控时间:xxx + +## 异常变化检测 + +### 🔴 严重异常 +1. [异常项] 具体描述 + 建议措施 + +### 🟡 一般异常 +1. [异常项] 具体描述 + +### 🟢 无异常 +- 征信记录:无新增异常 +- 涉诉记录:无新增异常 +- 舆情信息:无新增负面 + +## 监控建议 +- [暂停审批 / 重点关注 / 正常推进] +``` +""" + + +async def _stream_monitoring_report( + initial_data: ProjectData, + latest_data: ProjectData +) -> AsyncGenerator[str, None]: + """流式生成空窗期监控报告""" + llm = _create_assistant_llm(SYSTEM_PROMPT_MONITORING) + + user_message = f"""请对比以下项目的初始数据与最新数据,生成空窗期动态监控报告: + +**项目名称**:{initial_data.project_name} +**客户名称**:{initial_data.client_name} + +**初始数据(立项时)**: +征信报告:{json.dumps(initial_data.credit_report, ensure_ascii=False, indent=2)} +涉诉记录:{json.dumps(initial_data.litigation_records, ensure_ascii=False, indent=2)} +舆情信息:{json.dumps(initial_data.sentiment_info, ensure_ascii=False, indent=2)} + +**最新数据(当前)**: +征信报告:{json.dumps(latest_data.credit_report, ensure_ascii=False, indent=2)} +涉诉记录:{json.dumps(latest_data.litigation_records, ensure_ascii=False, indent=2)} +舆情信息:{json.dumps(latest_data.sentiment_info, ensure_ascii=False, indent=2)} + +请识别异常变化并生成监控报告。 +""" + + async for chunk in llm.astream(user_message): + if hasattr(chunk, 'content'): + yield chunk.content + + +@router.post("/monitoring/stream") +async def monitoring_stream(request: MonitoringRequest): + """空窗期动态监控(流式返回) + + 实际使用中,会定时调用此接口(每3天),传入最新数据 + 这里为演示方便,模拟一次对比 + """ + + # 模拟最新数据(实际应从外部数据源实时抓取) + latest_data = request.initial_data.model_copy() + latest_data.credit_report["overdue_count"] = latest_data.credit_report.get("overdue_count", 0) + 1 + latest_data.litigation_records.append({"case_number": "2026执1234", "amount": 50, "status": "执行中"}) + + async def event_generator(): + try: + yield f"data: {json.dumps({'type': 'start', 'message': '正在对比数据变化...'}, ensure_ascii=False)}\n\n" + + async for content in _stream_monitoring_report(request.initial_data, latest_data): + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + yield f"data: {json.dumps({'type': 'done', 'message': '监控完成'}, ensure_ascii=False)}\n\n" + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(event_generator(), media_type="text/event-stream") + + +# ============ 助手4:保单条款合规核验助手 ============ + +SYSTEM_PROMPT_POLICY_CHECK = """你是兴邦金租的"保单条款合规核验助手"。 + +**背景**: +兴邦金租要求客户为租赁物投保,保额需覆盖授信金额,但实际只检查"有没有保单",不细看条款,导致出险时发现保单回不了。 + +**你的职责**: +1. 解析保单关键信息(险种、保额、除外责任) +2. 核验是否符合兴邦要求: + - 保额 ≥ 授信金额(理想120%) + - 险种必须包含:财产一切险/车辆损失险/盗抢险(根据租赁物类型) + - 除外责任不能包含高频风险(如泡水、自然灾害等) +3. 标记风险点 +4. 生成合规核验报告 + +**兴邦保单合规要求**: +- 设备类:财产一切险/工程一切险,保额≥授信×120% +- 车辆类:车损险+盗抢险+三者险,保额≥授信×120% +- 除外责任禁止:战争、核辐射、故意损坏 OK,但泡水、自然灾害、机械故障等高频风险不能除外 + +**输出格式**(Markdown): +```markdown +# 保单条款合规核验报告 + +## 保单基本信息 +- 保单号:xxx +- 保险公司:xxx +- 险种:xxx +- 保额:xxx万元 + +## 合规核验结果 + +### ✅ 符合项 +1. [核验项] 符合要求 + +### ❌ 不符合项 +1. [核验项] 不符合要求 + 风险说明 + +## 核验结论 +- **是否合规**:[合规 / 不合规] +- **风险等级**:[无风险 / 低风险 / 高风险] +- **处理建议**:[通过 / 要求客户补正保单 / 拒绝] + +## 详细说明 +[具体分析] +``` +""" + + +async def _stream_policy_check( + project_name: str, + client_name: str, + credit_amount: float, + lease_value: float, + policy: InsurancePolicy +) -> AsyncGenerator[str, None]: + """流式生成保单合规核验报告""" + llm = _create_assistant_llm(SYSTEM_PROMPT_POLICY_CHECK) + + required_coverage = credit_amount * 1.2 # 要求120%覆盖 + + user_message = f"""请核验以下保单是否符合兴邦金租要求: + +**项目信息**: +- 项目名称:{project_name} +- 客户名称:{client_name} +- 授信金额:{credit_amount}万元 +- 租赁物价值:{lease_value}万元 +- 要求保额:≥{required_coverage}万元(120%覆盖) + +**保单信息**: +- 保单号:{policy.policy_number} +- 保险公司:{policy.insurance_company} +- 保额:{policy.coverage_amount}万元 +- 险种:{policy.policy_type} +- 除外责任:{json.dumps(policy.exclusions, ensure_ascii=False)} + +请生成合规核验报告。 +""" + + async for chunk in llm.astream(user_message): + if hasattr(chunk, 'content'): + yield chunk.content + + +@router.post("/policy-check/stream") +async def policy_check_stream(request: PolicyCheckRequest): + """保单条款合规核验(流式返回)""" + + async def event_generator(): + try: + yield f"data: {json.dumps({'type': 'start', 'message': '正在核验保单条款...'}, ensure_ascii=False)}\n\n" + + async for content in _stream_policy_check( + request.project_name, + request.client_name, + request.credit_amount, + request.lease_value, + request.policy + ): + yield f"data: {json.dumps({'type': 'content', 'data': content}, ensure_ascii=False)}\n\n" + + yield f"data: {json.dumps({'type': 'done', 'message': '保单核验完成'}, ensure_ascii=False)}\n\n" + except Exception as e: + yield f"data: {json.dumps({'type': 'error', 'message': str(e)}, ensure_ascii=False)}\n\n" + + return StreamingResponse(event_generator(), media_type="text/event-stream") + + +# ============ 综合工作台接口 ============ + +@router.get("/assistants/list") +async def list_assistants(): + """获取所有助手列表""" + return { + "assistants": [ + { + "id": "risk-check", + "name": "立项前置风控助手", + "description": "业务经理立项前风险自查,只给风险点不给结论", + "icon": "🔍", + "pain_point": "业务与评审对立,项目被退回", + "value": "降低退回率,缩短评审周期" + }, + { + "id": "zhongchengxin", + "name": "中诚信模板智能填报助手", + "description": "激活闲置的中诚信模板,自动填充+评级", + "icon": "📊", + "pain_point": "十几万的中诚信模板手工录入太繁琐", + "value": "每个项目省30-60分钟,激活投资" + }, + { + "id": "monitoring", + "name": "空窗期动态监控助手", + "description": "每3天重扫征信/涉诉/舆情,上会前强制重扫", + "icon": "🚨", + "pain_point": "17天空窗期,上会时信息已过时", + "value": "17天风险不落地,避免决策失误" + }, + { + "id": "policy-check", + "name": "保单条款合规核验助手", + "description": "OCR+条款解析,核验足额覆盖", + "icon": "🛡️", + "pain_point": "只看有没有保单,不看条款,出险回不了", + "value": "避免保单风险盲区" + } + ] + } diff --git a/backend/app.py b/backend/app.py index 671b16d..f4455e4 100644 --- a/backend/app.py +++ b/backend/app.py @@ -24,8 +24,11 @@ async def lifespan(app: FastAPI): agent_manager.initialize(BASE_DIR) # Initialize memory indexer for RAG mode - indexer = get_memory_indexer(BASE_DIR) - indexer.rebuild_index() + try: + indexer = get_memory_indexer(BASE_DIR) + indexer.rebuild_index() + except Exception as e: + print(f"⚠️ Memory indexer initialization skipped: {e}") print("✅ mini OpenClaw backend ready") yield @@ -47,7 +50,11 @@ from api.sessions import router as sessions_router from api.tokens import router as tokens_router from api.compress import router as compress_router from api.config_api import router as config_router -from api.xingbang_api import router as xingbang_router +from api.xingbang_router import router as xingbang_router +from api.xingbang_v2 import router as xingbang_v2_router +from api.review_assist import router as review_assist_router +from api.xingbang_unified import router as xingbang_unified_router +from api.risk_assess import router as risk_assess_router app.include_router(chat_router, prefix="/api") app.include_router(files_router, prefix="/api") @@ -56,8 +63,18 @@ app.include_router(tokens_router, prefix="/api") app.include_router(compress_router, prefix="/api") app.include_router(config_router, prefix="/api") app.include_router(xingbang_router) +app.include_router(xingbang_v2_router) +app.include_router(review_assist_router) +app.include_router(xingbang_unified_router) +app.include_router(risk_assess_router, prefix="/api/risk-assess") @app.get("/") async def root(): return {"name": "mini OpenClaw", "version": "0.1.0", "status": "running"} + + +# 支持 `python app.py` 直接启动后端(FastAPI + uvicorn) +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8002) diff --git a/backend/data_layer/__init__.py b/backend/data_layer/__init__.py new file mode 100644 index 0000000..2040a71 --- /dev/null +++ b/backend/data_layer/__init__.py @@ -0,0 +1,20 @@ +"""兴邦金租数据层模块 + +提供统一的数据访问接口: +- ERP 数据库连接(加密) +- 历史评审案例向量库 +- 主审员画像库 +- 团队集体经验库 +""" + +from .erp_connector import ERPConnector +from .history_store import HistoryStore +from .reviewer_profile import ReviewerProfileStore +from .team_experience import TeamExperienceStore + +__all__ = [ + "ERPConnector", + "HistoryStore", + "ReviewerProfileStore", + "TeamExperienceStore", +] diff --git a/backend/data_layer/erp_connector.py b/backend/data_layer/erp_connector.py new file mode 100644 index 0000000..a54939f --- /dev/null +++ b/backend/data_layer/erp_connector.py @@ -0,0 +1,301 @@ +"""ERP 数据库连接器(加密+只读) + +安全措施: +1. SSL/TLS 加密连接 +2. 只读账号(SELECT 权限) +3. 敏感字段脱敏 +4. 连接池管理 +5. 审计日志 +""" + +import os +import logging +from typing import Dict, List, Optional, Any +from datetime import datetime +import asyncio +from contextlib import asynccontextmanager + +# 使用 asyncpg 作为 PostgreSQL 连接器(如果 ERP 是 PostgreSQL) +# 如果是 MySQL,则使用 aiomysql +# 如果是 Oracle,则使用 cx_Oracle + +logger = logging.getLogger(__name__) + + +class ERPConnector: + """ERP 数据库连接器(加密+只读)""" + + def __init__(self): + self.host = os.getenv("ERP_DB_HOST") + self.port = int(os.getenv("ERP_DB_PORT", "5432")) + self.database = os.getenv("ERP_DB_NAME") + self.user = os.getenv("ERP_DB_USER") # 只读账号 + self.password = os.getenv("ERP_DB_PASSWORD") + self.ssl_mode = os.getenv("ERP_DB_SSL_MODE", "require") + + self._pool = None + self._audit_log_path = os.getenv("ERP_AUDIT_LOG", "./logs/erp_access.log") + + async def initialize(self): + """初始化连接池""" + if self._pool is not None: + return + + try: + # 这里以 asyncpg (PostgreSQL) 为例 + import asyncpg + + self._pool = await asyncpg.create_pool( + host=self.host, + port=self.port, + database=self.database, + user=self.user, + password=self.password, + ssl=self.ssl_mode, + min_size=2, + max_size=10, + command_timeout=30, + ) + logger.info(f"ERP 连接池初始化成功: {self.host}:{self.port}/{self.database}") + except ImportError: + logger.warning("asyncpg 未安装,使用模拟数据模式") + self._pool = "mock" # 标记为模拟模式 + except Exception as e: + logger.error(f"ERP 连接池初始化失败: {e}") + self._pool = "mock" + + async def close(self): + """关闭连接池""" + if self._pool and self._pool != "mock": + await self._pool.close() + self._pool = None + + def _audit_log(self, operation: str, table: str, filters: Dict): + """记录数据访问审计日志""" + timestamp = datetime.now().isoformat() + log_entry = f"[{timestamp}] {operation} | table={table} | filters={filters}\n" + + os.makedirs(os.path.dirname(self._audit_log_path), exist_ok=True) + with open(self._audit_log_path, "a", encoding="utf-8") as f: + f.write(log_entry) + + def _mask_sensitive_fields(self, data: Dict, sensitive_fields: List[str]) -> Dict: + """脱敏敏感字段""" + masked = data.copy() + for field in sensitive_fields: + if field in masked and masked[field]: + value = str(masked[field]) + if len(value) > 4: + masked[field] = value[:2] + "***" + value[-2:] + else: + masked[field] = "***" + return masked + + async def get_project_by_id(self, project_id: str) -> Optional[Dict]: + """根据项目 ID 获取项目基础数据""" + self._audit_log("SELECT", "projects", {"project_id": project_id}) + + if self._pool == "mock": + return self._mock_project_data(project_id) + + async with self._pool.acquire() as conn: + query = """ + SELECT + project_id, project_name, client_name, industry, + credit_amount, lease_object, lease_value, status, + created_at, updated_at + FROM projects + WHERE project_id = $1 + """ + row = await conn.fetchrow(query, project_id) + return dict(row) if row else None + + async def get_client_financial_data(self, client_name: str) -> Dict: + """获取客户财务数据""" + self._audit_log("SELECT", "client_financials", {"client_name": client_name}) + + if self._pool == "mock": + return self._mock_financial_data(client_name) + + async with self._pool.acquire() as conn: + query = """ + SELECT + registered_capital, established_years, + asset_liability_ratio, current_ratio, quick_ratio, + net_profit_2024, net_profit_2025, revenue_growth_rate, + roe, debt_to_equity_ratio + FROM client_financials + WHERE client_name = $1 + ORDER BY fiscal_year DESC + LIMIT 1 + """ + row = await conn.fetchrow(query, client_name) + return dict(row) if row else {} + + async def get_credit_report(self, client_name: str) -> Dict: + """获取征信报告(敏感信息脱敏)""" + self._audit_log("SELECT", "credit_reports", {"client_name": client_name}) + + if self._pool == "mock": + return self._mock_credit_report(client_name) + + async with self._pool.acquire() as conn: + query = """ + SELECT + overdue_count, query_count_recent_6m, + credit_limit, enforcement_records, + last_update_date + FROM credit_reports + WHERE client_name = $1 + ORDER BY last_update_date DESC + LIMIT 1 + """ + row = await conn.fetchrow(query, client_name) + data = dict(row) if row else {} + + # 脱敏:隐藏具体授信额度的前几位 + return self._mask_sensitive_fields(data, ["credit_limit"]) + + async def get_litigation_records(self, client_name: str) -> List[Dict]: + """获取涉诉记录""" + self._audit_log("SELECT", "litigation_records", {"client_name": client_name}) + + if self._pool == "mock": + return self._mock_litigation_records(client_name) + + async with self._pool.acquire() as conn: + query = """ + SELECT + case_number, court, case_type, status, + amount, filing_date, verdict_date + FROM litigation_records + WHERE client_name = $1 + ORDER BY filing_date DESC + LIMIT 10 + """ + rows = await conn.fetch(query, client_name) + return [dict(row) for row in rows] + + async def get_insurance_policies(self, project_id: str) -> List[Dict]: + """获取保单信息""" + self._audit_log("SELECT", "insurance_policies", {"project_id": project_id}) + + if self._pool == "mock": + return self._mock_insurance_policies(project_id) + + async with self._pool.acquire() as conn: + query = """ + SELECT + policy_number, insurance_company, policy_type, + coverage_amount, premium, start_date, end_date, + policy_pdf_url + FROM insurance_policies + WHERE project_id = $1 + ORDER BY start_date DESC + """ + rows = await conn.fetch(query, project_id) + return [dict(row) for row in rows] + + async def get_reviewer_workload(self) -> List[Dict]: + """获取主审员当前工作负荷""" + self._audit_log("SELECT", "reviewer_workload", {}) + + if self._pool == "mock": + return self._mock_reviewer_workload() + + async with self._pool.acquire() as conn: + query = """ + SELECT + reviewer_name, + COUNT(*) as current_projects, + STRING_AGG(industry, ',') as industries + FROM project_reviews + WHERE status IN ('待评审', '评审中') + GROUP BY reviewer_name + ORDER BY current_projects DESC + """ + rows = await conn.fetch(query) + return [dict(row) for row in rows] + + # ============ Mock 数据(用于测试/演示)============ + + def _mock_project_data(self, project_id: str) -> Dict: + """模拟项目数据""" + return { + "project_id": project_id, + "project_name": "阳光光伏设备融资租赁项目", + "client_name": "阳光光伏设备有限公司", + "industry": "光伏设备制造", + "credit_amount": 5000.0, + "lease_object": "光伏组件生产线设备", + "lease_value": 6500.0, + "status": "待评审", + "created_at": datetime.now(), + "updated_at": datetime.now(), + } + + def _mock_financial_data(self, client_name: str) -> Dict: + """模拟财务数据""" + return { + "registered_capital": 8000, + "established_years": 6, + "asset_liability_ratio": 0.58, + "current_ratio": 1.45, + "quick_ratio": 1.2, + "net_profit_2024": 1200, + "net_profit_2025": 1450, + "revenue_growth_rate": 0.18, + "roe": 0.15, + "debt_to_equity_ratio": 1.38, + } + + def _mock_credit_report(self, client_name: str) -> Dict: + """模拟征信报告""" + return { + "overdue_count": 0, + "query_count_recent_6m": 4, + "credit_limit": "20***00", # 已脱敏 + "enforcement_records": 0, + "last_update_date": datetime.now().date(), + } + + def _mock_litigation_records(self, client_name: str) -> List[Dict]: + """模拟涉诉记录""" + return [] + + def _mock_insurance_policies(self, project_id: str) -> List[Dict]: + """模拟保单数据""" + return [ + { + "policy_number": "PICC-2026-001234", + "insurance_company": "中国人保财险", + "policy_type": "财产一切险", + "coverage_amount": 7000.0, + "premium": 35.0, + "start_date": datetime(2026, 1, 1).date(), + "end_date": datetime(2029, 12, 31).date(), + "policy_pdf_url": "/policies/PICC-2026-001234.pdf", + } + ] + + def _mock_reviewer_workload(self) -> List[Dict]: + """模拟主审员负荷""" + return [ + {"reviewer_name": "张强", "current_projects": 7, "industries": "交通运输,制造业"}, + {"reviewer_name": "李明", "current_projects": 5, "industries": "制造业,新能源"}, + {"reviewer_name": "刘洋", "current_projects": 4, "industries": "医疗健康,服务业"}, + {"reviewer_name": "王芳", "current_projects": 3, "industries": "新能源,光伏"}, + ] + + +# 全局单例 +_erp_connector: Optional[ERPConnector] = None + + +async def get_erp_connector() -> ERPConnector: + """获取 ERP 连接器单例""" + global _erp_connector + if _erp_connector is None: + _erp_connector = ERPConnector() + await _erp_connector.initialize() + return _erp_connector diff --git a/backend/data_layer/history_store.py b/backend/data_layer/history_store.py new file mode 100644 index 0000000..d5f16d2 --- /dev/null +++ b/backend/data_layer/history_store.py @@ -0,0 +1,209 @@ +"""历史评审案例向量库 + +功能: +1. 存储历史评审案例(项目特征+评审结果+评审员行为) +2. 相似案例检索(基于向量相似度) +3. 评审员行为学习(团队集体经验,非个人预测) +""" + +import json +import os +from typing import List, Dict, Optional, Tuple +from datetime import datetime +from pathlib import Path +import logging + +logger = logging.getLogger(__name__) + + +class HistoryStore: + """历史评审案例存储(简化版,生产环境建议使用 Chroma/Qdrant)""" + + def __init__(self, data_dir: str = "./xingbang_data/history"): + self.data_dir = Path(data_dir) + self.data_dir.mkdir(parents=True, exist_ok=True) + self.cases_file = self.data_dir / "review_cases.jsonl" + + # 确保文件存在 + if not self.cases_file.exists(): + self.cases_file.touch() + + def add_case(self, case_data: Dict) -> str: + """添加一条历史评审案例""" + case_id = f"case_{datetime.now().strftime('%Y%m%d%H%M%S')}" + + case_record = { + "case_id": case_id, + "timestamp": datetime.now().isoformat(), + **case_data + } + + # 追加写入 JSONL + with open(self.cases_file, "a", encoding="utf-8") as f: + f.write(json.dumps(case_record, ensure_ascii=False) + "\n") + + logger.info(f"案例已保存: {case_id}") + return case_id + + def search_similar_cases( + self, + industry: str, + credit_amount: float, + risk_level: str = None, + limit: int = 5 + ) -> List[Dict]: + """检索相似案例(简化版:基于行业+金额范围)""" + + if not self.cases_file.exists(): + return [] + + similar_cases = [] + + with open(self.cases_file, "r", encoding="utf-8") as f: + for line in f: + if not line.strip(): + continue + + try: + case = json.loads(line) + + # 行业匹配 + industry_match = industry in case.get("industry", "") or case.get("industry", "") in industry + + # 金额范围匹配(±30%) + case_amount = case.get("credit_amount", 0) + amount_match = 0.7 * credit_amount <= case_amount <= 1.3 * credit_amount + + # 风险等级匹配(可选) + risk_match = risk_level is None or case.get("risk_level") == risk_level + + if industry_match and amount_match and risk_match: + similar_cases.append(case) + + except json.JSONDecodeError: + continue + + # 按时间倒序返回最近的案例 + similar_cases.sort(key=lambda x: x.get("timestamp", ""), reverse=True) + return similar_cases[:limit] + + def get_case_by_id(self, case_id: str) -> Optional[Dict]: + """根据 ID 获取案例详情""" + + if not self.cases_file.exists(): + return None + + with open(self.cases_file, "r", encoding="utf-8") as f: + for line in f: + if not line.strip(): + continue + + try: + case = json.loads(line) + if case.get("case_id") == case_id: + return case + except json.JSONDecodeError: + continue + + return None + + def get_team_patterns(self, industry: str = None) -> Dict: + """提取团队集体经验模式(高频风险点、常见处理方式)""" + + if not self.cases_file.exists(): + return {"high_frequency_risks": [], "common_solutions": []} + + risk_counter = {} + solution_counter = {} + + with open(self.cases_file, "r", encoding="utf-8") as f: + for line in f: + if not line.strip(): + continue + + try: + case = json.loads(line) + + # 行业过滤 + if industry and industry not in case.get("industry", ""): + continue + + # 统计高频风险点 + for risk in case.get("identified_risks", []): + risk_key = risk.get("type", "unknown") + risk_counter[risk_key] = risk_counter.get(risk_key, 0) + 1 + + # 统计常见解决方案 + solution = case.get("solution", "") + if solution: + solution_counter[solution] = solution_counter.get(solution, 0) + 1 + + except json.JSONDecodeError: + continue + + # 排序 + high_frequency_risks = sorted( + [{"risk": k, "count": v} for k, v in risk_counter.items()], + key=lambda x: x["count"], + reverse=True + )[:10] + + common_solutions = sorted( + [{"solution": k, "count": v} for k, v in solution_counter.items()], + key=lambda x: x["count"], + reverse=True + )[:10] + + return { + "high_frequency_risks": high_frequency_risks, + "common_solutions": common_solutions, + } + + def get_statistics(self) -> Dict: + """获取统计数据""" + + if not self.cases_file.exists(): + return {"total_cases": 0} + + total_cases = 0 + industry_dist = {} + approval_rate = {"approved": 0, "rejected": 0, "pending": 0} + + with open(self.cases_file, "r", encoding="utf-8") as f: + for line in f: + if not line.strip(): + continue + + try: + case = json.loads(line) + total_cases += 1 + + # 行业分布 + industry = case.get("industry", "未知") + industry_dist[industry] = industry_dist.get(industry, 0) + 1 + + # 通过率 + result = case.get("review_result", "pending") + if result in approval_rate: + approval_rate[result] += 1 + + except json.JSONDecodeError: + continue + + return { + "total_cases": total_cases, + "industry_distribution": industry_dist, + "approval_rate": approval_rate, + } + + +# 全局单例 +_history_store: Optional[HistoryStore] = None + + +def get_history_store() -> HistoryStore: + """获取历史案例存储单例""" + global _history_store + if _history_store is None: + _history_store = HistoryStore() + return _history_store diff --git a/backend/data_layer/reviewer_profile.py b/backend/data_layer/reviewer_profile.py new file mode 100644 index 0000000..e32c86a --- /dev/null +++ b/backend/data_layer/reviewer_profile.py @@ -0,0 +1,262 @@ +"""主审员画像库 + +功能: +1. 存储主审员基础信息(专长、效率、质量评分) +2. 动态更新主审员负荷 +3. 多维度评分(用于智能分配) +""" + +import json +from typing import Dict, List, Optional +from pathlib import Path +from datetime import datetime +import logging + +logger = logging.getLogger(__name__) + + +class ReviewerProfileStore: + """主审员画像存储""" + + def __init__(self, data_dir: str = "./xingbang_data/reviewers"): + self.data_dir = Path(data_dir) + self.data_dir.mkdir(parents=True, exist_ok=True) + self.profiles_file = self.data_dir / "reviewer_profiles.json" + + # 初始化默认数据 + if not self.profiles_file.exists(): + self._init_default_profiles() + + def _init_default_profiles(self): + """初始化默认主审员画像""" + default_profiles = { + "李明": { + "name": "李明", + "specialty": ["制造业", "新能源"], + "current_projects": 5, + "avg_completion_days": 8, + "quality_score": 0.85, # 0-1,基于历史评审质量 + "efficiency_score": 0.78, # 0-1,基于完成速度 + "workload_capacity": 7, # 最大承载项目数 + "industry_experience": { + "制造业": 45, # 历史评审项目数 + "新能源": 32, + "光伏": 18, + }, + "created_at": datetime.now().isoformat(), + "updated_at": datetime.now().isoformat(), + }, + "王芳": { + "name": "王芳", + "specialty": ["新能源", "光伏", "风电"], + "current_projects": 3, + "avg_completion_days": 7, + "quality_score": 0.92, + "efficiency_score": 0.88, + "workload_capacity": 6, + "industry_experience": { + "新能源": 56, + "光伏": 38, + "风电": 22, + }, + "created_at": datetime.now().isoformat(), + "updated_at": datetime.now().isoformat(), + }, + "张强": { + "name": "张强", + "specialty": ["交通运输", "物流", "制造业"], + "current_projects": 7, + "avg_completion_days": 9, + "quality_score": 0.80, + "efficiency_score": 0.72, + "workload_capacity": 8, + "industry_experience": { + "交通运输": 67, + "物流": 43, + "制造业": 28, + }, + "created_at": datetime.now().isoformat(), + "updated_at": datetime.now().isoformat(), + }, + "刘洋": { + "name": "刘洋", + "specialty": ["医疗健康", "服务业", "消费"], + "current_projects": 4, + "avg_completion_days": 8, + "quality_score": 0.88, + "efficiency_score": 0.82, + "workload_capacity": 7, + "industry_experience": { + "医疗健康": 51, + "服务业": 34, + "消费": 29, + }, + "created_at": datetime.now().isoformat(), + "updated_at": datetime.now().isoformat(), + }, + } + + with open(self.profiles_file, "w", encoding="utf-8") as f: + json.dump(default_profiles, f, ensure_ascii=False, indent=2) + + logger.info("主审员画像初始化完成") + + def get_all_profiles(self) -> Dict[str, Dict]: + """获取所有主审员画像""" + with open(self.profiles_file, "r", encoding="utf-8") as f: + return json.load(f) + + def get_profile(self, reviewer_name: str) -> Optional[Dict]: + """获取指定主审员画像""" + profiles = self.get_all_profiles() + return profiles.get(reviewer_name) + + def update_workload(self, reviewer_name: str, current_projects: int): + """更新主审员工作负荷""" + profiles = self.get_all_profiles() + + if reviewer_name in profiles: + profiles[reviewer_name]["current_projects"] = current_projects + profiles[reviewer_name]["updated_at"] = datetime.now().isoformat() + + with open(self.profiles_file, "w", encoding="utf-8") as f: + json.dump(profiles, f, ensure_ascii=False, indent=2) + + logger.info(f"已更新 {reviewer_name} 的负荷: {current_projects} 个项目") + + def update_quality_score(self, reviewer_name: str, quality_score: float): + """更新主审员质量评分""" + profiles = self.get_all_profiles() + + if reviewer_name in profiles: + profiles[reviewer_name]["quality_score"] = quality_score + profiles[reviewer_name]["updated_at"] = datetime.now().isoformat() + + with open(self.profiles_file, "w", encoding="utf-8") as f: + json.dump(profiles, f, ensure_ascii=False, indent=2) + + logger.info(f"已更新 {reviewer_name} 的质量评分: {quality_score:.2f}") + + def calculate_match_score( + self, + reviewer_name: str, + project_industry: str, + project_complexity: int = 3 + ) -> Dict: + """计算主审员与项目的匹配度(多维度评分) + + 评分维度: + - 35% 行业匹配度(专长+历史经验) + - 35% 可用性(当前负荷) + - 20% 质量评分(历史评审质量) + - 10% 团队平衡(避免某人长期超载) + """ + profile = self.get_profile(reviewer_name) + if not profile: + return {"total_score": 0, "breakdown": {}} + + # 1. 行业匹配度(35%) + industry_match = 0 + if any(spec in project_industry or project_industry in spec for spec in profile["specialty"]): + industry_match = 0.8 # 专长匹配 + + # 检查历史经验 + exp_count = 0 + for industry_key in profile["industry_experience"]: + if industry_key in project_industry or project_industry in industry_key: + exp_count = profile["industry_experience"][industry_key] + break + + if exp_count >= 30: + industry_match = 1.0 + elif exp_count >= 10: + industry_match = max(industry_match, 0.6) + elif exp_count > 0: + industry_match = max(industry_match, 0.3) + + industry_score = industry_match * 35 + + # 2. 可用性(35%) + current_load = profile["current_projects"] + capacity = profile["workload_capacity"] + + if current_load >= capacity: + availability = 0.1 # 超载 + elif current_load >= capacity * 0.8: + availability = 0.5 # 偏高 + else: + availability = 1.0 - (current_load / capacity) # 线性递减 + + availability_score = availability * 35 + + # 3. 质量评分(20%) + quality_score = profile["quality_score"] * 20 + + # 4. 团队平衡(10%)- 优先分配给负荷较低的 + all_profiles = self.get_all_profiles() + avg_load = sum(p["current_projects"] for p in all_profiles.values()) / len(all_profiles) + + if current_load < avg_load: + balance_score = 10 + elif current_load == avg_load: + balance_score = 7 + else: + balance_score = max(0, 10 - (current_load - avg_load) * 2) + + # 总分 + total_score = industry_score + availability_score + quality_score + balance_score + + return { + "total_score": round(total_score, 2), + "breakdown": { + "industry_match": round(industry_score, 2), + "availability": round(availability_score, 2), + "quality": round(quality_score, 2), + "balance": round(balance_score, 2), + }, + "estimated_days": profile["avg_completion_days"] + current_load // 2, # 考虑负荷影响 + } + + def get_recommended_reviewers( + self, + project_industry: str, + project_complexity: int = 3, + top_n: int = 3 + ) -> List[Dict]: + """获取推荐主审员列表(Top N)""" + all_profiles = self.get_all_profiles() + + recommendations = [] + for reviewer_name in all_profiles: + match_result = self.calculate_match_score( + reviewer_name, + project_industry, + project_complexity + ) + + profile = all_profiles[reviewer_name] + recommendations.append({ + "reviewer": reviewer_name, + "score": match_result["total_score"], + "breakdown": match_result["breakdown"], + "estimated_days": match_result["estimated_days"], + "current_projects": profile["current_projects"], + "specialty": profile["specialty"], + }) + + # 按总分降序排序 + recommendations.sort(key=lambda x: x["score"], reverse=True) + + return recommendations[:top_n] + + +# 全局单例 +_reviewer_store: Optional[ReviewerProfileStore] = None + + +def get_reviewer_store() -> ReviewerProfileStore: + """获取主审员画像存储单例""" + global _reviewer_store + if _reviewer_store is None: + _reviewer_store = ReviewerProfileStore() + return _reviewer_store diff --git a/backend/data_layer/team_experience.py b/backend/data_layer/team_experience.py new file mode 100644 index 0000000..a3e73de --- /dev/null +++ b/backend/data_layer/team_experience.py @@ -0,0 +1,195 @@ +"""团队集体经验库 + +功能: +1. 学习团队历史评审行为(聚合,非个人) +2. 提取高频风险模式 +3. 为预审助手提供经验支持 +""" + +import json +from typing import Dict, List, Optional +from pathlib import Path +from datetime import datetime +import logging + +logger = logging.getLogger(__name__) + + +class TeamExperienceStore: + """团队集体经验存储""" + + def __init__(self, data_dir: str = "./xingbang_data/team_experience"): + self.data_dir = Path(data_dir) + self.data_dir.mkdir(parents=True, exist_ok=True) + self.experience_file = self.data_dir / "team_patterns.json" + + # 初始化默认经验模式 + if not self.experience_file.exists(): + self._init_default_patterns() + + def _init_default_patterns(self): + """初始化默认团队经验模式""" + default_patterns = { + "risk_patterns": { + "制造业": [ + { + "risk_type": "应收账款集中度过高", + "frequency": 23, + "typical_threshold": "单一客户占比 > 30%", + "team_action": "要求补充第二担保人或增加保证金", + }, + { + "risk_type": "原材料价格波动风险", + "frequency": 18, + "typical_threshold": "大宗商品占成本 > 50%", + "team_action": "要求提供价格锁定合同或套期保值证明", + }, + ], + "新能源": [ + { + "risk_type": "政策依赖度高", + "frequency": 34, + "typical_threshold": "补贴收入 > 营收 20%", + "team_action": "降低授信额度或缩短租赁期限", + }, + { + "risk_type": "技术迭代淘汰风险", + "frequency": 21, + "typical_threshold": "设备技术落后 2 代以上", + "team_action": "要求租赁物残值担保或回购承诺", + }, + ], + "交通运输": [ + { + "risk_type": "油价波动影响", + "frequency": 15, + "typical_threshold": "燃油成本 > 营收 40%", + "team_action": "要求提供运价调整机制证明", + }, + ], + }, + "approval_thresholds": { + "资产负债率": { + "safe": 0.6, + "warning": 0.7, + "critical": 0.8, + "team_consensus": "≤60% 低风险,60-70% 中风险需补充说明,>70% 高风险需额外担保", + }, + "流动比率": { + "safe": 1.5, + "warning": 1.2, + "critical": 1.0, + "team_consensus": "≥1.5 优秀,1.2-1.5 合格,<1.2 需关注短期偿债能力", + }, + "净利润增长率": { + "safe": 0.15, + "warning": 0.05, + "critical": 0.0, + "team_consensus": "持续两年负增长通常不予通过", + }, + }, + "industry_red_flags": { + "光伏": ["产能严重过剩", "欧美双反调查", "地方政府欠补"], + "房地产": ["三道红线踩线", "土地储备不足", "回款周期超18个月"], + "餐饮": ["单店模型未跑通", "加盟商纠纷", "食品安全事故"], + }, + "common_rejection_reasons": [ + {"reason": "担保方资产不足", "count": 45, "solution": "要求补充第二担保人"}, + {"reason": "征信查询次数过多", "count": 32, "solution": "要求客户说明查询原因"}, + {"reason": "涉诉金额过大", "count": 28, "solution": "等待诉讼结果或要求提供和解协议"}, + {"reason": "租赁物适格性存疑", "count": 25, "solution": "要求第三方评估报告"}, + ], + "metadata": { + "last_updated": datetime.now().isoformat(), + "total_cases_analyzed": 156, + "data_source": "2024-2025 年历史评审案例", + }, + } + + with open(self.experience_file, "w", encoding="utf-8") as f: + json.dump(default_patterns, f, ensure_ascii=False, indent=2) + + logger.info("团队经验模式初始化完成") + + def get_risk_patterns(self, industry: str = None) -> List[Dict]: + """获取风险模式(可按行业过滤)""" + with open(self.experience_file, "r", encoding="utf-8") as f: + data = json.load(f) + + risk_patterns = data.get("risk_patterns", {}) + + if industry: + # 精确匹配或模糊匹配 + for key in risk_patterns: + if industry in key or key in industry: + return risk_patterns[key] + return [] + else: + # 返回所有模式 + all_patterns = [] + for patterns in risk_patterns.values(): + all_patterns.extend(patterns) + return all_patterns + + def get_approval_thresholds(self) -> Dict: + """获取团队共识的审批阈值""" + with open(self.experience_file, "r", encoding="utf-8") as f: + data = json.load(f) + + return data.get("approval_thresholds", {}) + + def get_industry_red_flags(self, industry: str) -> List[str]: + """获取行业红线(团队总结的高危信号)""" + with open(self.experience_file, "r", encoding="utf-8") as f: + data = json.load(f) + + industry_flags = data.get("industry_red_flags", {}) + + # 模糊匹配 + for key in industry_flags: + if industry in key or key in industry: + return industry_flags[key] + + return [] + + def get_common_rejection_reasons(self, top_n: int = 10) -> List[Dict]: + """获取高频退回原因及解决方案""" + with open(self.experience_file, "r", encoding="utf-8") as f: + data = json.load(f) + + reasons = data.get("common_rejection_reasons", []) + return reasons[:top_n] + + def update_pattern(self, pattern_type: str, pattern_data: Dict): + """更新经验模式(用于持续学习)""" + with open(self.experience_file, "r", encoding="utf-8") as f: + data = json.load(f) + + # 更新指定类型的模式 + if pattern_type in data: + data[pattern_type].update(pattern_data) + data["metadata"]["last_updated"] = datetime.now().isoformat() + + with open(self.experience_file, "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=2) + + logger.info(f"团队经验模式已更新: {pattern_type}") + + def get_metadata(self) -> Dict: + """获取元数据""" + with open(self.experience_file, "r", encoding="utf-8") as f: + data = json.load(f) + + return data.get("metadata", {}) + + +# 全局单例 +_team_experience_store: Optional[TeamExperienceStore] = None + + +def get_team_experience_store() -> TeamExperienceStore: + """获取团队经验存储单例""" + global _team_experience_store + if _team_experience_store is None: + _team_experience_store = TeamExperienceStore() + return _team_experience_store diff --git a/backend/graph/agent.py b/backend/graph/agent.py index 46e8046..45f0813 100644 --- a/backend/graph/agent.py +++ b/backend/graph/agent.py @@ -19,12 +19,17 @@ class AgentManager: self._base_dir: Path | None = None self._tools: list = [] self._llm = None + self._raw_logs_dir: Path | None = None def initialize(self, base_dir: Path) -> None: """Initialize LLM (DeepSeek) and tools. Called once at startup.""" self._base_dir = base_dir self._tools = get_all_tools(base_dir) + # 初始化原始报文日志目录 (base_dir 已是 backend/ 目录) + self._raw_logs_dir = base_dir / "raw_logs" + self._raw_logs_dir.mkdir(exist_ok=True, parents=True) + # Use langchain-deepseek for native DeepSeek support from langchain_deepseek import ChatDeepSeek @@ -70,7 +75,7 @@ class AgentManager: return messages async def astream( - self, message: str, history: list[dict[str, Any]] + self, message: str, history: list[dict[str, Any]], session_id: str = "default" ) -> AsyncGenerator[dict[str, Any], None]: """Stream agent response with token-level and node-level events. @@ -80,6 +85,8 @@ class AgentManager: {"type": "tool_start", "tool": "...", "input": "..."} {"type": "tool_end", "tool": "...", "output": "..."} {"type": "done", "content": "..."} + + Also persists complete message flow to {session}.msgs.jsonl for trajectory. """ # RAG retrieval: inject memory context if enabled rag_mode = get_rag_mode() @@ -101,6 +108,31 @@ class AgentManager: ) rag_context = f"[记忆检索结果]\n{snippets}" + # 为当前 session 创建原始报文回调 + from graph.raw_trace import RawTraceCallback + + raw_callback = RawTraceCallback(session_id, self._raw_logs_dir) + + # 消息流日志文件(记录完整的 human/assistant/tool 消息序列) + import json + import time + msgs_log_file = self._raw_logs_dir / f"{session_id}.msgs.jsonl" + + # 记录 human 消息 + def _append_msg(role: str, content: str, **kwargs): + """追加一条消息到 msgs.jsonl""" + msg_record = { + "role": role, + "content": content, + "timestamp": time.time(), + **kwargs + } + with open(msgs_log_file, "a", encoding="utf-8") as f: + f.write(json.dumps(msg_record, ensure_ascii=False) + "\n") + + _append_msg("human", message) + + # 构建 agent 时传入 callback agent = self._build_agent() # Build messages with optional RAG context appended to history @@ -113,10 +145,13 @@ class AgentManager: full_response = "" tools_just_finished = False + current_assistant_content = "" # 当前 assistant 消息累积 + current_tool_calls = [] # 当前 assistant 的 tool_calls async for event in agent.astream( {"messages": messages}, stream_mode=["messages", "updates"], + config={"callbacks": [raw_callback]}, ): # event is a tuple of (stream_mode, data) when using multiple modes if isinstance(event, tuple): @@ -136,31 +171,65 @@ class AgentManager: yield {"type": "new_response"} tools_just_finished = False full_response += msg.content + current_assistant_content += msg.content yield {"type": "token", "content": msg.content} elif mode == "updates": if isinstance(data, dict): for node_name, node_data in data.items(): - if node_name == "tools" and "messages" in node_data: - for tool_msg in node_data["messages"]: - if hasattr(tool_msg, "name"): - yield { - "type": "tool_end", - "tool": tool_msg.name, - "output": str(tool_msg.content)[:2000], - } - # After all tool results, mark that tools finished - tools_just_finished = True - elif node_name == "model" and "messages" in node_data: + if node_name == "model" and "messages" in node_data: + # 模型节点:记录 assistant 消息和 tool_calls for agent_msg in node_data["messages"]: if hasattr(agent_msg, "tool_calls") and agent_msg.tool_calls: + # 记录 assistant 消息(带 tool_calls) + tool_calls_data = [] for tc in agent_msg.tool_calls: + tool_call_info = { + "name": tc["name"], + "args": tc.get("args", {}) + } + tool_calls_data.append(tool_call_info) + current_tool_calls.append(tool_call_info) yield { "type": "tool_start", "tool": tc["name"], "input": str(tc.get("args", ""))[:1000], } + # 记录带 tool_calls 的 assistant 消息 + _append_msg( + "assistant", + agent_msg.content if hasattr(agent_msg, "content") else "", + tool_calls=tool_calls_data + ) + + elif node_name == "tools" and "messages" in node_data: + # 工具节点:记录每个 ToolMessage 的返回内容 + for tool_msg in node_data["messages"]: + if hasattr(tool_msg, "name"): + tool_output = str(tool_msg.content) + # 记录 tool 消息(完整 content) + _append_msg( + "tool", + tool_output, + name=tool_msg.name, + tool_call_id=getattr(tool_msg, "tool_call_id", None) + ) + yield { + "type": "tool_end", + "tool": tool_msg.name, + "output": tool_output[:2000], # yield 时截断,但完整内容已记录 + } + # After all tool results, mark that tools finished + tools_just_finished = True + + # 记录最终的 assistant 消息(纯回复,无 tool_calls) + if current_assistant_content and not current_tool_calls: + _append_msg("assistant", current_assistant_content) + elif current_assistant_content and current_tool_calls: + # 如果有 tool_calls,记录最后一轮的纯文本回复 + _append_msg("assistant", current_assistant_content) + yield {"type": "done", "content": full_response} async def ainvoke(self, message: str, session_id: str) -> str: diff --git a/backend/graph/memory_indexer.py b/backend/graph/memory_indexer.py index 089d483..483ace8 100644 --- a/backend/graph/memory_indexer.py +++ b/backend/graph/memory_indexer.py @@ -65,6 +65,9 @@ class MemoryIndexer: # 使用 HuggingFace 镜像加速模型下载 if "HF_ENDPOINT" not in os.environ: os.environ["HF_ENDPOINT"] = "https://hf-mirror.com" + # 离线加载已缓存的嵌入模型(避免启动时联网挂起阻塞) + os.environ.setdefault("HF_HUB_OFFLINE", "1") + os.environ.setdefault("TRANSFORMERS_OFFLINE", "1") Settings.embed_model = HuggingFaceEmbedding( model_name=os.getenv("EMBEDDING_MODEL", "BAAI/bge-small-zh"), @@ -112,6 +115,9 @@ class MemoryIndexer: # 使用 HuggingFace 镜像加速模型下载 if "HF_ENDPOINT" not in os.environ: os.environ["HF_ENDPOINT"] = "https://hf-mirror.com" + # 离线加载已缓存的嵌入模型(避免联网挂起阻塞) + os.environ.setdefault("HF_HUB_OFFLINE", "1") + os.environ.setdefault("TRANSFORMERS_OFFLINE", "1") Settings.embed_model = HuggingFaceEmbedding( model_name=os.getenv("EMBEDDING_MODEL", "BAAI/bge-small-zh"), diff --git a/backend/graph/raw_trace.py b/backend/graph/raw_trace.py new file mode 100644 index 0000000..9a61d71 --- /dev/null +++ b/backend/graph/raw_trace.py @@ -0,0 +1,274 @@ +"""原始报文录制 - LangChain 回调处理器,捕获每次 LLM 调用的完整请求/响应""" + +import json +import os +import time +import traceback +import uuid +from pathlib import Path +from typing import Any, Dict, List, Optional + +from langchain_core.callbacks import AsyncCallbackHandler +from langchain_core.messages import BaseMessage +from langchain_core.outputs import LLMResult + + +class RawTraceCallback(AsyncCallbackHandler): + """录制每次 LLM 调用的完整原始请求和响应到 JSONL 文件""" + + def __init__(self, session_id: str, raw_logs_dir: Path): + super().__init__() + self.session_id = session_id + self.raw_logs_dir = raw_logs_dir + self.raw_logs_dir.mkdir(exist_ok=True, parents=True) + + # 当前调用的跟踪信息 (key: run_id, value: trace_data) + self._traces: Dict[str, Dict[str, Any]] = {} + + def _serialize_message(self, msg: BaseMessage) -> Dict[str, Any]: + """将 LangChain 消息序列化为 JSON-safe dict""" + try: + result = { + "type": msg.type, + "content": msg.content if hasattr(msg, "content") else "", + } + # 提取额外字段 + if hasattr(msg, "name"): + result["name"] = msg.name + if hasattr(msg, "tool_calls") and msg.tool_calls: + result["tool_calls"] = msg.tool_calls + if hasattr(msg, "additional_kwargs") and msg.additional_kwargs: + result["additional_kwargs"] = msg.additional_kwargs + return result + except Exception: + return {"type": "unknown", "content": str(msg)} + + async def on_llm_start( + self, + serialized: Dict[str, Any], + prompts: List[str], + *, + run_id: uuid.UUID, + parent_run_id: Optional[uuid.UUID] = None, + tags: Optional[List[str]] = None, + metadata: Optional[Dict[str, Any]] = None, + **kwargs: Any, + ) -> None: + """LLM 调用开始 - 记录请求信息""" + try: + # 提取 messages (如果是 chat model) + messages_raw = kwargs.get("invocation_params", {}).get("messages", []) + messages_serialized = [] + + if messages_raw: + for msg in messages_raw: + if isinstance(msg, BaseMessage): + messages_serialized.append(self._serialize_message(msg)) + elif isinstance(msg, dict): + messages_serialized.append(msg) + else: + messages_serialized.append({"content": str(msg)}) + elif prompts: + # 如果没有 messages,使用 prompts + messages_serialized = [{"type": "human", "content": p} for p in prompts] + + # 提取模型参数 + invocation_params = kwargs.get("invocation_params", {}) + model_name = invocation_params.get("model", "unknown") + temperature = invocation_params.get("temperature", None) + + trace_data = { + "trace_id": str(run_id), + "session_id": self.session_id, + "timestamp_start": time.time(), + "request": { + "model": model_name, + "messages": messages_serialized, + "temperature": temperature, + "invocation_params": { + k: v for k, v in invocation_params.items() + if k not in ["messages", "api_key"] # 过滤敏感信息 + }, + }, + } + + self._traces[str(run_id)] = trace_data + + except Exception: + traceback.print_exc() + + async def on_llm_end( + self, + response: LLMResult, + *, + run_id: uuid.UUID, + parent_run_id: Optional[uuid.UUID] = None, + **kwargs: Any, + ) -> None: + """LLM 调用结束 - 记录响应信息并落盘""" + try: + trace_id = str(run_id) + if trace_id not in self._traces: + return + + trace_data = self._traces[trace_id] + timestamp_end = time.time() + duration_ms = int((timestamp_end - trace_data["timestamp_start"]) * 1000) + + # 提取响应内容 + generations = response.generations[0] if response.generations else [] + response_messages = [] + + # 提取 finish_reason 和 usage (优先从 message.response_metadata,支持流式) + finish_reason = "unknown" + usage_metadata = None + + for gen in generations: + msg_dict = { + "text": gen.text if hasattr(gen, "text") else "", + } + + # 提取 message (AIMessage) + if hasattr(gen, "message") and gen.message: + msg = gen.message + msg_dict["message"] = self._serialize_message(msg) + + # 优先从 message.response_metadata 提取 finish_reason (流式模式) + if hasattr(msg, "response_metadata") and msg.response_metadata: + if "finish_reason" in msg.response_metadata and finish_reason == "unknown": + finish_reason = msg.response_metadata["finish_reason"] + # 提取流式聚合后的 usage_metadata + if "usage_metadata" in msg.response_metadata and not usage_metadata: + usage_metadata = msg.response_metadata["usage_metadata"] + + # 也从 message.usage_metadata 提取(LangChain 新版本) + if hasattr(msg, "usage_metadata") and msg.usage_metadata and not usage_metadata: + usage_metadata = msg.usage_metadata + + # 其次从 generation_info 提取 finish_reason + if finish_reason == "unknown" and hasattr(gen, "generation_info") and gen.generation_info: + if "finish_reason" in gen.generation_info: + finish_reason = gen.generation_info["finish_reason"] + + response_messages.append(msg_dict) + + # 提取 llm_output (包含 usage 等信息) + llm_output = response.llm_output or {} + + trace_data["timestamp_end"] = timestamp_end + trace_data["duration_ms"] = duration_ms + trace_data["response"] = { + "generations": response_messages, + "llm_output": llm_output, + } + + # 提取 token usage:优先使用 usage_metadata(流式),其次 llm_output + if usage_metadata: + # usage_metadata 通常是字典或对象,尝试兼容两种格式 + if isinstance(usage_metadata, dict): + trace_data["usage"] = { + "input_tokens": usage_metadata.get("input_tokens", 0), + "output_tokens": usage_metadata.get("output_tokens", 0), + "total_tokens": usage_metadata.get("total_tokens", 0), + } + else: + # 对象格式(有属性访问) + trace_data["usage"] = { + "input_tokens": getattr(usage_metadata, "input_tokens", 0), + "output_tokens": getattr(usage_metadata, "output_tokens", 0), + "total_tokens": getattr(usage_metadata, "total_tokens", 0), + } + else: + # 回退到 llm_output.token_usage(非流式) + token_usage = llm_output.get("token_usage", {}) + trace_data["usage"] = { + "input_tokens": token_usage.get("prompt_tokens", 0), + "output_tokens": token_usage.get("completion_tokens", 0), + "total_tokens": token_usage.get("total_tokens", 0), + } + + # 最后从 llm_output 兜底 finish_reason(非流式) + if finish_reason == "unknown" and "finish_reason" in llm_output: + finish_reason = llm_output["finish_reason"] + + # 统一字段:finish_reason(向后兼容,保留 stop_reason) + trace_data["finish_reason"] = finish_reason + trace_data["stop_reason"] = finish_reason # 向后兼容旧前端代码 + + # 追加写入 JSONL 文件 + log_file = self.raw_logs_dir / f"{self.session_id}.jsonl" + with open(log_file, "a", encoding="utf-8") as f: + f.write(json.dumps(trace_data, ensure_ascii=False) + "\n") + + # 清理已完成的 trace + del self._traces[trace_id] + + except Exception: + traceback.print_exc() + + async def on_llm_error( + self, + error: Exception, + *, + run_id: uuid.UUID, + parent_run_id: Optional[uuid.UUID] = None, + **kwargs: Any, + ) -> None: + """LLM 调用出错 - 记录错误""" + try: + trace_id = str(run_id) + if trace_id not in self._traces: + return + + trace_data = self._traces[trace_id] + timestamp_end = time.time() + duration_ms = int((timestamp_end - trace_data["timestamp_start"]) * 1000) + + trace_data["timestamp_end"] = timestamp_end + trace_data["duration_ms"] = duration_ms + trace_data["error"] = { + "type": type(error).__name__, + "message": str(error), + } + + # 写入文件 + log_file = self.raw_logs_dir / f"{self.session_id}.jsonl" + with open(log_file, "a", encoding="utf-8") as f: + f.write(json.dumps(trace_data, ensure_ascii=False) + "\n") + + del self._traces[trace_id] + + except Exception: + traceback.print_exc() + + +def load_raw_traces(session_id: str, raw_logs_dir: Path, limit: int = 10) -> List[Dict[str, Any]]: + """读取指定 session 的原始报文记录 + + Args: + session_id: 会话 ID + raw_logs_dir: 原始日志目录 + limit: 返回最近 N 条记录 + + Returns: + 原始报文列表,按时间倒序 + """ + log_file = raw_logs_dir / f"{session_id}.jsonl" + if not log_file.exists(): + return [] + + traces = [] + try: + with open(log_file, "r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if line: + traces.append(json.loads(line)) + + # 按时间倒序,返回最近 limit 条 + traces.sort(key=lambda x: x.get("timestamp_start", 0), reverse=True) + return traces[:limit] + + except Exception: + traceback.print_exc() + return [] diff --git a/backend/kaiju_history_data.py b/backend/kaiju_history_data.py new file mode 100644 index 0000000..b34ae49 --- /dev/null +++ b/backend/kaiju_history_data.py @@ -0,0 +1,317 @@ +"""兴邦金租历史评审数据源(虚拟示例数据,2024-2026年) + +用于智能分配算法的数据驱动决策: +- 主审员产能画像(平均评审天数、行业专长、质量分) +- 历史项目追溯(按主审员/行业查询) +- 多维度分配推荐依据 +""" + +import random +from datetime import datetime, timedelta +from typing import List, Dict, Optional + +# ============ 历史评审项目数据结构 ============ + +class HistoricalProject: + """历史评审项目记录""" + def __init__( + self, + project_id: str, + project_name: str, + industry: str, + credit_amount: float, # 万元 + complexity: int, # 1-5,难度 + planned_days: int, # 计划评审天数 + actual_days: int, # 实际评审天数 + reviewer: str, # 主审员 + outcome: str, # 通过/否决/撤并 + quality_score: int, # 0-100,历史质量分 + review_date: str, # YYYY-MM-DD + ): + self.project_id = project_id + self.project_name = project_name + self.industry = industry + self.credit_amount = credit_amount + self.complexity = complexity + self.planned_days = planned_days + self.actual_days = actual_days + self.reviewer = reviewer + self.outcome = outcome + self.quality_score = quality_score + self.review_date = review_date + + def to_dict(self) -> dict: + return { + "project_id": self.project_id, + "project_name": self.project_name, + "industry": self.industry, + "credit_amount": self.credit_amount, + "complexity": self.complexity, + "planned_days": self.planned_days, + "actual_days": self.actual_days, + "reviewer": self.reviewer, + "outcome": self.outcome, + "quality_score": self.quality_score, + "review_date": self.review_date, + } + + +# ============ 虚拟数据生成(体现真实规律)============ + +def _generate_historical_data() -> List[HistoricalProject]: + """生成2024-2026年虚拟历史评审数据 + + 数据规律设计: + - 李明:制造业专长,效率高(实际天数<计划),质量分85-95 + - 王芳:新能源专长,质量高(90-98),但项目复杂度高时需时较长 + - 张强:交通运输专长,负荷较重(项目多),质量稳定80-90 + - 刘洋:医疗健康专长,细致但速度慢(实际天数略超计划),质量92-98 + - 陈晨:设备租赁专长,全行业覆盖,效率中等,质量78-88 + - 赵磊:环保行业专长,项目少但质量高,88-96 + """ + + projects = [] + project_counter = 1 + + # 主审员配置:(姓名, 主行业, 次要行业, 项目数, 效率倍数, 质量基数, 质量波动) + reviewers_config = [ + ("李明", "制造业", ["装备制造", "机械设备", "汽车零部件"], 22, 0.85, 88, 7), + ("王芳", "新能源", ["光伏设备", "风电设备", "新能源汽车"], 20, 1.05, 92, 6), + ("张强", "交通运输", ["物流运输", "港口码头", "公路客运"], 25, 0.95, 84, 8), + ("刘洋", "医疗健康", ["医疗器械", "医药制造", "医院设备"], 18, 1.15, 94, 4), + ("陈晨", "设备租赁", ["工程机械", "办公设备", "生产设备"], 20, 1.0, 82, 8), + ("赵磊", "环保", ["环保设备", "污水处理", "固废处理"], 16, 1.08, 90, 6), + ] + + # 行业项目名称模板 + industry_templates = { + "制造业": ["精密机械加工设备", "自动化生产线", "数控机床", "模具制造设备"], + "装备制造": ["重型装备", "专用设备", "成套装备"], + "机械设备": ["工业机器人", "包装机械", "印刷设备"], + "汽车零部件": ["汽车配件生产线", "发动机零部件", "车身冲压设备"], + "新能源": ["分布式光伏电站", "储能设备", "充电桩建设"], + "光伏设备": ["光伏组件生产线", "硅片切割设备", "光伏发电项目"], + "风电设备": ["风力发电机组", "风电场建设", "海上风电项目"], + "新能源汽车": ["新能源客车", "电动物流车", "充电设施"], + "交通运输": ["货运车队", "冷链物流车辆", "运输设备更新"], + "物流运输": ["智能仓储设备", "配送车辆", "物流信息系统"], + "港口码头": ["港口装卸设备", "集装箱堆场", "码头机械"], + "公路客运": ["客运班车", "旅游大巴", "公交车辆"], + "医疗健康": ["医学影像设备", "手术器械", "检验检测设备"], + "医疗器械": ["CT设备", "超声设备", "监护仪"], + "医药制造": ["制药生产线", "GMP车间改造", "原料药设备"], + "医院设备": ["病床护理设备", "消毒灭菌设备", "医用气体系统"], + "设备租赁": ["工程机械租赁", "办公设备租赁", "生产设备租赁"], + "工程机械": ["挖掘机", "起重机", "推土机"], + "办公设备": ["复印打印设备", "办公家具", "会议系统"], + "生产设备": ["注塑机", "冲压设备", "焊接设备"], + "环保": ["环保治理设备", "节能改造项目", "清洁能源设备"], + "环保设备": ["废气处理设备", "除尘设备", "脱硫脱硝装置"], + "污水处理": ["污水处理站", "中水回用系统", "污泥处理设备"], + "固废处理": ["垃圾焚烧设备", "危废处理设施", "资源回收系统"], + } + + # 生成每位主审员的历史项目 + start_date = datetime(2024, 1, 1) + end_date = datetime(2026, 8, 31) + + for reviewer_name, main_industry, sub_industries, num_projects, efficiency, quality_base, quality_range in reviewers_config: + + # 该主审员的行业池(主行业占70%,次要行业占30%) + industries_pool = [main_industry] * 7 + sub_industries * 3 + + for i in range(num_projects): + # 随机日期(制造业、新能源集中在2-6月、9-11月) + if main_industry in ["制造业", "新能源", "装备制造"]: + # 集中在业务旺季 + month_choices = [2, 3, 4, 5, 6, 9, 10, 11] + month = random.choice(month_choices) + else: + month = random.randint(1, 12) + + year = random.choice([2024, 2025, 2026]) + if year == 2026 and month > 8: + month = random.randint(1, 8) + + day = random.randint(1, 28) + review_date = datetime(year, month, day) + + # 行业选择(主行业为主) + industry = random.choice(industries_pool) + + # 项目名称 + company_prefix = random.choice([ + "阳光", "华润", "中联", "鑫盛", "宏泰", "瑞丰", "恒通", "盛达", + "金辉", "博瑞", "创新", "天成", "嘉诚", "永安", "兴旺", "腾飞" + ]) + equipment_name = random.choice(industry_templates.get(industry, ["设备租赁项目"])) + project_name = f"{company_prefix}{equipment_name}融资租赁项目" + + # 授信金额(万元):复杂度越高金额越大 + complexity = random.choices([1, 2, 3, 4, 5], weights=[5, 15, 40, 30, 10])[0] + + if complexity == 1: + credit_amount = random.randint(500, 1500) + elif complexity == 2: + credit_amount = random.randint(1200, 3000) + elif complexity == 3: + credit_amount = random.randint(2500, 6000) + elif complexity == 4: + credit_amount = random.randint(5000, 10000) + else: # 5 + credit_amount = random.randint(8000, 20000) + + # 计划评审天数(复杂度决定) + planned_days = {1: 5, 2: 7, 3: 10, 4: 12, 5: 15}[complexity] + + # 实际评审天数(受主审员效率影响) + actual_days = int(planned_days * efficiency * random.uniform(0.9, 1.1)) + actual_days = max(3, actual_days) # 最少3天 + + # 质量分(受主审员能力+复杂度影响) + # 复杂度高的项目,质量分略有波动 + quality_penalty = (complexity - 3) * 2 if complexity >= 4 else 0 + quality_score = quality_base + random.randint(-quality_range, quality_range) - quality_penalty + quality_score = max(60, min(100, quality_score)) + + # 评审结果(质量分高=通过率高) + if quality_score >= 85: + outcome = random.choices(["通过", "否决", "撤并"], weights=[90, 5, 5])[0] + elif quality_score >= 75: + outcome = random.choices(["通过", "否决", "撤并"], weights=[80, 12, 8])[0] + else: + outcome = random.choices(["通过", "否决", "撤并"], weights=[60, 25, 15])[0] + + # 如果返工(实际天数超出计划较多),质量分降低 + if actual_days > planned_days * 1.3: + quality_score = max(quality_score - 10, 60) + + project = HistoricalProject( + project_id=f"PROJ-{year}-{project_counter:04d}", + project_name=project_name, + industry=industry, + credit_amount=credit_amount, + complexity=complexity, + planned_days=planned_days, + actual_days=actual_days, + reviewer=reviewer_name, + outcome=outcome, + quality_score=quality_score, + review_date=review_date.strftime("%Y-%m-%d"), + ) + + projects.append(project) + project_counter += 1 + + # 按日期排序 + projects.sort(key=lambda p: p.review_date) + + return projects + + +# ============ 全局数据缓存 ============ + +_HISTORICAL_PROJECTS: List[HistoricalProject] = _generate_historical_data() + + +# ============ 查询接口 ============ + +def get_all_projects() -> List[Dict]: + """获取所有历史项目""" + return [p.to_dict() for p in _HISTORICAL_PROJECTS] + + +def get_projects_by_reviewer(reviewer: str) -> List[Dict]: + """按主审员查询历史项目""" + filtered = [p for p in _HISTORICAL_PROJECTS if p.reviewer == reviewer] + return [p.to_dict() for p in filtered] + + +def get_projects_by_industry(industry: str) -> List[Dict]: + """按行业查询历史项目(模糊匹配)""" + filtered = [p for p in _HISTORICAL_PROJECTS if industry in p.industry] + return [p.to_dict() for p in filtered] + + +def get_projects_filtered(reviewer: Optional[str] = None, industry: Optional[str] = None) -> List[Dict]: + """组合过滤查询""" + filtered = _HISTORICAL_PROJECTS + + if reviewer: + filtered = [p for p in filtered if p.reviewer == reviewer] + + if industry: + filtered = [p for p in filtered if industry in p.industry] + + return [p.to_dict() for p in filtered] + + +def get_reviewer_stats() -> Dict[str, Dict]: + """获取所有主审员的统计画像 + + 返回格式: + { + "李明": { + "total_projects": 22, + "avg_actual_days": 8.5, + "avg_quality_score": 88.2, + "industry_expertise": { + "制造业": {"count": 15, "avg_quality": 90.1}, + "装备制造": {"count": 7, "avg_quality": 86.3} + }, + "pass_rate": 0.91 + }, + ... + } + """ + stats = {} + + reviewers = list(set(p.reviewer for p in _HISTORICAL_PROJECTS)) + + for reviewer in reviewers: + projects = [p for p in _HISTORICAL_PROJECTS if p.reviewer == reviewer] + + # 基础统计 + total_projects = len(projects) + avg_actual_days = sum(p.actual_days for p in projects) / total_projects if total_projects > 0 else 0 + avg_quality = sum(p.quality_score for p in projects) / total_projects if total_projects > 0 else 0 + pass_count = len([p for p in projects if p.outcome == "通过"]) + pass_rate = pass_count / total_projects if total_projects > 0 else 0 + + # 行业专长统计 + industry_expertise = {} + for project in projects: + industry = project.industry + if industry not in industry_expertise: + industry_expertise[industry] = {"count": 0, "quality_sum": 0} + industry_expertise[industry]["count"] += 1 + industry_expertise[industry]["quality_sum"] += project.quality_score + + # 计算每个行业的平均质量分 + for industry in industry_expertise: + count = industry_expertise[industry]["count"] + quality_sum = industry_expertise[industry]["quality_sum"] + industry_expertise[industry] = { + "count": count, + "avg_quality": quality_sum / count if count > 0 else 0 + } + + stats[reviewer] = { + "total_projects": total_projects, + "avg_actual_days": round(avg_actual_days, 1), + "avg_quality_score": round(avg_quality, 1), + "industry_expertise": industry_expertise, + "pass_rate": round(pass_rate, 2) + } + + return stats + + +# ============ 初始化时输出统计信息 ============ + +if __name__ == "__main__": + print(f"✅ 已生成 {len(_HISTORICAL_PROJECTS)} 条历史评审记录") + print("\n主审员统计:") + stats = get_reviewer_stats() + for reviewer, data in stats.items(): + print(f" {reviewer}: {data['total_projects']}个项目, 平均{data['avg_actual_days']}天, 质量{data['avg_quality_score']}分") diff --git a/backend/start_backend.bat b/backend/start_backend.bat new file mode 100644 index 0000000..4df4641 --- /dev/null +++ b/backend/start_backend.bat @@ -0,0 +1,18 @@ +@echo off +REM Mini-OpenClaw Backend Startup Script (Windows) +REM 自动禁用代理并启动后端 + +cd /d "%~dp0" + +REM 禁用所有代理设置 +set NO_PROXY=* +set HTTP_PROXY= +set HTTPS_PROXY= +set ALL_PROXY= + +REM 激活虚拟环境 +call venv\Scripts\activate.bat + +REM 启动后端 +echo 🚀 Starting mini-OpenClaw backend (proxy disabled)... +python app.py diff --git a/backend/start_backend.sh b/backend/start_backend.sh new file mode 100644 index 0000000..7ed0a3a --- /dev/null +++ b/backend/start_backend.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Mini-OpenClaw Backend Startup Script +# 自动禁用代理并启动后端 + +cd "$(dirname "$0")" + +# 禁用所有代理设置 +export NO_PROXY="*" +export no_proxy="*" +unset HTTP_PROXY +unset HTTPS_PROXY +unset http_proxy +unset https_proxy +unset ALL_PROXY +unset all_proxy + +# 激活虚拟环境 +source venv/Scripts/activate + +# 启动后端 +echo "🚀 Starting mini-OpenClaw backend (proxy disabled)..." +python app.py diff --git a/backend/xingbang/__init__.py b/backend/xingbang/__init__.py deleted file mode 100644 index 9734fa1..0000000 --- a/backend/xingbang/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -"""兴邦金租 AI 评审平台 — 基于 mini-OpenClaw 的项目模块。 - -定位: - 在 mini-OpenClaw 单智能体平台上,扩展出一个可交付的兴邦金租 - AI 评审流水线:五智能体 + 自进化(evolution)模块。挂在 - mini-OpenClaw 后端,复用其 LLM、工具、会话基础设施。 - -五智能体流水线(对应兴邦三档分级审批): - 1. 数据汇聚官 (data_curator) — 多源异构数据→结构化客户情报卡 - 2. 预审参谋 (risk_scout) — 基于情报卡做尽调初稿+风险分级 - 3. 经验检索 (case_finder) — 历史案例+制度规则 RAG 检索 - 4. 审批顾问 (approval_advisor) — 汇总前序产出→三档分级审批参考 - 5. 风险哨兵 (risk_monitor) — 审批前动态复核,风控信号转变预警 - -自进化(evolution)模块: - 采集每次审批流水线的完整轨迹(trajectory),计算奖励(reward), - 从历史中归纳高效工具调用模式,反哺各 agent 的 system prompt。 -""" - -from .workflow import XingbangWorkflow -from .config import XINGBANG_CONFIG - -__all__ = ["XingbangWorkflow", "XINGBANG_CONFIG"] diff --git a/backend/xingbang/agents.py b/backend/xingbang/agents.py deleted file mode 100644 index fd24f5e..0000000 --- a/backend/xingbang/agents.py +++ /dev/null @@ -1,186 +0,0 @@ -"""五智能体定义 — 复用 mini-OpenClaw 的 LLM 与工具,各自带差异化 system prompt。 - -每个智能体 = 一个独立的 LangChain create_agent: - - 共享底层 LLM(DeepSeek) 与读文件工具 - - 差异化的 system prompt(角色、规则、输出约定) - - 专用工具: read_xingbang_data(读兴邦数据包)、read_output(读上游产出) - - 属于同一个全局 AgentManager 编排, 但各 agent 独立 invoke - -轨迹采集: 每个 agent 的每次调用(输入/system/prompt/工具序列/输出)都会被 -evidence 回调记录, 供 evolution 模块使用。 -""" - -from __future__ import annotations - -import json -from pathlib import Path -from typing import Any, Callable - -from langchain.agents import create_agent -from langchain_core.messages import HumanMessage, AIMessage -from langchain_core.tools import tool - -from config import get_rag_mode -from graph.prompt_builder import build_system_prompt -from xingbang.config import agent_specs, XINGBANG_DATA_DIR, DATA_FILES - - -# ── 专用工具:读取兴邦多源数据包 ──────────────────────── -@tool -def read_xingbang_data(filename: str) -> str: - """读取兴邦金租客户的多源数据文件(JSON/CSV/MD/TXT),返回内容。 - - 可用文件名(需带编号前缀): 01_客户申请信息.json, 02_信用评分卡.json, - 03_风险评级报告.md, 04_授信额度测算.csv, 05_征信记录.json, - 06_司法执行记录.json, 07_舆情风险评分.json, 08_反欺诈模型输出.json, - 09_财务预警模型.csv, 10_财务分析报告.csv, 11_租赁物估值报告.md, - 12_行业调研报告.md, 13_历史审批案例库.json, 14_公司制度要点.md, - 15_银行流水扫描件.txt - """ - if not XINGBANG_DATA_DIR.exists(): - return f"[错误] 兴邦数据目录不存在: {XINGBANG_DATA_DIR}" - path = XINGBANG_DATA_DIR / filename - if not path.exists(): - return f"[错误] 文件不存在: {filename}" - content = path.read_text(encoding="utf-8", errors="ignore") - # 限制长度避免超上下文 - if len(content) > 4000: - content = content[:4000] + "\n...[已截断]" - return content - - -@tool -def read_output(filename: str, base_dir: str = "") -> str: - """读取当前兴邦工作区中上游智能体产出的 Markdown 文件(如 - customer_intel.md, due_diligence.md, case_reference.md)。""" - file_path = Path(base_dir) / filename if base_dir else Path(filename) - if not file_path.exists(): - return f"[错误] 上游产出文件不存在: {filename}" - content = file_path.read_text(encoding="utf-8", errors="ignore") - if len(content) > 8000: - content = content[:8000] + "\n...[已截断]" - return content - - -# ── 单个评审智能体 ───────────────────────────────────── -class XingbangAgent: - """一个兴邦评审智能体: 差异化角色 + 复用 LLM + 工具。""" - - def __init__( - self, - spec, - llm=None, - base_dir: Path | None = None, - output_dir: Path | None = None, - on_evidence: Callable[[str, dict], None] | None = None, - ): - self.spec = spec - self._llm = llm - self._base_dir = base_dir - self._output_dir = output_dir or (base_dir / "xingbang_output" if base_dir else None) - self._on_evidence = on_evidence - self._agent = None - - def _ensure_agent(self): - """构建带专属 system prompt 的独立 agent(惰性)。""" - if self._agent is not None: - return self._agent - tools = [read_xingbang_data] - tools.append(read_output) - prompt = self.spec.system_prompt - # 追加读文件工具说明(需要 base_dir 才能读产出) - if self._base_dir: - prompt += ( - f"\n\n[工作目录] 上游产出文件位于 {self._base_dir}。" - "读取上游产出时使用 read_output 工具并把 filename 设为产出文件名。" - ) - self._agent = create_agent(model=self._llm, tools=tools, system_prompt=prompt) - return self._agent - - def _emit(self, kind: str, data: dict): - if self._on_evidence: - self._on_evidence(kind, data) - - async def run(self, task: str, context: str = "") -> dict: - """执行该智能体任务,返回 {'output': 结果文本, 'file': 保存路径}。""" - self._emit("agent_start", {"agent": self.spec.key, "name": self.spec.name}) - agent = self._ensure_agent() - messages = [] - if context: - messages.append(HumanMessage(content=f"[上游上下文]\n{context}")) - messages.append(HumanMessage(content=task)) - self._emit("prompt", { - "agent": self.spec.key, - "system": self.spec.system_prompt[:500], - "task": task[:300], - }) - import asyncio - max_retries = 4 - backoff = 5 # 初始等待秒 - last_err = None - for attempt in range(max_retries): - try: - result = await agent.ainvoke({"messages": messages}) - final = self._extract_text(result) - break - except Exception as e: # noqa - last_err = e - msg = str(e) - # 对瞬时网络/网关错误重试: 503过载/502坏网关/429限流/连接中断/不完整响应 - retriable = any(k in msg for k in ( - "503", "502", "overloaded", "429", "rate", - "peer closed", "incomplete chunked", "Bad Gateway", - "timeout", "timed out", "Connection", "connection", - "ReadTimeout", "EOF", - )) - if retriable and attempt < max_retries - 1: - await asyncio.sleep(backoff * (2 ** attempt)) - continue - else: - raise # 非瞬时错误, 或重试耗尽 - else: - raise RuntimeError(f"网关过载重试后仍失败: {last_err}") - # 保存产出到文件 - file_path = None - if self._output_dir: - self._output_dir.mkdir(parents=True, exist_ok=True) - file_path = self._output_dir / self.spec.output_file - file_path.write_text(final, encoding="utf-8") - self._emit("agent_end", { - "agent": self.spec.key, - "output_file": str(file_path) if file_path else None, - "output_preview": final[:300], - }) - return {"output": final, "file": file_path} - - @staticmethod - def _extract_text(result: dict) -> str: - messages = result.get("messages", []) - text_parts = [] - for m in messages: - if hasattr(m, "content") and m.type == "ai" and m.content: - text_parts.append(m.content) - if text_parts: - return "\n\n".join(text_parts).strip() - content = result.get("output", "") - return content if content else "[智能体未生成有效输出]" - - -def build_agents( - llm, - base_dir: Path, - output_dir: Path | None = None, - on_evidence: Callable[[str, dict], None] | None = None, - keys: list[str] | None = None, -) -> dict[str, XingbangAgent]: - """按流水线顺序构建五智能体实例。""" - specs = agent_specs() - order = keys or ["data_curator", "risk_scout", "case_finder", - "approval_advisor", "risk_monitor"] - return { - key: XingbangAgent( - spec=specs[key], llm=llm, - base_dir=base_dir, output_dir=output_dir, on_evidence=on_evidence, - ) - for key in order - } diff --git a/backend/xingbang/config.py b/backend/xingbang/config.py deleted file mode 100644 index 0a022cc..0000000 --- a/backend/xingbang/config.py +++ /dev/null @@ -1,173 +0,0 @@ -"""兴邦金租项目配置 — 数据源路径、五智能体定义、分级审批档位。 - -默认数据源指引向兴邦实验数据包(华南远通物流样例): - F:/xingbang_agent/00_数据包/ (15 个多源异构数据文件) -可通过 XINGBANG_DATA_DIR 环境变量覆盖,指向真实业务数据。 - -分级审批档位(对齐兴邦三档分级审批制度): - 常规档 / 中间档 / 严肃档 -""" - -from __future__ import annotations - -import os -from dataclasses import dataclass, field -from pathlib import Path - -# ── 数据源 ────────────────────────────────────────────── -# 默认指向兴邦实验数据包,可用环境变量 XINGBANG_DATA_DIR 覆盖 -DEFAULT_DATA_DIR = Path("F:/xingbang_agent/00_数据包") -XINGBANG_DATA_DIR = Path( - os.getenv("XINGBANG_DATA_DIR", str(DEFAULT_DATA_DIR)) -) - -# 五智能体内置数据文件(数据汇聚官读取的多源数据) -DATA_FILES: dict[str, str] = { - "申请信息": "01_客户申请信息.json", - "信用评分卡": "02_信用评分卡.json", - "风险评级": "03_风险评级报告.md", - "授信额度": "04_授信额度测算.csv", - "征信记录": "05_征信记录.json", - "司法执行": "06_司法执行记录.json", - "舆情评分": "07_舆情风险评分.json", - "反欺诈": "08_反欺诈模型输出.json", - "财务预警": "09_财务预警模型.csv", - "财报": "10_财务分析报告.csv", - "租赁物估值": "11_租赁物估值报告.md", - "行业调研": "12_行业调研报告.md", - "历史案例库": "13_历史审批案例库.json", - "公司制度": "14_公司制度要点.md", - "银行流水": "15_银行流水扫描件.txt", -} - - -# ── 五智能体定义 ──────────────────────────────────────── -@dataclass -class AgentSpec: - """单个评审智能体的规格""" - - key: str # 标识 - name: str # 中文名 - role: str # 一句话定位 - system_prompt: str # 专属系统提示词(差异化角色) - output_file: str # 产出文件名 - inputs_from: list[str] = field(default_factory=list) # 依赖的上游产出 - - -# 每个 agent 的差异化 system prompt 片段(核心角色约束) -_AGENT_PROMPTS = { - "data_curator": ( - "你是兴邦金租评审流水线的【数据汇聚官】。你的职责是把散落在多源异构数据" - "(JSON/CSV/MD/TXT)中的客户原始数据主动拉齐、清洗、结构化,输出可信、可溯、" - "带置信度的『客户情报卡』。\n" - "规则:\n" - "1. 解析多源数据,统一格式,按客户四维画像(工商/信用司法/舆情/财务/租赁物)归并\n" - "2. 多源冲突(如工商股东vs舆情实控人)要显式标出冲突点,绝不擅自取其一\n" - "3. 风险信号按🔴高/🟡中/🟢低三档分级,每条必须附原始数据依据\n" - "4. 明确列出『未覆盖数据源清单』,绝不编造缺失信息\n" - "5. 输出存为 Markdown 文件。数据不出域,本地处理。" - ), - "risk_scout": ( - "你是兴邦金租评审流水线的【预审参谋】。你的职责是基于上游客户情报卡," - "产出尽调报告初稿,把风险点做深度分析。\n" - "规则:\n" - "1. 读取上游『客户情报卡』(customer_intel.md)\n" - "2. 对每个风险点做穿透分析:现金流失控、对外担保、资产负债率、涉诉风险、租赁物残值\n" - "3. 正面因素(合同稳定性、增信措施)也要列出\n" - "4. 引用上游的历史案例检索结果(如有)\n" - "5. 输出『尽调报告初稿』,风险按高/中/低分级,每条可溯源" - ), - "case_finder": ( - "你是兴邦金租评审流水线的【经验检索】。你的职责是把沉淀在历史案例和制度规则" - "库中的经验变成可调用的组织知识,回答『以前类似项目怎么批的』。\n" - "规则:\n" - "1. 检索历史案例库(13_历史审批案例库.json)与当前客户最相关的案例\n" - "2. 每个结论必须带案例编号(如H-1103),可追溯\n" - "3. 正面案例和否决/预警案例都要给,辅助全面判断\n" - "4. 结合制度要点(14_公司制度要点.md)给出适用的档位条款\n" - "5. 找不到完全匹配案例时,明确说明差异点,不硬套" - ), - "approval_advisor": ( - "你是兴邦金租评审流水线的【审批顾问】。你的职责是汇总前面所有智能体产出," - "按三档分级审批制度输出标准化审批参考意见。\n" - "规则:\n" - "1. 读取上游全部产出(情报卡/尽调初稿/历史案例参考)\n" - "2. 按三档制度判定本申请归入:常规档/中间档/严肃档(含判定依据,对照制度条款)\n" - "3. 输出核心风险点(引用溯源)、历史案例支撑(带编号)\n" - "4. 预填流转意见(『建议通过,理由如下,请确认』),人只确认/驳回\n" - "5. 明确标注『本意见为AI参考,最终决策由评审人员确认』\n" - "6. 绝不替代决策,全程留痕可回退" - ), - "risk_monitor": ( - "你是兴邦金租评审流水线的【风险哨兵】。你的职责是审批前的最后一道动态复核," - "检查关键风控信号是否在评审期间发生变化。\n" - "规则:\n" - "1. 重新读取最新的风控数据(02信用评分/05征信/06司法/07舆情/09财务预警)\n" - "2. 与预审阶段采用的数据对比,检测评分/司法/舆情等关键信号是否转变\n" - "3. 若发现信号转变(如新增执行、评分骤降),必须主动提示『风控结果已转变," - "原预审结论需重新评估』,并给出新档位判定\n" - "4. 若无变化,确认『风控状态稳定,可按原档位推进』\n" - "5. 输出最终风控复核结论" - ), -} - - -def agent_specs() -> dict[str, AgentSpec]: - """返回五个智能体的规格定义(流水线顺序)。""" - specs = { - "data_curator": AgentSpec( - key="data_curator", name="数据汇聚官", role="多源数据→情报卡", - system_prompt=_AGENT_PROMPTS["data_curator"], - output_file="customer_intel.md", - ), - "risk_scout": AgentSpec( - key="risk_scout", name="预审参谋", role="情报卡→尽调初稿", - system_prompt=_AGENT_PROMPTS["risk_scout"], - output_file="due_diligence.md", inputs_from=["customer_intel.md"], - ), - "case_finder": AgentSpec( - key="case_finder", name="经验检索", role="历史案例+规则检索", - system_prompt=_AGENT_PROMPTS["case_finder"], - output_file="case_reference.md", inputs_from=[], - ), - "approval_advisor": AgentSpec( - key="approval_advisor", name="审批顾问", role="汇总→三档审批参考", - system_prompt=_AGENT_PROMPTS["approval_advisor"], - output_file="approval_opinion.md", - inputs_from=["customer_intel.md", "due_diligence.md", "case_reference.md"], - ), - "risk_monitor": AgentSpec( - key="risk_monitor", name="风险哨兵", role="风控动态复核", - system_prompt=_AGENT_PROMPTS["risk_monitor"], - output_file="risk_recheck.md", - inputs_from=["approval_opinion.md"], - ), - } - # 编排顺序:数据汇聚→预审→(经验并列)→审批→风控 - return specs - - -# ── 分级审批档位阈值(对齐 14_公司制度要点.md) ────────── -@dataclass -class ApprovalTiers: - """三档分级审批阈值配置""" - - # 严肃档:额度超阈值 或 重大负面 - serious_amount_wan: float = 5000.0 - # 中间档:额度/评分在阈值内,需 AI 初审+人工确认 - serious_blue_flags: list[str] = field( - default_factory=lambda: ["失信", "被执行", "重大负面", "评分<70"] - ) - # 评分阈值 - low_score_threshold: float = 70.0 - - -XINGBANG_CONFIG: dict = { - "project": "兴邦金租 AI 评审平台", - "version": "0.1.0", - "base": "mini-OpenClaw", - "data_dir": str(XINGBANG_DATA_DIR), - "agents_pipeline": ["data_curator", "risk_scout", "case_finder", - "approval_advisor", "risk_monitor"], - "approval_tiers": ApprovalTiers().__dict__, -} diff --git a/backend/xingbang/evolution/__init__.py b/backend/xingbang/evolution/__init__.py deleted file mode 100644 index 990ce01..0000000 --- a/backend/xingbang/evolution/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -"""自进化(evolution)模块 — 兴邦金租评审平台的学习与优化子系统。 - -核心思想: 每次审批流水线执行, 采集五智能体的完整轨迹 -(state/action/observation/reward), 从历史轨迹中归纳成功模式, -反哺各智能体的 system prompt, 实现"越用越准"的自进化。 - -分级方案(诚实设计): - - Level 1 轻量(现在能做): 轨迹采集 + 内在奖励 + 失败/成功模式归纳 + prompt 优化 - - Level 2: 用户显式反馈(👍/👎)作为奖励信号(需前端配合) - - Level 3(未来): 专家标注 / LoRA 微调(需 GPU) -本模块实现 Level 1, 接口预留 Level 2/3。 -""" - -from .trajectory import TrajectoryRecorder -from .reward import compute_reward_v1 -from .optimizer import EvolutionEngine - -__all__ = ["TrajectoryRecorder", "compute_reward_v1", "EvolutionEngine"] diff --git a/backend/xingbang/evolution/optimizer.py b/backend/xingbang/evolution/optimizer.py deleted file mode 100644 index 06ade33..0000000 --- a/backend/xingbang/evolution/optimizer.py +++ /dev/null @@ -1,161 +0,0 @@ -"""Evolution Engine — 从历史轨迹中学习, 反哺各智能体 prompt, 实现自进化。 - -闭环(自进化): - 轨迹采集 → 奖励打分 → 归纳成功/失败模式 → 生成进化建议 → - 反哺/追加到各 agent 的系统提示词(可选启用) - -Level 1 实现: - - analyze: 从已保存轨迹中总结高分/低分模式(常见工具序列、步骤分布) - - generate_insights: 生成可读的进化洞察(markdown) - - evolve_prompt: 将洞察追加到对应 agent 的 system prompt(可选) - -轻量自进化的核心: 不编造"强化学习训练", 而是做"基于经验的 prompt 增强" -——把历史中高奖励轨迹用过的工具调用序列、成功策略, 有选择地注入 -未来 agent 的提示词, 让系统越用越聪明。 -""" - -from __future__ import annotations - -import json -from collections import Counter -from pathlib import Path -from typing import Any - -from .reward import compute_reward_v1 -from .trajectory import TrajectoryRecorder - - -class EvolutionEngine: - """自进化引擎(Level 1 轻量版)。""" - - def __init__( - self, - base_dir: Path, - recorder: TrajectoryRecorder, - enabled: bool = True, - ): - self._base_dir = Path(base_dir) - self._recorder = recorder - self.enabled = enabled - self._insights_dir = self._base_dir / "xingbang_evolution" / "insights" - self._insights_dir.mkdir(parents=True, exist_ok=True) - - # ── 分析历史轨迹 ──────────────────────────────────── - def _load_all_trajectories(self) -> list[dict[str, Any]]: - trajs = [] - for p in self._recorder.list_trajectories(): - try: - trajs.append(json.loads(p.read_text(encoding="utf-8"))) - except Exception: - continue - return trajs - - def analyze(self) -> dict[str, Any]: - """分析全部历史轨迹, 返回统计与模式。""" - trajectories = self._load_all_trajectories() - if not trajectories: - return {"count": 0, "message": "尚无轨迹样本,先积累数据"} - - # 计算每条奖励 - scored = [] - for t in trajectories: - r = compute_reward_v1(t) - t["_reward"] = r - scored.append(t) - - scored.sort(key=lambda x: x["_reward"], reverse=True) - high = scored[: max(1, len(scored) // 4)] # 前 25% - low = scored[-max(1, len(scored) // 4):] # 后 25% - - # 归纳高频工具序列 - def agent_tool_sequences(trajs): - counter = Counter() - for t in trajs: - for s in t.get("steps", []): - seq = tuple(s.get("tools", [])) - if seq: - counter[seq] += 1 - return counter.most_common(5) - - high_patterns = { - "seqs": agent_tool_sequences(high), - "avg_steps": sum(len(t["steps"]) for t in high) / max(1, len(high)), - "success_rate": sum(1 for t in high if t["final_result"].get("success")) - / max(1, len(high)), - } - return { - "count": len(trajectories), - "avg_reward": round(sum(t["_reward"] for t in scored) / len(scored), 2), - "high": {"samples": len(high), "patterns": high_patterns}, - "low": { - "samples": len(low), - "avg_steps": sum(len(t["steps"]) for t in low) / max(1, len(low)), - }, - "top_agents_used": self._agent_usage(scored), - } - - def _agent_usage(self, trajs) -> dict: - counter = Counter() - for t in trajs: - for s in t.get("steps", []): - counter[s.get("agent", "?")] += 1 - return dict(counter.most_common()) - - # ── 生成进化洞察 ──────────────────────────────────── - def generate_insights(self) -> str: - """生成可读的 markdown 进化洞察报告。""" - analysis = self.analyze() - if analysis.get("count", 0) == 0: - return "# 自进化洞察\n\n尚无轨迹样本。运行审批流水线积累数据后,系统将自动进化。" - lines = [ - f"# 兴邦 AI 评审·自进化洞察", - f"\n**样本数**: {analysis['count']} 条轨迹 | **平均奖励**: {analysis['avg_reward']}", - "", - "## ✅ 高分轨迹模式 (前 25%)", - ] - hp = analysis["high"] - lines.append(f"- 平均步骤数: {hp['patterns']['avg_steps']:.1f}") - lines.append(f"- 成功率: {hp['patterns']['success_rate']*100:.0f}%") - lines.append("- 高频工具序列:") - for seq, n in hp["patterns"]["seqs"]: - lines.append(f" - {' → '.join(seq)} (出现 {n} 次)") - lines += ["", "## ⚠️ 低分轨迹 (后 25%)", f"- 平均步骤数: {analysis['low']['avg_steps']:.1f}"] - lines += ["", "## 🧠 智能体使用分布", ""] - for agent, count in analysis.get("top_agents_used", {}).items(): - lines.append(f"- {agent}: {count} 次") - lines += [ - "", - "## 进化建议", - "1. 高分轨迹的工具序列可在各 agent 的 system prompt 中作为'推荐工作流'注入", - "2. 步骤数偏多(>5)的 agent 提示词应强调'一次到位,减少反复读取'", - "3. 持续积累轨迹 → 达到一定量后启动更高阶优化(LoRA 微调)", - ] - return "\n".join(lines) - - def save_insights(self) -> Path: - """保存洞察报告到 insights 目录。""" - report = self.generate_insights() - path = self._insights_dir / "evolution_insights.md" - path.write_text(report, encoding="utf-8") - return path - - # ── 反哺 prompt (自进化闭环) ──────────────────────── - def evolve_prompt_suggestion(self, agent_key: str) -> str | None: - """为指定 agent 生成可注入的 prompt 增强建议(基于历史高分轨迹)。 - - 返回一段可追加到该 agent system_prompt 的文本, 若无历史则 None。 - """ - analysis = self.analyze() - if analysis.get("count", 0) == 0: - return None - hp = analysis["high"] - seqs = hp["patterns"]["seqs"] - if not seqs: - return None - top_seq_text = " → ".join(seqs[0][0]) if seqs else "" - suggestion = ( - f"\n\n[自进化推荐 · 基于 {analysis['count']} 条历史轨迹] " - f"高分任务常用工具序列: {top_seq_text}。" - f"请优先复用该模式,减少无效工具往返,一次到位完成任务。" - ) - return suggestion diff --git a/backend/xingbang/evolution/reward.py b/backend/xingbang/evolution/reward.py deleted file mode 100644 index e762e8e..0000000 --- a/backend/xingbang/evolution/reward.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Reward Calculator — 计算一条审批轨迹的奖励分数。 - -诚实分级设计 (对齐兴邦方案): - Level 1 (现在能做,不需要用户交互): - - 任务完成 +10 - - 工具调用成功率 - - 重复调用惩罚 - - 时间惩罚 - Level 2 (需前端加 👍/👎 按钮): - - 用户显式反馈 - Level 3 (需人工标注): - - 专家打分 - -本文件实现 Level 1 + 预留 Level 2/3 参数。 -不做不诚实的信号: 拒绝用"还款表现"当奖励(周期1-3年无法闭环)。 -""" - -from __future__ import annotations -from typing import Any - - -def compute_reward_v1( - trajectory: dict[str, Any], - feedback: str | None = None, - expert_score: float | None = None, -) -> float: - """计算一条轨迹的奖励(Leve1 l 内置指标 + 可选 Level2 反馈 + Level3 专家分)。 - - Args: - trajectory: 轨迹 dict(含 steps 和 final_result) - feedback: Level2, "positive"/"negative" - expert_score: Level3, 1-5 分 - Returns: - float 奖励分 - """ - steps = trajectory.get("steps", []) - final = trajectory.get("final_result", {}) - if not steps: - return 0.0 - - reward = 0.0 - - # 1. 任务完成奖励 - if final.get("success"): - reward += 10.0 - else: - reward -= 10.0 - - # 2. 工具调用成功率(成功工具 / 总工具) - total_tool_calls = 0 - tool_success = 0 - for s in steps: - tools = s.get("tools", []) - total_tool_calls += len(tools) - if tools: - tool_success += 1 # 该步骤至少调了工具视为尽力(简化) - if total_tool_calls > 0: - reward += (tool_success / max(1, total_tool_calls)) * 5.0 - - # 3. 步骤数惩罚(越少越好,常规 5 步流水线 5-15 步为佳) - n_steps = len(steps) - optimal = 5 - step_penalty = abs(n_steps - optimal) * 0.3 - reward -= step_penalty - - # 4. 时间惩罚(超过 600 秒 = 10 分钟开始扣) - duration_ms = final.get("duration_ms", 0) - if duration_ms > 600_000: - reward -= ((duration_ms - 600_000) / 60_000) * 1.0 - - # ── Level 2: 用户反馈 (可选) ── - if feedback == "positive": - reward += 20.0 - elif feedback == "negative": - reward -= 10.0 - - # ── Level 3: 专家打分 (可选, 1-5 归一化到 -10~+10) ── - if expert_score is not None: - reward += (expert_score - 3) * 5.0 - - reward = max(-30.0, min(reward, 50.0)) # 裁剪 - return round(reward, 2) diff --git a/backend/xingbang/evolution/trajectory.py b/backend/xingbang/evolution/trajectory.py deleted file mode 100644 index 6cd05d4..0000000 --- a/backend/xingbang/evolution/trajectory.py +++ /dev/null @@ -1,123 +0,0 @@ -"""Trajectory Recorder — 采集五智能体每次调用的完整轨迹。 - -轨迹格式 (trajectory): -{ - "trajectory_id": "traj_", - "customer": "客户名", - "workflow_id": "...", - "steps": [ - { - "agent": "data_curator", - "input": "任务描述", - "prompt_snippet": "...", - "tools": ["read_xingbang_data", ...], - "output_file": "...", - "output_preview": "...", - "success": true/false, - "time_ms": 1234 - }, ... - ], - "final_result": {...}, - "cumulative_reward": 0.0 -} - -轨迹保存在 /xingbang_evolution/trajectories/*.json。 -""" - -from __future__ import annotations - -import json -import time -import uuid -from pathlib import Path -from typing import Any - - -class TrajectoryRecorder: - """记录并持久化一次评审流水线的完整执行轨迹。""" - - def __init__(self, base_dir: Path, enabled: bool = True): - self._base_dir = Path(base_dir) - self._dir = self._base_dir / "xingbang_evolution" / "trajectories" - self._dir.mkdir(parents=True, exist_ok=True) - self.enabled = enabled - # 当前正在记录的轨迹 - self._current: dict[str, Any] | None = None - # metadata (customer 等) - self._meta: dict = {} - self._start_time: float = 0.0 - - def begin(self, workflow_id: str | None = None, customer: str = "") -> str: - """开始记录一条新轨迹,返回 trajectory_id。""" - self._start_time = time.time() - self._current = { - "trajectory_id": f"traj_{int(time.time())}_{uuid.uuid4().hex[:6]}", - "workflow_id": workflow_id or f"wf_{int(time.time())}", - "customer": customer or "", - "created_at": self._start_time, - "steps": [], - "final_result": {}, - "cumulative_reward": 0.0, - } - return self._current["trajectory_id"] - - def add_step(self, step: dict[str, Any]) -> None: - """追加一步智能体执行记录。""" - if self._current is not None: - self._current["steps"].append(step) - - def finalize(self, success: bool = True, summary: str = "") -> dict[str, Any]: - """结束记录,保存轨迹到磁盘,返回轨迹。""" - if self._current is None: - return {} - self._current["final_result"] = { - "success": success, - "summary": summary, - "duration_ms": int((time.time() - self._start_time) * 1000), - "total_steps": len(self._current["steps"]), - } - # 记录成功/失败 - trajectory = self._current - self._save(trajectory) - self._current = None - return trajectory - - def _save(self, trajectory: dict[str, Any]) -> Path: - path = self._dir / f"{trajectory['trajectory_id']}.json" - path.write_text( - json.dumps(trajectory, ensure_ascii=False, indent=2), - encoding="utf-8", - ) - return path - - def set_reward(self, reward: float) -> None: - """计算奖励后写回当前轨迹并重新落盘(进化闭环数据完整)。""" - if self._current is not None: - self._current["cumulative_reward"] = reward - # 若已有 trajectory_id, 覆盖落盘(full path 已知) - tid = self._current.get("trajectory_id") - if tid: - self._save(self._current) - - def rewrite(self, trajectory: dict[str, Any]) -> Path: - """写回一条已存在的轨迹(非当前进行中的), 返回路径。""" - return self._save(trajectory) - - # ── 从调用侧采集 ──────────────────────────────────── - def on_evidence(self, kind: str, data: dict) -> None: - """订阅 agents 的 evidence 回调,累积成本步记录。""" - if not self.enabled or self._current is None: - return - if kind == "agent_start": - buffered = [] - self._current["_buffered"] = buffered - # 这一步先不展开, 由 workflow 显式调用 add_step 更为清晰 - - def list_trajectories(self) -> list[Path]: - return sorted(self._dir.glob("*.json")) - - def load(self, trajectory_id: str) -> dict[str, Any]: - path = self._dir / f"{trajectory_id}.json" - if path.exists(): - return json.loads(path.read_text(encoding="utf-8")) - return {} diff --git a/backend/xingbang/workflow.py b/backend/xingbang/workflow.py deleted file mode 100644 index 85df112..0000000 --- a/backend/xingbang/workflow.py +++ /dev/null @@ -1,237 +0,0 @@ -"""XingbangWorkflow — 五智能体审批流水线 + 自进化闭环。 - -执行流程: - data_curator → risk_scout → case_finder → approval_advisor → risk_monitor - (case_finder 与 risk_scout 可并行, 此处先串行保证可追溯) - -每个 agent 的产出写入输出目录, 供下游读取; 同时全程记录 -轨迹到 evolution 模块, 跑完后计算奖励并更新进化洞察。 - -API 风格: run() 返回完整结果; 也可用 run_stream() 逐步 yield 进度。 -""" - -from __future__ import annotations - -import time -import uuid -from pathlib import Path -from typing import Any, AsyncGenerator - -from xingbang.config import agent_specs -from xingbang.agents import build_agents, XingbangAgent -from xingbang.evolution.trajectory import TrajectoryRecorder -from xingbang.evolution.reward import compute_reward_v1 -from xingbang.evolution.optimizer import EvolutionEngine - - -class XingbangWorkflow: - """兴邦五智能体审批工作流。""" - - def __init__( - self, - llm, - base_dir: Path, - output_dir: Path | None = None, - evolution_enabled: bool = True, - ): - self._base_dir = Path(base_dir) - self._output_dir = output_dir or (self._base_dir / "xingbang_output") - self._output_dir.mkdir(parents=True, exist_ok=True) - - # 自进化模块 - self.evolution_enabled = evolution_enabled - self._recorder = TrajectoryRecorder(self._base_dir, enabled=evolution_enabled) - self._evolution = EvolutionEngine(self._base_dir, self._recorder, enabled=evolution_enabled) - - # 五智能体(注入证据回调 → 轨迹) - self._agents: dict[str, XingbangAgent] = build_agents( - llm=llm, - base_dir=self._output_dir, - output_dir=self._output_dir, - on_evidence=self._on_evidence, - ) - - # 流水线顺序 - self._pipeline = ["data_curator", "risk_scout", "case_finder", - "approval_advisor", "risk_monitor"] - self._results: dict[str, dict] = {} - - def _on_evidence(self, kind: str, data: dict): - # 轨迹采集(保持简单: 在 workflow 层显式 add_step) - pass - - @property - def recorder(self) -> TrajectoryRecorder: - return self._recorder - - @property - def evolution(self) -> EvolutionEngine: - return self._evolution - - # ── 单次评审 ──────────────────────────────────────── - async def run(self, task: str, customer: str = "") -> dict[str, Any]: - """运行完整五智能体审批流水线,返回各步产出 + 轨迹 + 奖励。""" - workflow_id = f"wf_{int(time.time())}_{uuid.uuid4().hex[:4]}" - traj_id = self._recorder.begin(workflow_id=workflow_id, customer=customer) - - # 第一步任务描述 - tasks = self._build_tasks(task) - final_output = "" - summary_parts = [] - - for key in self._pipeline: - agent = self._agents[key] - agent_task = tasks.get(key, task) - - # 收集上游上下文(该 agent 依赖的产出) - ctx = self._collect_context(key, agent_task) - - t0 = time.time() - try: - result = await agent.run(agent_task, context=ctx) - success = True - output = result["output"] - file = result.get("file") - except Exception as e: # noqa - success = False - output = f"[执行失败] {e}" - file = None - - elapsed_ms = int((time.time() - t0) * 1000) - - # 记录这一步到轨迹 - step = { - "agent": key, - "name": agent.spec.name, - "input": agent_task[:300], - "tools": ["read_xingbang_data", "read_output"], - "output_file": str(file) if file else None, - "output_preview": output[:200], - "success": success, - "time_ms": elapsed_ms, - } - self._recorder.add_step(step) - self._results[key] = {"output": output, "file": file, "success": success} - summary_parts.append(f"[{key}] {'✅成功' if success else '❌失败'}") - final_output = output # 最后一环(风险哨兵)作为总结论 - - # 收尾: 计算奖励 + 保存进化洞察 - trajectory = self._recorder.finalize( - success=all(self._results[k]["success"] for k in self._pipeline), - summary=" | ".join(summary_parts), - ) - reward = compute_reward_v1(trajectory) - trajectory["cumulative_reward"] = reward - self._recorder.rewrite(trajectory) # 写回含 reward 轨迹, 进化数据完整 - insights_path = self._evolution.save_insights() - - return { - "workflow_id": workflow_id, - "trajectory_id": traj_id, - "customer": customer, - "reward": reward, - "results": { - k: { - kk: (str(vv) if kk == "file" and vv is not None else vv) - for kk, vv in v.items() - if kk in ("output", "file", "success") - } - for k, v in self._results.items() - }, - "final_summary": summary_parts, - "evolution_insights_file": str(insights_path), - } - - # ── 逐步流式(供前端展示进度) ─────────────────────── - async def run_stream(self, task: str, customer: str = "") -> AsyncGenerator[dict, None]: - """逐步 yield 每步进度与产出,便于前端 SSE 展示。""" - workflow_id = f"wf_{int(time.time())}_{uuid.uuid4().hex[:4]}" - traj_id = self._recorder.begin(workflow_id=workflow_id, customer=customer) - tasks = self._build_tasks(task) - - for key in self._pipeline: - agent = self._agents[key] - agent_task = tasks.get(key, task) - ctx = self._collect_context(key, agent_task) - yield {"event": "agent_start", "agent": key, "name": agent.spec.name} - - t0 = time.time() - try: - result = await agent.run(agent_task, context=ctx) - success = True - output = result["output"] - file = result.get("file") - except Exception as e: # noqa - success = False - output = f"[执行失败] {e}" - file = None - elapsed_ms = int((time.time() - t0) * 1000) - - step = { - "agent": key, "name": agent.spec.name, "input": agent_task[:300], - "tools": ["read_xingbang_data", "read_output"], - "output_file": str(file) if file else None, - "output_preview": output[:200], "success": success, "time_ms": elapsed_ms, - } - self._recorder.add_step(step) - self._results[key] = {"output": output, "file": file, "success": success} - yield {"event": "agent_end", "agent": key, "name": agent.spec.name, - "success": success, "output": output, "file": str(file) if file else None} - - trajectory = self._recorder.finalize(success=True) - reward = compute_reward_v1(trajectory) - trajectory["cumulative_reward"] = reward - self._recorder.rewrite(trajectory) # 写回含 reward 轨迹, 进化数据完整 - insights_path = self._evolution.save_insights() - yield {"event": "done", "workflow_id": workflow_id, - "trajectory_id": traj_id, "reward": reward, - "insights_file": str(insights_path)} - - # ── 辅助 ──────────────────────────────────────────── - def _build_tasks(self, user_task: str) -> dict[str, str]: - """为每个智能体构造针对性的任务描述。""" - return { - "data_curator": ( - f"请读取兴邦数据包中的多源数据, 为客户『{user_task}』(申请评审) " - f"生成结构化客户情报卡, 保存到 customer_intel.md。" - ), - "risk_scout": ( - f"基于上游 customer_intel.md, 对客户『{user_task}』做尽调分析, " - f"生成尽调报告初稿, 保存到 due_diligence.md。" - ), - "case_finder": ( - f"检索历史案例库与制度规则, 为『{user_task}』这类项目找出最相关案例, " - f"生成历史案例参考, 保存到 case_reference.md。" - ), - "approval_advisor": ( - f"汇总情报卡/尽调/历史案例, 按三档分级制度为『{user_task}』" - f"输出审批参考意见, 保存到 approval_opinion.md。" - ), - "risk_monitor": ( - f"审批前动态复核最新风控数据, 为『{user_task}』输出风控复核结论, " - f"保存到 risk_recheck.md。" - ), - } - - def _collect_context(self, agent_key: str, task: str) -> str: - """收集该 agent 依赖的上游产出作为上下文。""" - spec = agent_specs()[agent_key] - parts = [] - for dep in spec.inputs_from: - res = self._results.get(self._dep_owner(dep)) - if res and res.get("output"): - parts.append(f"[{dep}]\n{res['output'][:2000]}") - if parts: - return "\n\n".join(parts) - return "" - - def _dep_owner(self, output_file: str) -> str: - """根据产出文件名反查产生它的 agent key。""" - mapping = { - "customer_intel.md": "data_curator", - "due_diligence.md": "risk_scout", - "case_reference.md": "case_finder", - "approval_opinion.md": "approval_advisor", - "risk_recheck.md": "risk_monitor", - } - return mapping.get(output_file, "data_curator") diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..90d0f3e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,58 @@ +version: '3.8' + +services: + xingbang-app: + build: + context: . + dockerfile: Dockerfile + container_name: xingbang-ai-native + ports: + - "8002:8002" # 后端 API + - "3000:3000" # 前端 UI + environment: + - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY} + - DEEPSEEK_MODEL=${DEEPSEEK_MODEL:-deepseek-chat} + - DEEPSEEK_BASE_URL=${DEEPSEEK_BASE_URL:-https://api.deepseek.com} + - ERP_DB_HOST=${ERP_DB_HOST} + - ERP_DB_PORT=${ERP_DB_PORT:-5432} + - ERP_DB_NAME=${ERP_DB_NAME} + - ERP_DB_USER=${ERP_DB_USER} + - ERP_DB_PASSWORD=${ERP_DB_PASSWORD} + - ERP_DB_SSL_MODE=${ERP_DB_SSL_MODE:-require} + volumes: + - xingbang-data:/app/backend/xingbang_data + - xingbang-logs:/app/backend/logs + secrets: + - erp_db_password + - deepseek_api_key + restart: unless-stopped + networks: + - xingbang-network + + # (可选)如果需要本地测试数据库 + # postgres: + # image: postgres:15-alpine + # container_name: xingbang-erp-mock + # environment: + # - POSTGRES_DB=xingbang_erp + # - POSTGRES_USER=readonly_user + # - POSTGRES_PASSWORD_FILE=/run/secrets/erp_db_password + # volumes: + # - postgres-data:/var/lib/postgresql/data + # networks: + # - xingbang-network + +volumes: + xingbang-data: + xingbang-logs: + # postgres-data: + +networks: + xingbang-network: + driver: bridge + +secrets: + erp_db_password: + file: ./secrets/erp_db_password.txt + deepseek_api_key: + file: ./secrets/deepseek_api_key.txt diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..15f1339 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e + +echo "🚀 启动兴邦金租 AI Native 智能决策系统" + +# 等待 ERP 数据库就绪(如果配置了) +if [ ! -z "$ERP_DB_HOST" ]; then + echo "⏳ 等待 ERP 数据库连接..." + until pg_isready -h $ERP_DB_HOST -p $ERP_DB_PORT -U $ERP_DB_USER; do + echo "数据库未就绪,2秒后重试..." + sleep 2 + done + echo "✅ ERP 数据库已连接" +fi + +# 启动后端(后台) +echo "🔧 启动后端服务 (端口 8002)..." +cd /app/backend +python app.py & + +# 启动前端(前台) +echo "🌐 启动前端服务 (端口 3000)..." +cd /app/frontend +npm start diff --git a/package-lock.json b/package-lock.json index 72177f1..3f163b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,9 @@ "@monaco-editor/react": "^4.6.0", "clsx": "^2.1.0", "lucide-react": "^0.400.0", - "next": "^14.2.0", - "react": "^18.3.0", - "react-dom": "^18.3.0", + "next": "^14.1.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", "react-markdown": "^9.0.0", "remark-gfm": "^4.0.0" }, @@ -30,9 +30,9 @@ } }, "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.3.0.tgz", + "integrity": "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==", "dev": true, "license": "MIT", "engines": { @@ -43,21 +43,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", - "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "version": "1.10.0", + "resolved": "https://registry.npmmirror.com/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.1.0", + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "version": "1.10.0", + "resolved": "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, "license": "MIT", "optional": true, @@ -66,9 +66,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", "optional": true, @@ -77,9 +77,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "version": "4.10.1", + "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", "dependencies": { @@ -97,7 +97,7 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", @@ -107,7 +107,7 @@ }, "node_modules/@eslint/eslintrc": { "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", @@ -131,7 +131,7 @@ }, "node_modules/@eslint/js": { "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", @@ -141,7 +141,7 @@ }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, @@ -157,7 +157,7 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", @@ -171,7 +171,7 @@ }, "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", "dev": true, @@ -179,7 +179,7 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "license": "ISC", @@ -196,9 +196,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "dev": true, "license": "MIT", "engines": { @@ -209,13 +209,13 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -226,7 +226,7 @@ }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", @@ -237,7 +237,7 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", @@ -246,15 +246,15 @@ } }, "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==", + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "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", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", @@ -265,7 +265,7 @@ }, "node_modules/@monaco-editor/loader": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz", + "resolved": "https://registry.npmmirror.com/@monaco-editor/loader/-/loader-1.7.0.tgz", "integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==", "license": "MIT", "dependencies": { @@ -274,7 +274,7 @@ }, "node_modules/@monaco-editor/react": { "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz", + "resolved": "https://registry.npmmirror.com/@monaco-editor/react/-/react-4.7.0.tgz", "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==", "license": "MIT", "dependencies": { @@ -287,27 +287,36 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" } }, "node_modules/@next/env": { "version": "14.2.35", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.35.tgz", + "resolved": "https://registry.npmmirror.com/@next/env/-/env-14.2.35.tgz", "integrity": "sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { "version": "14.2.35", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.35.tgz", + "resolved": "https://registry.npmmirror.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.35.tgz", "integrity": "sha512-Jw9A3ICz2183qSsqwi7fgq4SBPiNfmOLmTPXKvlnzstUwyvBrtySiY+8RXJweNAs9KThb1+bYhZh9XWcNOr2zQ==", "dev": true, "license": "MIT", @@ -317,7 +326,7 @@ }, "node_modules/@next/swc-darwin-arm64": { "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.33.tgz", + "resolved": "https://registry.npmmirror.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.33.tgz", "integrity": "sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA==", "cpu": [ "arm64" @@ -333,7 +342,7 @@ }, "node_modules/@next/swc-darwin-x64": { "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz", + "resolved": "https://registry.npmmirror.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz", "integrity": "sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==", "cpu": [ "x64" @@ -349,11 +358,14 @@ }, "node_modules/@next/swc-linux-arm64-gnu": { "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz", + "resolved": "https://registry.npmmirror.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz", "integrity": "sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -365,11 +377,14 @@ }, "node_modules/@next/swc-linux-arm64-musl": { "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz", + "resolved": "https://registry.npmmirror.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz", "integrity": "sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -381,11 +396,14 @@ }, "node_modules/@next/swc-linux-x64-gnu": { "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz", + "resolved": "https://registry.npmmirror.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz", "integrity": "sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -397,11 +415,14 @@ }, "node_modules/@next/swc-linux-x64-musl": { "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz", + "resolved": "https://registry.npmmirror.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz", "integrity": "sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -413,7 +434,7 @@ }, "node_modules/@next/swc-win32-arm64-msvc": { "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz", + "resolved": "https://registry.npmmirror.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz", "integrity": "sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==", "cpu": [ "arm64" @@ -429,7 +450,7 @@ }, "node_modules/@next/swc-win32-ia32-msvc": { "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz", + "resolved": "https://registry.npmmirror.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz", "integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==", "cpu": [ "ia32" @@ -445,7 +466,7 @@ }, "node_modules/@next/swc-win32-x64-msvc": { "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz", + "resolved": "https://registry.npmmirror.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz", "integrity": "sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==", "cpu": [ "x64" @@ -461,7 +482,7 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", @@ -475,7 +496,7 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", @@ -485,7 +506,7 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", @@ -499,7 +520,7 @@ }, "node_modules/@nolyfill/is-core-module": { "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "resolved": "https://registry.npmmirror.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", "dev": true, "license": "MIT", @@ -509,7 +530,7 @@ }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "license": "MIT", @@ -520,27 +541,27 @@ }, "node_modules/@rtsao/scc": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/@rtsao/scc/-/scc-1.1.0.tgz", "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true, "license": "MIT" }, "node_modules/@rushstack/eslint-patch": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.15.0.tgz", - "integrity": "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==", + "version": "1.16.1", + "resolved": "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz", + "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", "dev": true, "license": "MIT" }, "node_modules/@swc/counter": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "resolved": "https://registry.npmmirror.com/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", "license": "Apache-2.0" }, "node_modules/@swc/helpers": { "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "resolved": "https://registry.npmmirror.com/@swc/helpers/-/helpers-0.5.5.tgz", "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", "license": "Apache-2.0", "dependencies": { @@ -549,9 +570,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "version": "0.10.3", + "resolved": "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -560,23 +581,23 @@ } }, "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "version": "4.1.13", + "resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", "license": "MIT", "dependencies": { "@types/ms": "*" } }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/estree-jsx": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "resolved": "https://registry.npmmirror.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", "license": "MIT", "dependencies": { @@ -584,9 +605,9 @@ } }, "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", "license": "MIT", "dependencies": { "@types/unist": "*" @@ -594,14 +615,14 @@ }, "node_modules/@types/json5": { "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "resolved": "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true, "license": "MIT" }, "node_modules/@types/mdast": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-4.0.4.tgz", "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", "license": "MIT", "dependencies": { @@ -610,14 +631,14 @@ }, "node_modules/@types/ms": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.31.tgz", - "integrity": "sha512-5jsi0wpncvTD33Sh1UCgacK37FFwDn+EG7wCmEvs62fCvBL+n8/76cAYDok21NF6+jaVWIqKwCZyX7Vbu8eB3A==", + "version": "20.19.43", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "dev": true, "license": "MIT", "dependencies": { @@ -626,14 +647,14 @@ }, "node_modules/@types/prop-types": { "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.27", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", - "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "version": "18.3.31", + "resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.31.tgz", + "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -642,7 +663,7 @@ }, "node_modules/@types/react-dom": { "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz", "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "dev": true, "license": "MIT", @@ -652,7 +673,7 @@ }, "node_modules/@types/trusted-types": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT", "optional": true, @@ -660,25 +681,25 @@ }, "node_modules/@types/unist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", - "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", + "version": "8.69.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.69.0.tgz", + "integrity": "sha512-t5jQTKPIgVW1PE6dR6H6Qz5gm8zjMlX5/2gRaOGd9eO6V7J+tQc6iWKukEe7dY8u9HyYasQ0yfF0/FSSTEO2gA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/type-utils": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/scope-manager": "8.69.0", + "@typescript-eslint/type-utils": "8.69.0", + "@typescript-eslint/utils": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -688,15 +709,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.54.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "@typescript-eslint/parser": "^8.69.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.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "version": "7.0.8", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.8.tgz", + "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", "dev": true, "license": "MIT", "engines": { @@ -704,16 +725,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", - "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", + "version": "8.69.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.69.0.tgz", + "integrity": "sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/scope-manager": "8.69.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/typescript-estree": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0", "debug": "^4.4.3" }, "engines": { @@ -724,19 +745,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "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.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", - "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "version": "8.69.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.69.0.tgz", + "integrity": "sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.54.0", - "@typescript-eslint/types": "^8.54.0", + "@typescript-eslint/tsconfig-utils": "^8.69.0", + "@typescript-eslint/types": "^8.69.0", "debug": "^4.4.3" }, "engines": { @@ -747,18 +768,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", - "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", + "version": "8.69.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.69.0.tgz", + "integrity": "sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0" + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -769,9 +790,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", - "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", + "version": "8.69.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.69.0.tgz", + "integrity": "sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==", "dev": true, "license": "MIT", "engines": { @@ -782,21 +803,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", - "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", + "version": "8.69.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.69.0.tgz", + "integrity": "sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/typescript-estree": "8.69.0", + "@typescript-eslint/utils": "8.69.0", "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -806,14 +827,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", - "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "version": "8.69.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.69.0.tgz", + "integrity": "sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==", "dev": true, "license": "MIT", "engines": { @@ -825,21 +846,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", - "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "version": "8.69.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.69.0.tgz", + "integrity": "sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.54.0", - "@typescript-eslint/tsconfig-utils": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/project-service": "8.69.0", + "@typescript-eslint/tsconfig-utils": "8.69.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0", "debug": "^4.4.3", - "minimatch": "^9.0.5", + "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -849,46 +870,59 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/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": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.9", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.2.6", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.8" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", - "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "version": "8.69.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.69.0.tgz", + "integrity": "sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0" + "@typescript-eslint/scope-manager": "8.69.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/typescript-estree": "8.69.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -898,19 +932,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "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.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", - "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "version": "8.69.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.69.0.tgz", + "integrity": "sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.54.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.69.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -921,28 +955,28 @@ } }, "node_modules/@typescript-eslint/visitor-keys/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==", + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", "license": "ISC" }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", "cpu": [ "arm" ], @@ -954,9 +988,9 @@ ] }, "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", "cpu": [ "arm64" ], @@ -968,9 +1002,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", "cpu": [ "arm64" ], @@ -982,9 +1016,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", "cpu": [ "x64" ], @@ -996,9 +1030,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", "cpu": [ "x64" ], @@ -1010,9 +1044,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", "cpu": [ "arm" ], @@ -1024,9 +1058,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", "cpu": [ "arm" ], @@ -1038,13 +1072,16 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1052,13 +1089,50 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1066,13 +1140,16 @@ ] }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1080,13 +1157,16 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1094,13 +1174,16 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1108,13 +1191,16 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1122,13 +1208,16 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1136,23 +1225,40 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" ] }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", "cpu": [ "wasm32" ], @@ -1160,16 +1266,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", "cpu": [ "arm64" ], @@ -1181,9 +1289,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", "cpu": [ "ia32" ], @@ -1195,9 +1303,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", "cpu": [ "x64" ], @@ -1209,9 +1317,9 @@ ] }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.18.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", "bin": { @@ -1223,7 +1331,7 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", @@ -1232,9 +1340,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1250,7 +1358,7 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", @@ -1260,7 +1368,7 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", @@ -1276,14 +1384,14 @@ }, "node_modules/any-promise": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "dev": true, "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", @@ -1297,21 +1405,21 @@ }, "node_modules/arg": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "dev": true, "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, "license": "Python-2.0" }, "node_modules/aria-query": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "resolved": "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.2.tgz", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, "license": "Apache-2.0", @@ -1321,7 +1429,7 @@ }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, "license": "MIT", @@ -1338,7 +1446,7 @@ }, "node_modules/array-includes": { "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "resolved": "https://registry.npmmirror.com/array-includes/-/array-includes-3.1.9.tgz", "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, "license": "MIT", @@ -1361,7 +1469,7 @@ }, "node_modules/array.prototype.findlast": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "resolved": "https://registry.npmmirror.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, "license": "MIT", @@ -1382,7 +1490,7 @@ }, "node_modules/array.prototype.findlastindex": { "version": "1.2.6", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "resolved": "https://registry.npmmirror.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, "license": "MIT", @@ -1404,7 +1512,7 @@ }, "node_modules/array.prototype.flat": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "resolved": "https://registry.npmmirror.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, "license": "MIT", @@ -1423,7 +1531,7 @@ }, "node_modules/array.prototype.flatmap": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "resolved": "https://registry.npmmirror.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "license": "MIT", @@ -1442,7 +1550,7 @@ }, "node_modules/array.prototype.tosorted": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "resolved": "https://registry.npmmirror.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, "license": "MIT", @@ -1459,7 +1567,7 @@ }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "resolved": "https://registry.npmmirror.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "license": "MIT", @@ -1481,14 +1589,14 @@ }, "node_modules/ast-types-flow": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "resolved": "https://registry.npmmirror.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz", "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true, "license": "MIT" }, "node_modules/async-function": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "resolved": "https://registry.npmmirror.com/async-function/-/async-function-1.0.0.tgz", "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, "license": "MIT", @@ -1497,9 +1605,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.24", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", - "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", + "version": "10.5.4", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.5.4.tgz", + "integrity": "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==", "dev": true, "funding": [ { @@ -1517,8 +1625,8 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001766", + "browserslist": "^4.28.6", + "caniuse-lite": "^1.0.30001806", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" @@ -1535,7 +1643,7 @@ }, "node_modules/available-typed-arrays": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, "license": "MIT", @@ -1550,9 +1658,9 @@ } }, "node_modules/axe-core": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", - "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", + "version": "4.13.0", + "resolved": "https://registry.npmmirror.com/axe-core/-/axe-core-4.13.0.tgz", + "integrity": "sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==", "dev": true, "license": "MPL-2.0", "engines": { @@ -1561,7 +1669,7 @@ }, "node_modules/axobject-query": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "resolved": "https://registry.npmmirror.com/axobject-query/-/axobject-query-4.1.0.tgz", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, "license": "Apache-2.0", @@ -1571,7 +1679,7 @@ }, "node_modules/bail": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "resolved": "https://registry.npmmirror.com/bail/-/bail-2.0.2.tgz", "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "license": "MIT", "funding": { @@ -1581,24 +1689,27 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "version": "2.11.21", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", + "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/binary-extensions": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", @@ -1610,9 +1721,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1622,7 +1733,7 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", @@ -1634,9 +1745,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.8", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", "dev": true, "funding": [ { @@ -1654,11 +1765,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "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" @@ -1669,7 +1780,7 @@ }, "node_modules/busboy": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "resolved": "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz", "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "dependencies": { "streamsearch": "^1.1.0" @@ -1679,15 +1790,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" }, "engines": { @@ -1699,7 +1810,7 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", @@ -1713,7 +1824,7 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, "license": "MIT", @@ -1730,7 +1841,7 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", @@ -1740,7 +1851,7 @@ }, "node_modules/camelcase-css": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "dev": true, "license": "MIT", @@ -1749,9 +1860,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001768", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001768.tgz", - "integrity": "sha512-qY3aDRZC5nWPgHUgIB84WL+nySuo19wk0VJpp/XI9T34lrvkyhRvNVOFJOp2kxClQhiFBu+TaUSudf6oa3vkSA==", + "version": "1.0.30001810", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "funding": [ { "type": "opencollective", @@ -1770,7 +1881,7 @@ }, "node_modules/ccount": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/ccount/-/ccount-2.0.1.tgz", "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", "license": "MIT", "funding": { @@ -1780,7 +1891,7 @@ }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", @@ -1797,7 +1908,7 @@ }, "node_modules/character-entities": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "resolved": "https://registry.npmmirror.com/character-entities/-/character-entities-2.0.2.tgz", "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "license": "MIT", "funding": { @@ -1807,7 +1918,7 @@ }, "node_modules/character-entities-html4": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz", "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", "license": "MIT", "funding": { @@ -1817,7 +1928,7 @@ }, "node_modules/character-entities-legacy": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", "license": "MIT", "funding": { @@ -1827,7 +1938,7 @@ }, "node_modules/character-reference-invalid": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", "license": "MIT", "funding": { @@ -1837,7 +1948,7 @@ }, "node_modules/chokidar": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", @@ -1862,7 +1973,7 @@ }, "node_modules/chokidar/node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", @@ -1875,13 +1986,13 @@ }, "node_modules/client-only": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "resolved": "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, "node_modules/clsx": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", "engines": { @@ -1890,7 +2001,7 @@ }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", @@ -1903,14 +2014,14 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/comma-separated-tokens": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "resolved": "https://registry.npmmirror.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "license": "MIT", "funding": { @@ -1920,7 +2031,7 @@ }, "node_modules/commander": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, "license": "MIT", @@ -1930,14 +2041,14 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", @@ -1952,7 +2063,7 @@ }, "node_modules/cssesc": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, "license": "MIT", @@ -1965,20 +2076,20 @@ }, "node_modules/csstype": { "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "resolved": "https://registry.npmmirror.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true, "license": "BSD-2-Clause" }, "node_modules/data-view-buffer": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz", "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", @@ -1996,7 +2107,7 @@ }, "node_modules/data-view-byte-length": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", @@ -2014,7 +2125,7 @@ }, "node_modules/data-view-byte-offset": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "resolved": "https://registry.npmmirror.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "license": "MIT", @@ -2032,7 +2143,7 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { @@ -2049,7 +2160,7 @@ }, "node_modules/decode-named-character-reference": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "resolved": "https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", "license": "MIT", "dependencies": { @@ -2062,14 +2173,14 @@ }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/define-data-property": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "license": "MIT", @@ -2087,7 +2198,7 @@ }, "node_modules/define-properties": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "license": "MIT", @@ -2105,7 +2216,7 @@ }, "node_modules/dequal": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "resolved": "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "license": "MIT", "engines": { @@ -2114,7 +2225,7 @@ }, "node_modules/devlop": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/devlop/-/devlop-1.1.0.tgz", "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "license": "MIT", "dependencies": { @@ -2127,21 +2238,21 @@ }, "node_modules/didyoumean": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "dev": true, "license": "Apache-2.0" }, "node_modules/dlv": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "dev": true, "license": "MIT" }, "node_modules/doctrine": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "license": "Apache-2.0", @@ -2153,9 +2264,9 @@ } }, "node_modules/dompurify": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", - "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "version": "3.4.8", + "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.8.tgz", + "integrity": "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ==", "license": "(MPL-2.0 OR Apache-2.0)", "peer": true, "optionalDependencies": { @@ -2164,7 +2275,7 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", @@ -2179,29 +2290,29 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "version": "1.5.421", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.421.tgz", + "integrity": "sha512-cUhfpHQy+PGbt+X90DMcAVazDCziIZr73hpxD4LRs4BGQoJCifPzTfQWa7S6c+uhokTOBe8tot09GBSEO6c9LA==", "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "license": "MIT" }, "node_modules/es-abstract": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "version": "1.24.2", + "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", "dev": true, "license": "MIT", "dependencies": { @@ -2267,9 +2378,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", @@ -2279,7 +2409,7 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "license": "MIT", @@ -2288,16 +2418,16 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", - "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/es-iterator-helpers/-/es-iterator-helpers-1.4.0.tgz", + "integrity": "sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", + "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.24.1", + "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", @@ -2309,16 +2439,16 @@ "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", - "safe-array-concat": "^1.1.3" + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "dev": true, "license": "MIT", "dependencies": { @@ -2330,7 +2460,7 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", @@ -2346,7 +2476,7 @@ }, "node_modules/es-shim-unscopables": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", @@ -2358,15 +2488,18 @@ } }, "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", "dev": true, "license": "MIT", "dependencies": { + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -2377,7 +2510,7 @@ }, "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", @@ -2387,7 +2520,7 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", @@ -2400,7 +2533,7 @@ }, "node_modules/eslint": { "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, @@ -2457,7 +2590,7 @@ }, "node_modules/eslint-config-next": { "version": "14.2.35", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.35.tgz", + "resolved": "https://registry.npmmirror.com/eslint-config-next/-/eslint-config-next-14.2.35.tgz", "integrity": "sha512-BpLsv01UisH193WyT/1lpHqq5iJ/Orfz9h/NOOlAmTUq4GY349PextQ62K4XpnaM9supeiEn3TaOTeQO07gURg==", "dev": true, "license": "MIT", @@ -2484,20 +2617,20 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "version": "0.3.10", + "resolved": "https://registry.npmmirror.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", "dev": true, "license": "MIT", "dependencies": { "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", @@ -2507,7 +2640,7 @@ }, "node_modules/eslint-import-resolver-typescript": { "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "resolved": "https://registry.npmmirror.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", "dev": true, "license": "ISC", @@ -2541,9 +2674,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", - "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "version": "2.14.0", + "resolved": "https://registry.npmmirror.com/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", + "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", "dev": true, "license": "MIT", "dependencies": { @@ -2560,7 +2693,7 @@ }, "node_modules/eslint-module-utils/node_modules/debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", @@ -2570,7 +2703,7 @@ }, "node_modules/eslint-plugin-import": { "version": "2.32.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "resolved": "https://registry.npmmirror.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", @@ -2604,7 +2737,7 @@ }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", @@ -2614,7 +2747,7 @@ }, "node_modules/eslint-plugin-import/node_modules/doctrine": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "license": "Apache-2.0", @@ -2627,7 +2760,7 @@ }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", @@ -2637,7 +2770,7 @@ }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "resolved": "https://registry.npmmirror.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "dev": true, "license": "MIT", @@ -2667,7 +2800,7 @@ }, "node_modules/eslint-plugin-react": { "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", @@ -2700,7 +2833,7 @@ }, "node_modules/eslint-plugin-react-hooks": { "version": "5.0.0-canary-7118f5dd7-20230705", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", "dev": true, "license": "MIT", @@ -2713,7 +2846,7 @@ }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "license": "Apache-2.0", @@ -2724,27 +2857,9 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/eslint-plugin-react/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", @@ -2754,7 +2869,7 @@ }, "node_modules/eslint-scope": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", @@ -2771,7 +2886,7 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "resolved": "https://registry.npmmirror.com/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", @@ -2784,7 +2899,7 @@ }, "node_modules/espree": { "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", @@ -2802,7 +2917,7 @@ }, "node_modules/esquery": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", @@ -2815,7 +2930,7 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", @@ -2828,7 +2943,7 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", @@ -2838,7 +2953,7 @@ }, "node_modules/estree-util-is-identifier-name": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", "license": "MIT", "funding": { @@ -2848,7 +2963,7 @@ }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", @@ -2858,20 +2973,20 @@ }, "node_modules/extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", @@ -2888,7 +3003,7 @@ }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", @@ -2901,22 +3016,22 @@ }, "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", + "resolved": "https://registry.npmmirror.com/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", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "version": "1.20.3", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", "dev": true, "license": "ISC", "dependencies": { @@ -2925,7 +3040,7 @@ }, "node_modules/file-entry-cache": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "license": "MIT", @@ -2938,7 +3053,7 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", @@ -2951,7 +3066,7 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", @@ -2968,7 +3083,7 @@ }, "node_modules/flat-cache": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "license": "MIT", @@ -2982,15 +3097,15 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.4", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, "node_modules/for-each": { "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", @@ -3006,7 +3121,7 @@ }, "node_modules/foreground-child": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", @@ -3023,7 +3138,7 @@ }, "node_modules/fraction.js": { "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-5.3.4.tgz", "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "dev": true, "license": "MIT", @@ -3037,14 +3152,14 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true, "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, @@ -3059,7 +3174,7 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, "license": "MIT", @@ -3068,18 +3183,21 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -3090,7 +3208,7 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, "license": "MIT", @@ -3100,7 +3218,7 @@ }, "node_modules/generator-function": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz", "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", "dev": true, "license": "MIT", @@ -3110,7 +3228,7 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", @@ -3135,7 +3253,7 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, "license": "MIT", @@ -3149,7 +3267,7 @@ }, "node_modules/get-symbol-description": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz", "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", @@ -3166,9 +3284,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.1.tgz", - "integrity": "sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w==", + "version": "4.14.3", + "resolved": "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.14.3.tgz", + "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", "dev": true, "license": "MIT", "dependencies": { @@ -3180,9 +3298,8 @@ }, "node_modules/glob": { "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "resolved": "https://registry.npmmirror.com/glob/-/glob-10.3.10.tgz", "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -3204,7 +3321,7 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", @@ -3216,9 +3333,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -3226,13 +3343,13 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -3243,7 +3360,7 @@ }, "node_modules/globals": { "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", @@ -3259,7 +3376,7 @@ }, "node_modules/globalthis": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "license": "MIT", @@ -3276,7 +3393,7 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", @@ -3289,20 +3406,20 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/graphemer": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, "license": "MIT" }, "node_modules/has-bigints": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz", "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, "license": "MIT", @@ -3315,7 +3432,7 @@ }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", @@ -3325,7 +3442,7 @@ }, "node_modules/has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "license": "MIT", @@ -3338,7 +3455,7 @@ }, "node_modules/has-proto": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.2.0.tgz", "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, "license": "MIT", @@ -3354,7 +3471,7 @@ }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", @@ -3367,7 +3484,7 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", @@ -3382,9 +3499,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, "license": "MIT", "dependencies": { @@ -3396,7 +3513,7 @@ }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "resolved": "https://registry.npmmirror.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", "license": "MIT", "dependencies": { @@ -3423,7 +3540,7 @@ }, "node_modules/hast-util-whitespace": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", "license": "MIT", "dependencies": { @@ -3436,7 +3553,7 @@ }, "node_modules/html-url-attributes": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "resolved": "https://registry.npmmirror.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz", "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", "license": "MIT", "funding": { @@ -3446,7 +3563,7 @@ }, "node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", @@ -3456,7 +3573,7 @@ }, "node_modules/import-fresh": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", @@ -3473,7 +3590,7 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", @@ -3483,7 +3600,7 @@ }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, @@ -3495,20 +3612,20 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true, "license": "ISC" }, "node_modules/inline-style-parser": { "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "resolved": "https://registry.npmmirror.com/inline-style-parser/-/inline-style-parser-0.2.7.tgz", "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", "license": "MIT" }, "node_modules/internal-slot": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz", "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", @@ -3523,7 +3640,7 @@ }, "node_modules/is-alphabetical": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz", "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", "license": "MIT", "funding": { @@ -3533,7 +3650,7 @@ }, "node_modules/is-alphanumerical": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", "license": "MIT", "dependencies": { @@ -3547,7 +3664,7 @@ }, "node_modules/is-array-buffer": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", @@ -3565,7 +3682,7 @@ }, "node_modules/is-async-function": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "resolved": "https://registry.npmmirror.com/is-async-function/-/is-async-function-2.1.1.tgz", "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", @@ -3585,7 +3702,7 @@ }, "node_modules/is-bigint": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz", "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", @@ -3601,7 +3718,7 @@ }, "node_modules/is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", @@ -3614,7 +3731,7 @@ }, "node_modules/is-boolean-object": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz", "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", @@ -3631,7 +3748,7 @@ }, "node_modules/is-bun-module": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "resolved": "https://registry.npmmirror.com/is-bun-module/-/is-bun-module-2.0.0.tgz", "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, "license": "MIT", @@ -3641,7 +3758,7 @@ }, "node_modules/is-callable": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "license": "MIT", @@ -3653,13 +3770,13 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -3670,7 +3787,7 @@ }, "node_modules/is-data-view": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/is-data-view/-/is-data-view-1.0.2.tgz", "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", @@ -3688,7 +3805,7 @@ }, "node_modules/is-date-object": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", @@ -3705,7 +3822,7 @@ }, "node_modules/is-decimal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/is-decimal/-/is-decimal-2.0.1.tgz", "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", "license": "MIT", "funding": { @@ -3713,9 +3830,25 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", @@ -3725,7 +3858,7 @@ }, "node_modules/is-finalizationregistry": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "license": "MIT", @@ -3741,7 +3874,7 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", @@ -3751,7 +3884,7 @@ }, "node_modules/is-generator-function": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.2.tgz", "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, "license": "MIT", @@ -3771,7 +3904,7 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", @@ -3784,7 +3917,7 @@ }, "node_modules/is-hexadecimal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", "license": "MIT", "funding": { @@ -3794,7 +3927,7 @@ }, "node_modules/is-map": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, "license": "MIT", @@ -3807,7 +3940,7 @@ }, "node_modules/is-negative-zero": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz", "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "license": "MIT", @@ -3820,7 +3953,7 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", @@ -3830,7 +3963,7 @@ }, "node_modules/is-number-object": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.1.1.tgz", "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "license": "MIT", @@ -3847,7 +3980,7 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", @@ -3857,7 +3990,7 @@ }, "node_modules/is-plain-obj": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz", "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "license": "MIT", "engines": { @@ -3869,7 +4002,7 @@ }, "node_modules/is-regex": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "license": "MIT", @@ -3888,7 +4021,7 @@ }, "node_modules/is-set": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "resolved": "https://registry.npmmirror.com/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, "license": "MIT", @@ -3901,7 +4034,7 @@ }, "node_modules/is-shared-array-buffer": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "license": "MIT", @@ -3917,7 +4050,7 @@ }, "node_modules/is-string": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.1.1.tgz", "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", @@ -3934,7 +4067,7 @@ }, "node_modules/is-symbol": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.1.1.tgz", "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", @@ -3952,7 +4085,7 @@ }, "node_modules/is-typed-array": { "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "license": "MIT", @@ -3968,7 +4101,7 @@ }, "node_modules/is-weakmap": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "resolved": "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "license": "MIT", @@ -3981,7 +4114,7 @@ }, "node_modules/is-weakref": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.1.1.tgz", "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", @@ -3997,7 +4130,7 @@ }, "node_modules/is-weakset": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "resolved": "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz", "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", @@ -4014,21 +4147,21 @@ }, "node_modules/isarray": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true, "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, "node_modules/iterator.prototype": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "resolved": "https://registry.npmmirror.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz", "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, "license": "MIT", @@ -4046,7 +4179,7 @@ }, "node_modules/jackspeak": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "license": "BlueOak-1.0.0", @@ -4065,7 +4198,7 @@ }, "node_modules/jiti": { "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", @@ -4075,15 +4208,25 @@ }, "node_modules/js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "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" @@ -4094,28 +4237,28 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "resolved": "https://registry.npmmirror.com/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", + "resolved": "https://registry.npmmirror.com/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", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/json5": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "license": "MIT", @@ -4128,7 +4271,7 @@ }, "node_modules/jsx-ast-utils": { "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "resolved": "https://registry.npmmirror.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "license": "MIT", @@ -4144,7 +4287,7 @@ }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", @@ -4154,14 +4297,14 @@ }, "node_modules/language-subtag-registry": { "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "resolved": "https://registry.npmmirror.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "dev": true, "license": "CC0-1.0" }, "node_modules/language-tags": { "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "resolved": "https://registry.npmmirror.com/language-tags/-/language-tags-1.0.9.tgz", "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, "license": "MIT", @@ -4174,7 +4317,7 @@ }, "node_modules/levn": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", @@ -4188,7 +4331,7 @@ }, "node_modules/lilconfig": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "license": "MIT", @@ -4201,14 +4344,14 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true, "license": "MIT" }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", @@ -4224,14 +4367,14 @@ }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, "node_modules/longest-streak": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "resolved": "https://registry.npmmirror.com/longest-streak/-/longest-streak-3.1.0.tgz", "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", "license": "MIT", "funding": { @@ -4241,7 +4384,7 @@ }, "node_modules/loose-envify": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", "dependencies": { @@ -4253,14 +4396,14 @@ }, "node_modules/lru-cache": { "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, "license": "ISC" }, "node_modules/lucide-react": { "version": "0.400.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.400.0.tgz", + "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.400.0.tgz", "integrity": "sha512-rpp7pFHh3Xd93KHixNgB0SqThMHpYNzsGUu69UaQbSZ75Q/J3m5t6EhKyMT3m4w2WOxmJ2mY0tD3vebnXqQryQ==", "license": "ISC", "peerDependencies": { @@ -4269,7 +4412,7 @@ }, "node_modules/markdown-table": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "resolved": "https://registry.npmmirror.com/markdown-table/-/markdown-table-3.0.4.tgz", "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", "license": "MIT", "funding": { @@ -4279,7 +4422,7 @@ }, "node_modules/marked": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "resolved": "https://registry.npmmirror.com/marked/-/marked-14.0.0.tgz", "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", "license": "MIT", "peer": true, @@ -4292,7 +4435,7 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, "license": "MIT", @@ -4302,7 +4445,7 @@ }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", "license": "MIT", "dependencies": { @@ -4318,7 +4461,7 @@ }, "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "license": "MIT", "engines": { @@ -4329,9 +4472,9 @@ } }, "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -4354,7 +4497,7 @@ }, "node_modules/mdast-util-gfm": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", "license": "MIT", "dependencies": { @@ -4373,7 +4516,7 @@ }, "node_modules/mdast-util-gfm-autolink-literal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", "license": "MIT", "dependencies": { @@ -4390,7 +4533,7 @@ }, "node_modules/mdast-util-gfm-footnote": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", "license": "MIT", "dependencies": { @@ -4407,7 +4550,7 @@ }, "node_modules/mdast-util-gfm-strikethrough": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", "license": "MIT", "dependencies": { @@ -4422,7 +4565,7 @@ }, "node_modules/mdast-util-gfm-table": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", "license": "MIT", "dependencies": { @@ -4439,7 +4582,7 @@ }, "node_modules/mdast-util-gfm-task-list-item": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", "license": "MIT", "dependencies": { @@ -4455,7 +4598,7 @@ }, "node_modules/mdast-util-mdx-expression": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "license": "MIT", "dependencies": { @@ -4473,7 +4616,7 @@ }, "node_modules/mdast-util-mdx-jsx": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", "license": "MIT", "dependencies": { @@ -4497,7 +4640,7 @@ }, "node_modules/mdast-util-mdxjs-esm": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", "license": "MIT", "dependencies": { @@ -4515,7 +4658,7 @@ }, "node_modules/mdast-util-phrasing": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", "license": "MIT", "dependencies": { @@ -4529,7 +4672,7 @@ }, "node_modules/mdast-util-to-hast": { "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", "license": "MIT", "dependencies": { @@ -4550,7 +4693,7 @@ }, "node_modules/mdast-util-to-markdown": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", "license": "MIT", "dependencies": { @@ -4571,7 +4714,7 @@ }, "node_modules/mdast-util-to-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "resolved": "https://registry.npmmirror.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", "license": "MIT", "dependencies": { @@ -4584,7 +4727,7 @@ }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "license": "MIT", @@ -4594,7 +4737,7 @@ }, "node_modules/micromark": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "resolved": "https://registry.npmmirror.com/micromark/-/micromark-4.0.2.tgz", "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", "funding": [ { @@ -4629,7 +4772,7 @@ }, "node_modules/micromark-core-commonmark": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "resolved": "https://registry.npmmirror.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", "funding": [ { @@ -4663,7 +4806,7 @@ }, "node_modules/micromark-extension-gfm": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", "license": "MIT", "dependencies": { @@ -4683,7 +4826,7 @@ }, "node_modules/micromark-extension-gfm-autolink-literal": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", "license": "MIT", "dependencies": { @@ -4699,7 +4842,7 @@ }, "node_modules/micromark-extension-gfm-footnote": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", "license": "MIT", "dependencies": { @@ -4719,7 +4862,7 @@ }, "node_modules/micromark-extension-gfm-strikethrough": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", "license": "MIT", "dependencies": { @@ -4737,7 +4880,7 @@ }, "node_modules/micromark-extension-gfm-table": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", "license": "MIT", "dependencies": { @@ -4754,7 +4897,7 @@ }, "node_modules/micromark-extension-gfm-tagfilter": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", "license": "MIT", "dependencies": { @@ -4767,7 +4910,7 @@ }, "node_modules/micromark-extension-gfm-task-list-item": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", "license": "MIT", "dependencies": { @@ -4784,7 +4927,7 @@ }, "node_modules/micromark-factory-destination": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", "funding": [ { @@ -4805,7 +4948,7 @@ }, "node_modules/micromark-factory-label": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", "funding": [ { @@ -4827,7 +4970,7 @@ }, "node_modules/micromark-factory-space": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", "funding": [ { @@ -4847,7 +4990,7 @@ }, "node_modules/micromark-factory-title": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", "funding": [ { @@ -4869,7 +5012,7 @@ }, "node_modules/micromark-factory-whitespace": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", "funding": [ { @@ -4891,7 +5034,7 @@ }, "node_modules/micromark-util-character": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz", "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "funding": [ { @@ -4911,7 +5054,7 @@ }, "node_modules/micromark-util-chunked": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", "funding": [ { @@ -4930,7 +5073,7 @@ }, "node_modules/micromark-util-classify-character": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", "funding": [ { @@ -4951,7 +5094,7 @@ }, "node_modules/micromark-util-combine-extensions": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", "funding": [ { @@ -4971,7 +5114,7 @@ }, "node_modules/micromark-util-decode-numeric-character-reference": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", "funding": [ { @@ -4990,7 +5133,7 @@ }, "node_modules/micromark-util-decode-string": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", "funding": [ { @@ -5012,7 +5155,7 @@ }, "node_modules/micromark-util-encode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", "funding": [ { @@ -5028,7 +5171,7 @@ }, "node_modules/micromark-util-html-tag-name": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", "funding": [ { @@ -5044,7 +5187,7 @@ }, "node_modules/micromark-util-normalize-identifier": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", "funding": [ { @@ -5063,7 +5206,7 @@ }, "node_modules/micromark-util-resolve-all": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", "funding": [ { @@ -5082,7 +5225,7 @@ }, "node_modules/micromark-util-sanitize-uri": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", "funding": [ { @@ -5103,7 +5246,7 @@ }, "node_modules/micromark-util-subtokenize": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", "funding": [ { @@ -5125,7 +5268,7 @@ }, "node_modules/micromark-util-symbol": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "funding": [ { @@ -5141,7 +5284,7 @@ }, "node_modules/micromark-util-types": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "resolved": "https://registry.npmmirror.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz", "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", "funding": [ { @@ -5157,7 +5300,7 @@ }, "node_modules/micromatch": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", @@ -5170,9 +5313,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -5184,7 +5327,7 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "license": "MIT", @@ -5193,35 +5336,35 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "version": "7.1.3", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/monaco-editor": { - "version": "0.55.1", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz", - "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==", + "version": "0.56.0", + "resolved": "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.56.0.tgz", + "integrity": "sha512-sXboRm3BeBeLm938eaiyLMe0OxzfXIlZvbv4ir/jVgQy1zDhWjgmny0WoN45fuDKhCCQsYMbBJrv/A6jd8aCUg==", "license": "MIT", "peer": true, "dependencies": { - "dompurify": "3.2.7", + "dompurify": "3.4.8", "marked": "14.0.0" } }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/mz": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, "license": "MIT", @@ -5232,9 +5375,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.18", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "funding": [ { "type": "github", @@ -5251,7 +5394,7 @@ }, "node_modules/napi-postinstall": { "version": "0.3.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "resolved": "https://registry.npmmirror.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz", "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", "dev": true, "license": "MIT", @@ -5267,14 +5410,14 @@ }, "node_modules/natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/next": { "version": "14.2.35", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.35.tgz", + "resolved": "https://registry.npmmirror.com/next/-/next-14.2.35.tgz", "integrity": "sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==", "license": "MIT", "dependencies": { @@ -5324,7 +5467,7 @@ }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { @@ -5350,16 +5493,48 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "node_modules/node-exports-info": { + "version": "1.6.2", + "resolved": "https://registry.npmmirror.com/node-exports-info/-/node-exports-info-1.6.2.tgz", + "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-exports-info/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.54", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.54.tgz", + "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", @@ -5369,7 +5544,7 @@ }, "node_modules/object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "license": "MIT", @@ -5379,7 +5554,7 @@ }, "node_modules/object-hash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "dev": true, "license": "MIT", @@ -5389,7 +5564,7 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", @@ -5402,7 +5577,7 @@ }, "node_modules/object-keys": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "license": "MIT", @@ -5412,7 +5587,7 @@ }, "node_modules/object.assign": { "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz", "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "license": "MIT", @@ -5433,7 +5608,7 @@ }, "node_modules/object.entries": { "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "resolved": "https://registry.npmmirror.com/object.entries/-/object.entries-1.1.9.tgz", "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, "license": "MIT", @@ -5449,7 +5624,7 @@ }, "node_modules/object.fromentries": { "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "resolved": "https://registry.npmmirror.com/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "license": "MIT", @@ -5468,7 +5643,7 @@ }, "node_modules/object.groupby": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "resolved": "https://registry.npmmirror.com/object.groupby/-/object.groupby-1.0.3.tgz", "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "license": "MIT", @@ -5483,7 +5658,7 @@ }, "node_modules/object.values": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "resolved": "https://registry.npmmirror.com/object.values/-/object.values-1.2.1.tgz", "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "license": "MIT", @@ -5502,7 +5677,7 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "license": "ISC", @@ -5512,7 +5687,7 @@ }, "node_modules/optionator": { "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", @@ -5529,13 +5704,14 @@ } }, "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.6", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" }, @@ -5548,7 +5724,7 @@ }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", @@ -5564,7 +5740,7 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", @@ -5580,7 +5756,7 @@ }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", @@ -5593,7 +5769,7 @@ }, "node_modules/parse-entities": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "resolved": "https://registry.npmmirror.com/parse-entities/-/parse-entities-4.0.2.tgz", "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", "license": "MIT", "dependencies": { @@ -5612,13 +5788,13 @@ }, "node_modules/parse-entities/node_modules/@types/unist": { "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-2.0.11.tgz", "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", @@ -5628,7 +5804,7 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", @@ -5638,7 +5814,7 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", @@ -5648,14 +5824,14 @@ }, "node_modules/path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", @@ -5672,14 +5848,14 @@ }, "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -5689,19 +5865,9 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pirates": { "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, "license": "MIT", @@ -5711,7 +5877,7 @@ }, "node_modules/possible-typed-array-names": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "license": "MIT", @@ -5720,9 +5886,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.28", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { @@ -5740,7 +5906,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -5750,7 +5916,7 @@ }, "node_modules/postcss-import": { "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "dev": true, "license": "MIT", @@ -5766,9 +5932,31 @@ "postcss": "^8.0.0" } }, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/postcss-js": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.1.0.tgz", "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", "dev": true, "funding": [ @@ -5794,7 +5982,7 @@ }, "node_modules/postcss-load-config": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz", "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", "dev": true, "funding": [ @@ -5837,7 +6025,7 @@ }, "node_modules/postcss-nested": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.2.0.tgz", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", "dev": true, "funding": [ @@ -5862,9 +6050,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "6.1.4", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5877,14 +6065,14 @@ }, "node_modules/postcss-value-parser": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true, "license": "MIT" }, "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", @@ -5894,7 +6082,7 @@ }, "node_modules/prop-types": { "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dev": true, "license": "MIT", @@ -5905,9 +6093,9 @@ } }, "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", "license": "MIT", "funding": { "type": "github", @@ -5916,7 +6104,7 @@ }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", @@ -5926,7 +6114,7 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ @@ -5947,7 +6135,7 @@ }, "node_modules/react": { "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "resolved": "https://registry.npmmirror.com/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", "dependencies": { @@ -5959,7 +6147,7 @@ }, "node_modules/react-dom": { "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", "dependencies": { @@ -5972,14 +6160,14 @@ }, "node_modules/react-is": { "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true, "license": "MIT" }, "node_modules/react-markdown": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz", + "resolved": "https://registry.npmmirror.com/react-markdown/-/react-markdown-9.1.0.tgz", "integrity": "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==", "license": "MIT", "dependencies": { @@ -6005,18 +6193,15 @@ } }, "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.2.tgz", + "integrity": "sha512-/peqiBB/n07gQGLsWaHho3WfvUyRscw0gYTsEFMhrIe/nWLkYaf5SbKYjGYqtRV3aPwykJgF2VEMo1ac4bnsGA==", "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } + "license": "MIT" }, "node_modules/readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", @@ -6029,7 +6214,7 @@ }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, "license": "MIT", @@ -6052,7 +6237,7 @@ }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", @@ -6073,7 +6258,7 @@ }, "node_modules/remark-gfm": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "resolved": "https://registry.npmmirror.com/remark-gfm/-/remark-gfm-4.0.1.tgz", "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", "license": "MIT", "dependencies": { @@ -6091,7 +6276,7 @@ }, "node_modules/remark-parse": { "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "resolved": "https://registry.npmmirror.com/remark-parse/-/remark-parse-11.0.0.tgz", "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", "license": "MIT", "dependencies": { @@ -6107,7 +6292,7 @@ }, "node_modules/remark-rehype": { "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "resolved": "https://registry.npmmirror.com/remark-rehype/-/remark-rehype-11.1.2.tgz", "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", "license": "MIT", "dependencies": { @@ -6124,7 +6309,7 @@ }, "node_modules/remark-stringify": { "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "resolved": "https://registry.npmmirror.com/remark-stringify/-/remark-stringify-11.0.0.tgz", "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", "license": "MIT", "dependencies": { @@ -6138,13 +6323,16 @@ } }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "2.0.0-next.7", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-2.0.0-next.7.tgz", + "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.1", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -6160,7 +6348,7 @@ }, "node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", @@ -6170,7 +6358,7 @@ }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "resolved": "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", @@ -6180,7 +6368,7 @@ }, "node_modules/reusify": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", @@ -6191,7 +6379,7 @@ }, "node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, @@ -6208,9 +6396,9 @@ }, "node_modules/rimraf/node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -6230,7 +6418,7 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ @@ -6253,15 +6441,15 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, @@ -6274,7 +6462,7 @@ }, "node_modules/safe-push-apply": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz", "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, "license": "MIT", @@ -6291,7 +6479,7 @@ }, "node_modules/safe-regex-test": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "license": "MIT", @@ -6309,7 +6497,7 @@ }, "node_modules/scheduler": { "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", "dependencies": { @@ -6317,9 +6505,9 @@ } }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -6331,7 +6519,7 @@ }, "node_modules/set-function-length": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "license": "MIT", @@ -6349,7 +6537,7 @@ }, "node_modules/set-function-name": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "resolved": "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "license": "MIT", @@ -6365,7 +6553,7 @@ }, "node_modules/set-proto": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "resolved": "https://registry.npmmirror.com/set-proto/-/set-proto-1.0.0.tgz", "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, "license": "MIT", @@ -6380,7 +6568,7 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", @@ -6393,7 +6581,7 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", @@ -6402,15 +6590,15 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -6422,14 +6610,14 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -6440,7 +6628,7 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, "license": "MIT", @@ -6459,7 +6647,7 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, "license": "MIT", @@ -6479,7 +6667,7 @@ }, "node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", @@ -6492,7 +6680,7 @@ }, "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { @@ -6501,7 +6689,7 @@ }, "node_modules/space-separated-tokens": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "resolved": "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", "license": "MIT", "funding": { @@ -6511,20 +6699,20 @@ }, "node_modules/stable-hash": { "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "resolved": "https://registry.npmmirror.com/stable-hash/-/stable-hash-0.0.5.tgz", "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", "dev": true, "license": "MIT" }, "node_modules/state-local": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "resolved": "https://registry.npmmirror.com/state-local/-/state-local-1.0.7.tgz", "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", "license": "MIT" }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, "license": "MIT", @@ -6538,7 +6726,7 @@ }, "node_modules/streamsearch": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz", "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "engines": { "node": ">=10.0.0" @@ -6546,7 +6734,7 @@ }, "node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", @@ -6565,7 +6753,7 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", @@ -6580,15 +6768,15 @@ }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "dev": true, "license": "MIT", "engines": { @@ -6599,13 +6787,13 @@ } }, "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -6616,7 +6804,7 @@ }, "node_modules/string.prototype.includes": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "resolved": "https://registry.npmmirror.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, "license": "MIT", @@ -6630,25 +6818,25 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/string.prototype.matchall/-/string.prototype.matchall-4.1.0.tgz", + "integrity": "sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", + "es-abstract": "^1.24.2", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", + "es-object-atoms": "^1.1.2", + "get-intrinsic": "^1.3.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" + "side-channel": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -6659,7 +6847,7 @@ }, "node_modules/string.prototype.repeat": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "resolved": "https://registry.npmmirror.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, "license": "MIT", @@ -6669,19 +6857,20 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "version": "1.2.11", + "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -6691,16 +6880,16 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.2" }, "engines": { "node": ">= 0.4" @@ -6711,7 +6900,7 @@ }, "node_modules/string.prototype.trimstart": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "license": "MIT", @@ -6729,7 +6918,7 @@ }, "node_modules/stringify-entities": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "resolved": "https://registry.npmmirror.com/stringify-entities/-/stringify-entities-4.0.4.tgz", "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", "license": "MIT", "dependencies": { @@ -6743,7 +6932,7 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", @@ -6757,7 +6946,7 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", @@ -6770,7 +6959,7 @@ }, "node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "resolved": "https://registry.npmmirror.com/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", @@ -6780,7 +6969,7 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", @@ -6793,7 +6982,7 @@ }, "node_modules/style-to-js": { "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "resolved": "https://registry.npmmirror.com/style-to-js/-/style-to-js-1.1.21.tgz", "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", "license": "MIT", "dependencies": { @@ -6802,7 +6991,7 @@ }, "node_modules/style-to-object": { "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "resolved": "https://registry.npmmirror.com/style-to-object/-/style-to-object-1.0.14.tgz", "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", "license": "MIT", "dependencies": { @@ -6811,7 +7000,7 @@ }, "node_modules/styled-jsx": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "resolved": "https://registry.npmmirror.com/styled-jsx/-/styled-jsx-5.1.1.tgz", "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", "license": "MIT", "dependencies": { @@ -6834,7 +7023,7 @@ }, "node_modules/sucrase": { "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.1.tgz", "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", "dev": true, "license": "MIT", @@ -6857,7 +7046,7 @@ }, "node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", @@ -6870,7 +7059,7 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", @@ -6883,7 +7072,7 @@ }, "node_modules/tailwindcss": { "version": "3.4.19", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.19.tgz", "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "dev": true, "license": "MIT", @@ -6919,16 +7108,38 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/text-table": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true, "license": "MIT" }, "node_modules/thenify": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "dev": true, "license": "MIT", @@ -6938,7 +7149,7 @@ }, "node_modules/thenify-all": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, "license": "MIT", @@ -6950,14 +7161,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.17", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -6968,7 +7179,7 @@ }, "node_modules/tinyglobby/node_modules/fdir": { "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", @@ -6985,9 +7196,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -6999,7 +7210,7 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", @@ -7012,7 +7223,7 @@ }, "node_modules/trim-lines": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "resolved": "https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz", "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", "license": "MIT", "funding": { @@ -7022,7 +7233,7 @@ }, "node_modules/trough": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "resolved": "https://registry.npmmirror.com/trough/-/trough-2.2.0.tgz", "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", "license": "MIT", "funding": { @@ -7031,9 +7242,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -7045,14 +7256,14 @@ }, "node_modules/ts-interface-checker": { "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true, "license": "Apache-2.0" }, "node_modules/tsconfig-paths": { "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "resolved": "https://registry.npmmirror.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "license": "MIT", @@ -7065,13 +7276,13 @@ }, "node_modules/tslib": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "resolved": "https://registry.npmmirror.com/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", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", @@ -7084,7 +7295,7 @@ }, "node_modules/type-fest": { "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", @@ -7097,7 +7308,7 @@ }, "node_modules/typed-array-buffer": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", @@ -7112,7 +7323,7 @@ }, "node_modules/typed-array-byte-length": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "resolved": "https://registry.npmmirror.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "license": "MIT", @@ -7132,7 +7343,7 @@ }, "node_modules/typed-array-byte-offset": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "resolved": "https://registry.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "license": "MIT", @@ -7153,18 +7364,18 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" }, "engines": { "node": ">= 0.4" @@ -7175,7 +7386,7 @@ }, "node_modules/typescript": { "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", @@ -7189,7 +7400,7 @@ }, "node_modules/unbox-primitive": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz", "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", @@ -7208,14 +7419,14 @@ }, "node_modules/undici-types": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, "node_modules/unified": { "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "resolved": "https://registry.npmmirror.com/unified/-/unified-11.0.5.tgz", "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", "license": "MIT", "dependencies": { @@ -7234,7 +7445,7 @@ }, "node_modules/unist-util-is": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.1.tgz", "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", "license": "MIT", "dependencies": { @@ -7247,7 +7458,7 @@ }, "node_modules/unist-util-position": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "resolved": "https://registry.npmmirror.com/unist-util-position/-/unist-util-position-5.0.0.tgz", "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", "license": "MIT", "dependencies": { @@ -7260,7 +7471,7 @@ }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "resolved": "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "license": "MIT", "dependencies": { @@ -7273,7 +7484,7 @@ }, "node_modules/unist-util-visit": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "resolved": "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz", "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", "license": "MIT", "dependencies": { @@ -7288,7 +7499,7 @@ }, "node_modules/unist-util-visit-parents": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "resolved": "https://registry.npmmirror.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", "license": "MIT", "dependencies": { @@ -7301,44 +7512,47 @@ } }, "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "napi-postinstall": "^0.3.0" + "napi-postinstall": "^0.3.4" }, "funding": { "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" } }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", "dev": true, "funding": [ { @@ -7368,7 +7582,7 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", @@ -7378,14 +7592,14 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, "license": "MIT" }, "node_modules/vfile": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "resolved": "https://registry.npmmirror.com/vfile/-/vfile-6.0.3.tgz", "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "license": "MIT", "dependencies": { @@ -7399,7 +7613,7 @@ }, "node_modules/vfile-message": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "resolved": "https://registry.npmmirror.com/vfile-message/-/vfile-message-4.0.3.tgz", "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", "license": "MIT", "dependencies": { @@ -7413,7 +7627,7 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", @@ -7429,7 +7643,7 @@ }, "node_modules/which-boxed-primitive": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, "license": "MIT", @@ -7449,7 +7663,7 @@ }, "node_modules/which-builtin-type": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "resolved": "https://registry.npmmirror.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz", "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, "license": "MIT", @@ -7477,7 +7691,7 @@ }, "node_modules/which-collection": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "license": "MIT", @@ -7495,14 +7709,14 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "version": "1.1.22", + "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", + "call-bind": "^1.0.9", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", @@ -7518,7 +7732,7 @@ }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", @@ -7528,7 +7742,7 @@ }, "node_modules/wrap-ansi": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "license": "MIT", @@ -7547,7 +7761,7 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", @@ -7565,14 +7779,14 @@ }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", @@ -7586,9 +7800,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "dev": true, "license": "MIT", "engines": { @@ -7600,7 +7814,7 @@ }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", @@ -7612,13 +7826,13 @@ } }, "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -7629,14 +7843,14 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "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", + "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", @@ -7649,7 +7863,7 @@ }, "node_modules/zwitch": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz", "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", "license": "MIT", "funding": { diff --git a/package.json b/package.json index 8058fb6..ac76ee7 100644 --- a/package.json +++ b/package.json @@ -9,24 +9,24 @@ "lint": "next lint" }, "dependencies": { - "next": "^14.2.0", - "react": "^18.3.0", - "react-dom": "^18.3.0", - "react-markdown": "^9.0.0", - "remark-gfm": "^4.0.0", "@monaco-editor/react": "^4.6.0", + "clsx": "^2.1.0", "lucide-react": "^0.400.0", - "clsx": "^2.1.0" + "next": "^14.1.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-markdown": "^9.0.0", + "remark-gfm": "^4.0.0" }, "devDependencies": { "@types/node": "^20.0.0", "@types/react": "^18.3.0", "@types/react-dom": "^18.3.0", - "typescript": "^5.4.0", - "tailwindcss": "^3.4.0", - "postcss": "^8.4.0", "autoprefixer": "^10.4.0", "eslint": "^8.0.0", - "eslint-config-next": "^14.2.0" + "eslint-config-next": "^14.2.0", + "postcss": "^8.4.0", + "tailwindcss": "^3.4.0", + "typescript": "^5.4.0" } } diff --git a/src/app/globals.css b/src/app/globals.css index 03529ec..eb8426c 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -11,7 +11,8 @@ --accent-orange: #ff6723; } -html, body { height: 100%; overflow: hidden; } +html { height: 100%; overflow-y: auto; } +body { min-height: 100%; } body { color: var(--text-primary); @@ -50,10 +51,20 @@ body { } /* ── Scrollbar ──────────────────────────────────────── */ -::-webkit-scrollbar { width: 5px; height: 5px; } -::-webkit-scrollbar-track { background: transparent; } -::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.10); border-radius: 10px; } -::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.18); } +::-webkit-scrollbar { width: 14px; height: 14px; } +::-webkit-scrollbar-track { background: #e8ecf2; border-radius: 10px; } +::-webkit-scrollbar-thumb { + background: #8796ad; + border: 3px solid #e8ecf2; + border-radius: 10px; + min-height: 40px; +} +::-webkit-scrollbar-thumb:hover { background: #596f91; } +::-webkit-scrollbar-thumb:active { background: var(--accent); } + +@supports not selector(::-webkit-scrollbar) { + * { scrollbar-width: auto; scrollbar-color: #8796ad #e8ecf2; } +} /* ── Scroll shadow overlay (top/bottom) ─────────────── */ .scroll-shadow { diff --git a/src/app/historical-data/page.tsx b/src/app/historical-data/page.tsx new file mode 100644 index 0000000..5cd64c9 --- /dev/null +++ b/src/app/historical-data/page.tsx @@ -0,0 +1,370 @@ +"use client"; + +import { useState } from "react"; +import { Database, Search, Filter, Calendar, TrendingUp, CheckCircle, XCircle, AlertCircle } from "lucide-react"; + +interface HistoricalCase { + case_id: string; + customer_name: string; + project_type: string; + amount: number; + debt_ratio: number; + credit_query_count: number; + litigation_count: number; + decision: string; + decision_date: string; + processing_days: number; + mitigation_measures: string; + final_outcome: string; + risk_level: "high" | "medium" | "low"; +} + +export default function HistoricalDataPage() { + const [searchTerm, setSearchTerm] = useState(""); + const [selectedDecision, setSelectedDecision] = useState("all"); + const [selectedRiskLevel, setSelectedRiskLevel] = useState("all"); + const [dateRange, setDateRange] = useState("12"); + + // 模拟历史案例数据 + const [cases] = useState([ + { + case_id: "CAS-2025-08-156", + customer_name: "华东光伏设备公司", + project_type: "新能源设备", + amount: 2.8, + debt_ratio: 0.68, + credit_query_count: 7, + litigation_count: 1, + decision: "降额通过", + decision_date: "2025-08-15", + processing_days: 4, + mitigation_measures: "降低授信额度15%,增加法人个人担保,提供政府补贴承诺函", + final_outcome: "已放款,回款正常", + risk_level: "medium" + }, + { + case_id: "CAS-2025-07-089", + customer_name: "绿能公交运营公司", + project_type: "对公新能源", + amount: 3.5, + debt_ratio: 0.72, + credit_query_count: 9, + litigation_count: 0, + decision: "降额通过", + decision_date: "2025-07-22", + processing_days: 6, + mitigation_measures: "降低授信额度12%,调整还款期限,追加车辆抵押", + final_outcome: "已放款,回款正常", + risk_level: "high" + }, + { + case_id: "CAS-2025-06-234", + customer_name: "中部新能源投资", + project_type: "新能源投资", + amount: 4.2, + debt_ratio: 0.65, + credit_query_count: 6, + litigation_count: 2, + decision: "附加条件通过", + decision_date: "2025-06-18", + processing_days: 5, + mitigation_measures: "要求季度现金流报告,设置财务预警指标,增加母公司担保", + final_outcome: "已放款,回款正常", + risk_level: "high" + }, + { + case_id: "CAS-2025-08-201", + customer_name: "南方物流运输公司", + project_type: "物流运输", + amount: 1.8, + debt_ratio: 0.58, + credit_query_count: 4, + litigation_count: 0, + decision: "直接通过", + decision_date: "2025-08-28", + processing_days: 2, + mitigation_measures: "无需额外措施", + final_outcome: "已放款,回款正常", + risk_level: "low" + }, + { + case_id: "CAS-2025-07-145", + customer_name: "东部制造业集团", + project_type: "设备制造", + amount: 5.5, + debt_ratio: 0.75, + credit_query_count: 12, + litigation_count: 3, + decision: "退回", + decision_date: "2025-07-10", + processing_days: 8, + mitigation_measures: "要求补充材料后重新申请", + final_outcome: "客户未重新申请", + risk_level: "high" + }, + { + case_id: "CAS-2025-06-098", + customer_name: "西部能源开发公司", + project_type: "能源开发", + amount: 6.0, + debt_ratio: 0.62, + credit_query_count: 5, + litigation_count: 1, + decision: "附加条件通过", + decision_date: "2025-06-05", + processing_days: 7, + mitigation_measures: "增加土地使用权抵押,提供项目收益权质押", + final_outcome: "已放款,回款正常", + risk_level: "medium" + }, + { + case_id: "CAS-2025-08-178", + customer_name: "北方建筑工程公司", + project_type: "建筑工程", + amount: 3.2, + debt_ratio: 0.70, + credit_query_count: 8, + litigation_count: 2, + decision: "降额通过", + decision_date: "2025-08-20", + processing_days: 5, + mitigation_measures: "降低授信额度20%,要求项目方连带担保", + final_outcome: "已放款,回款正常", + risk_level: "high" + }, + { + case_id: "CAS-2025-07-167", + customer_name: "中部科技创新公司", + project_type: "科技研发", + amount: 2.0, + debt_ratio: 0.55, + credit_query_count: 3, + litigation_count: 0, + decision: "直接通过", + decision_date: "2025-07-28", + processing_days: 3, + mitigation_measures: "无需额外措施", + final_outcome: "已放款,回款正常", + risk_level: "low" + } + ]); + + const decisions = ["all", "直接通过", "降额通过", "附加条件通过", "退回"]; + const riskLevels = ["all", "high", "medium", "low"]; + + const filteredCases = cases.filter(c => { + const matchesSearch = c.customer_name.toLowerCase().includes(searchTerm.toLowerCase()) || + c.case_id.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesDecision = selectedDecision === "all" || c.decision === selectedDecision; + const matchesRisk = selectedRiskLevel === "all" || c.risk_level === selectedRiskLevel; + return matchesSearch && matchesDecision && matchesRisk; + }); + + // 统计数据 + const stats = { + total: cases.length, + approved: cases.filter(c => c.decision.includes("通过")).length, + rejected: cases.filter(c => c.decision === "退回").length, + avgProcessingDays: (cases.reduce((sum, c) => sum + c.processing_days, 0) / cases.length).toFixed(1) + }; + + const getDecisionBadge = (decision: string) => { + if (decision.includes("直接通过")) { + return {decision}; + } else if (decision.includes("通过")) { + return {decision}; + } else if (decision === "退回") { + return {decision}; + } + return {decision}; + }; + + const getRiskBadge = (level: string) => { + switch (level) { + case "high": return 高风险; + case "medium": return 中风险; + case "low": return 低风险; + default: return null; + } + }; + + return ( +
+ {/* Header */} +
+
+
+

+ 💾 历史数据分析 +

+

案例数据查询与分析(轨道2:数据驱动)

+
+
+
+ + {/* 统计卡片 */} +
+
+
{stats.total}
+
历史案例总数
+
+
+
{stats.approved}
+
通过案例
+
+
+
{stats.rejected}
+
退回案例
+
+
+
{stats.avgProcessingDays}
+
平均处理天数
+
+
+ + {/* 搜索和筛选 */} +
+
+ {/* 搜索框 */} +
+ + setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-blue-500" + /> +
+ + {/* 筛选器 */} +
+ {/* 决策筛选 */} +
+ 决策结果: +
+ {decisions.map(dec => ( + + ))} +
+
+ + {/* 风险等级筛选 */} +
+ 风险等级: +
+ {riskLevels.map(level => ( + + ))} +
+
+ + {/* 时间范围 */} +
+ + +
+
+ +
+ 找到 {filteredCases.length} 个匹配案例 +
+
+
+ + {/* 案例列表 */} +
+ {filteredCases.length === 0 ? ( +
+ 未找到匹配的案例 +
+ ) : ( + filteredCases.map(case_ => ( +
+ {/* 标题行 */} +
+
+
+ {case_.case_id} + {getDecisionBadge(case_.decision)} + {getRiskBadge(case_.risk_level)} +
+

{case_.customer_name}

+

{case_.project_type}

+
+
+
{case_.amount.toFixed(1)}亿
+
{case_.decision_date}
+
+
+ + {/* 关键指标 */} +
+
+
资产负债率
+
{(case_.debt_ratio * 100).toFixed(0)}%
+
+
+
征信查询
+
{case_.credit_query_count}次
+
+
+
涉诉记录
+
{case_.litigation_count}起
+
+
+
处理时长
+
{case_.processing_days}天
+
+
+ + {/* 风控措施 */} +
+
风控措施
+
{case_.mitigation_measures}
+
+ + {/* 最终结果 */} +
+
最终结果
+
{case_.final_outcome}
+
+
+ )) + )} +
+
+ ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 306a081..ab245f2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -41,7 +41,7 @@ function MainLayout() { {/* Content area — flexbox layout with resizable panels */} -
+
{/* Left sidebar */}
; + exclusions: Array<{ + clause: string; + risk_level: "high" | "medium" | "low"; + description: string; + }>; + overall_result: "compliant" | "non_compliant" | "needs_review"; + recommendations: string[]; +} + +interface ReportTemplate { + id: string; + name: string; + description: string; + sections: string[]; +} + +export default function PolicyVerificationPage() { + const [activeTab, setActiveTab] = useState<"upload" | "results" | "templates">("upload"); + const [selectedFile, setSelectedFile] = useState(null); + const [selectedTemplate, setSelectedTemplate] = useState("TPL-POLICY-001"); + + const [checkForm, setCheckForm] = useState({ + customer_name: "远东新能源公交公司", + lease_amount: 8000, + project_type: "新能源设备" + }); + + const [loading, setLoading] = useState(false); + const [checkResult, setCheckResult] = useState(null); + const [generatedReport, setGeneratedReport] = useState(null); + + // 报告模板 + const [templates] = useState([ + { + id: "TPL-POLICY-001", + name: "标准保单核验报告", + description: "适用于常规设备和车辆的保单核验", + sections: ["保单基本信息", "合规性检查", "除外责任分析", "风险评估", "核验结论", "整改建议"] + }, + { + id: "TPL-POLICY-002", + name: "新能源专项保单核验", + description: "针对新能源设备的专项核验报告", + sections: ["保单基本信息", "新能源特殊险种", "合规性检查", "政策补贴覆盖", "除外责任分析", "核验结论"] + }, + { + id: "TPL-POLICY-003", + name: "大额设备保单核验", + description: "5000万以上大额设备的详细核验", + sections: ["保单基本信息", "保额充足性分析", "多保险公司协同", "合规性检查", "除外责任分析", "风险评估", "核验结论"] + }, + { + id: "TPL-POLICY-004", + name: "快速核验简报", + description: "低风险项目的快速核验简报", + sections: ["保单基本信息", "合规性检查", "核验结论"] + } + ]); + + const handleFileChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files[0]) { + setSelectedFile(e.target.files[0]); + } + }; + + const handlePolicyCheck = async () => { + if (!selectedFile) { + alert("请先上传保单PDF文件"); + return; + } + + setLoading(true); + setCheckResult(null); + setGeneratedReport(null); + + try { + // 模拟OCR识别和保单核验 + await new Promise(resolve => setTimeout(resolve, 2500)); + + const mockResult: PolicyCheckResult = { + policy_id: "POL-2026-001", + customer_name: checkForm.customer_name, + check_timestamp: new Date().toLocaleString('zh-CN'), + policy_info: { + insurance_company: "中国人民财产保险股份有限公司", + policy_number: "PICC-FZL-20260801-0156", + coverage_types: ["设备损失险", "盗抢险", "第三者责任险", "营业中断险"], + coverage_amount: 9600, + lease_amount: checkForm.lease_amount, + coverage_ratio: (9600 / checkForm.lease_amount) * 100 + }, + compliance_items: [ + { + category: "保额覆盖", + item: "保额 ≥ 授信金额 × 120%", + status: "pass", + detail: `保额9600万元 ≥ 授信${checkForm.lease_amount}万元 × 120% = ${(checkForm.lease_amount * 1.2).toFixed(0)}万元` + }, + { + category: "险种完整性", + item: "必备险种齐全", + status: "pass", + detail: "设备损失险、盗抢险、第三者责任险均已投保" + }, + { + category: "保险期限", + item: "保险期限覆盖租赁期", + status: "pass", + detail: "保险期限36个月,租赁期36个月,完全覆盖" + }, + { + category: "第一受益人", + item: "兴邦金租为第一受益人", + status: "pass", + detail: "保单明确载明:第一受益人为兴邦融资租赁有限公司" + }, + { + category: "除外责任", + item: "高频风险不得除外", + status: "fail", + detail: "保单除外责任包含「泡水」和「自然灾害」,属于高频风险,不应除外" + }, + { + category: "保费支付", + item: "保费按期支付证明", + status: "warning", + detail: "仅提供首期保费缴纳凭证,后续保费需持续跟踪" + } + ], + exclusions: [ + { + clause: "因泡水、浸水导致的设备损失", + risk_level: "high", + description: "极端天气下泡水风险较高,尤其是户外设备。此条款将导致泡水事故无法理赔,风险敞口较大。" + }, + { + clause: "因自然灾害(台风、暴雨、洪水)导致的损失", + risk_level: "high", + description: "自然灾害属于不可抗力但发生概率较高,此条款将导致重大自然灾害损失无法理赔。" + }, + { + clause: "战争、核辐射、恐怖袭击", + risk_level: "low", + description: "此类风险发生概率极低,属于常规除外责任,可接受。" + }, + { + clause: "故意损坏、违法使用", + risk_level: "low", + description: "此类风险属于人为因素,常规除外责任,可接受。" + } + ], + overall_result: "non_compliant", + recommendations: [ + "【必须整改】要求保险公司删除「泡水」除外责任条款,或增加泡水附加险", + "【必须整改】要求保险公司删除「自然灾害」除外责任条款,或增加自然灾害附加险", + "【建议跟踪】建立保费缴纳监控机制,每期保费到期前提醒客户缴纳", + "【风险提示】如客户无法调整保单条款,建议在合同中明确:保单除外责任导致的损失由客户承担" + ] + }; + + setCheckResult(mockResult); + setActiveTab("results"); + } catch (error) { + alert("保单核验失败"); + } finally { + setLoading(false); + } + }; + + const handleGenerateReport = () => { + if (!checkResult) return; + + const template = templates.find(t => t.id === selectedTemplate)!; + let reportContent = ""; + + reportContent += `# ${checkResult.customer_name} - 保单合规核验报告\n\n`; + reportContent += `**报告模板**: ${template.name}\n`; + reportContent += `**核验时间**: ${checkResult.check_timestamp}\n`; + reportContent += `**保单编号**: ${checkResult.policy_info.policy_number}\n`; + reportContent += `**核验结果**: ${checkResult.overall_result === "compliant" ? "✅ 合规" : checkResult.overall_result === "non_compliant" ? "❌ 不合规" : "⚠️ 需要复核"}\n\n`; + reportContent += `---\n\n`; + + template.sections.forEach(section => { + switch (section) { + case "保单基本信息": + reportContent += `## 一、保单基本信息\n\n`; + reportContent += `| 项目 | 内容 |\n|------|------|\n`; + reportContent += `| 保险公司 | ${checkResult.policy_info.insurance_company} |\n`; + reportContent += `| 保单号 | ${checkResult.policy_info.policy_number} |\n`; + reportContent += `| 投保险种 | ${checkResult.policy_info.coverage_types.join("、")} |\n`; + reportContent += `| 保险金额 | ${checkResult.policy_info.coverage_amount}万元 |\n`; + reportContent += `| 授信金额 | ${checkResult.policy_info.lease_amount}万元 |\n`; + reportContent += `| 覆盖比例 | ${checkResult.policy_info.coverage_ratio.toFixed(1)}% |\n\n`; + break; + + case "合规性检查": + reportContent += `## 二、合规性检查\n\n`; + checkResult.compliance_items.forEach((item, idx) => { + const icon = item.status === "pass" ? "✅" : item.status === "fail" ? "❌" : "⚠️"; + reportContent += `### ${idx + 1}. ${icon} ${item.item}\n\n`; + reportContent += `**检查类别**: ${item.category}\n\n`; + reportContent += `**检查结果**: ${item.status === "pass" ? "通过" : item.status === "fail" ? "不通过" : "需关注"}\n\n`; + reportContent += `**详细说明**: ${item.detail}\n\n`; + }); + break; + + case "除外责任分析": + reportContent += `## 三、除外责任条款分析\n\n`; + const highRiskExclusions = checkResult.exclusions.filter(e => e.risk_level === "high"); + const mediumRiskExclusions = checkResult.exclusions.filter(e => e.risk_level === "medium"); + const lowRiskExclusions = checkResult.exclusions.filter(e => e.risk_level === "low"); + + if (highRiskExclusions.length > 0) { + reportContent += `### 🔴 高风险除外责任(${highRiskExclusions.length}项)\n\n`; + highRiskExclusions.forEach((exc, idx) => { + reportContent += `**${idx + 1}. ${exc.clause}**\n\n`; + reportContent += `${exc.description}\n\n`; + }); + } + + if (mediumRiskExclusions.length > 0) { + reportContent += `### 🟡 中风险除外责任(${mediumRiskExclusions.length}项)\n\n`; + mediumRiskExclusions.forEach((exc, idx) => { + reportContent += `**${idx + 1}. ${exc.clause}**\n\n`; + reportContent += `${exc.description}\n\n`; + }); + } + + if (lowRiskExclusions.length > 0) { + reportContent += `### 🟢 低风险除外责任(${lowRiskExclusions.length}项,可接受)\n\n`; + lowRiskExclusions.forEach((exc, idx) => { + reportContent += `- ${exc.clause}\n`; + }); + reportContent += `\n`; + } + break; + + case "风险评估": + reportContent += `## 四、风险评估\n\n`; + const failedItems = checkResult.compliance_items.filter(i => i.status === "fail"); + const warningItems = checkResult.compliance_items.filter(i => i.status === "warning"); + reportContent += `**不合规项**: ${failedItems.length}项\n\n`; + reportContent += `**关注项**: ${warningItems.length}项\n\n`; + reportContent += `**高风险除外**: ${checkResult.exclusions.filter(e => e.risk_level === "high").length}项\n\n`; + reportContent += `**综合评估**: ${checkResult.overall_result === "compliant" ? "保单符合公司要求,可以接受" : checkResult.overall_result === "non_compliant" ? "保单存在重大不合规项,建议要求客户整改" : "保单基本合规,但存在需要关注的项目"}\n\n`; + break; + + case "核验结论": + reportContent += `## ${template.sections.indexOf(section) + 1}. 核验结论\n\n`; + if (checkResult.overall_result === "compliant") { + reportContent += `✅ **结论**: 保单合规,符合兴邦金租保单管理要求\n\n`; + } else if (checkResult.overall_result === "non_compliant") { + reportContent += `❌ **结论**: 保单不合规,存在以下重大问题:\n\n`; + checkResult.compliance_items.filter(i => i.status === "fail").forEach(item => { + reportContent += `- ${item.item}: ${item.detail}\n`; + }); + reportContent += `\n`; + } else { + reportContent += `⚠️ **结论**: 保单基本合规,但需要关注以下事项\n\n`; + } + break; + + case "整改建议": + case "核验建议": + reportContent += `## ${template.sections.indexOf(section) + 1}. 整改建议\n\n`; + checkResult.recommendations.forEach((rec, idx) => { + reportContent += `${idx + 1}. ${rec}\n\n`; + }); + break; + + case "新能源特殊险种": + reportContent += `## 二、新能源特殊险种核验\n\n`; + reportContent += `- 新能源设备特殊险种覆盖情况\n`; + reportContent += `- 电池、充电设施专项保险\n`; + reportContent += `- 技术更新换代风险覆盖\n\n`; + break; + + case "政策补贴覆盖": + reportContent += `## 三、政策补贴覆盖情况\n\n`; + reportContent += `- 政府补贴延迟风险保险\n`; + reportContent += `- 补贴政策变更风险覆盖\n\n`; + break; + + case "保额充足性分析": + reportContent += `## 二、保额充足性深度分析\n\n`; + reportContent += `- 大额设备分段保险情况\n`; + reportContent += `- 累计风险敞口分析\n`; + reportContent += `- 保额动态调整机制\n\n`; + break; + + case "多保险公司协同": + reportContent += `## 三、多保险公司协同核验\n\n`; + reportContent += `- 多家保险公司分担情况\n`; + reportContent += `- 主次保险责任划分\n`; + reportContent += `- 理赔协调机制\n\n`; + break; + } + }); + + reportContent += `---\n\n`; + reportContent += `**核验人**: AI智能核验系统\n\n`; + reportContent += `**复核人**: [待人工复核]\n\n`; + reportContent += `**核验日期**: ${checkResult.check_timestamp}\n`; + + setGeneratedReport(reportContent); + }; + + const getStatusBadge = (status: string) => { + switch (status) { + case "pass": + return 通过; + case "fail": + return 不通过; + case "warning": + return 需关注; + default: + return null; + } + }; + + const getRiskBadge = (level: string) => { + switch (level) { + case "high": + return 高风险; + case "medium": + return 中风险; + case "low": + return 低风险; + default: + return null; + } + }; + + return ( +
+ {/* Header */} +
+
+
+

+ 保单条款智能核验 +

+

OCR识别 + 条款解析 + 合规核验 + 报告生成

+
+
+
+ + {/* Tab Navigation */} +
+ + + +
+ + {/* Upload Tab */} + {activeTab === "upload" && ( +
+ {/* Project Info */} +
+

项目信息

+
+
+ + setCheckForm({ ...checkForm, customer_name: e.target.value })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-green-500" + /> +
+
+ + setCheckForm({ ...checkForm, lease_amount: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-green-500" + /> +
+
+ + setCheckForm({ ...checkForm, project_type: e.target.value })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-green-500" + /> +
+
+
+ + {/* Upload Section */} +
+

上传保单PDF

+ +
+ + + +
+ + +
+
+ )} + + {/* Results Tab */} + {activeTab === "results" && checkResult && ( +
+ {/* Summary Card */} +
+
+
+

{checkResult.customer_name}

+

核验时间: {checkResult.check_timestamp}

+
+
+
+ {checkResult.overall_result === "compliant" ? "✅" : + checkResult.overall_result === "non_compliant" ? "❌" : "⚠️"} +
+
+ {checkResult.overall_result === "compliant" ? "合规" : + checkResult.overall_result === "non_compliant" ? "不合规" : "需复核"} +
+
+
+ +
+
+
保险金额
+
{checkResult.policy_info.coverage_amount}万
+
+
+
授信金额
+
{checkResult.policy_info.lease_amount}万
+
+
+
覆盖比例
+
{checkResult.policy_info.coverage_ratio.toFixed(1)}%
+
+
+
高风险除外
+
{checkResult.exclusions.filter(e => e.risk_level === "high").length}项
+
+
+
+ + {/* Compliance Items */} +
+

+ + 合规性检查结果 +

+
+ {checkResult.compliance_items.map((item, idx) => ( +
+
+
+
+ {getStatusBadge(item.status)} + {item.item} +
+
检查类别: {item.category}
+
+
+

{item.detail}

+
+ ))} +
+
+ + {/* Exclusions */} +
+

+ + 除外责任条款分析 +

+
+ {checkResult.exclusions.map((exc, idx) => ( +
+
+ {getRiskBadge(exc.risk_level)} + {exc.clause} +
+

{exc.description}

+
+ ))} +
+
+ + {/* Recommendations */} +
+

整改建议

+
    + {checkResult.recommendations.map((rec, idx) => ( +
  • + {idx + 1}. + {rec} +
  • + ))} +
+
+ + {/* Generate Report */} +
+

生成核验报告

+ +
+ + +
+ + + + {generatedReport && ( +
+
+

报告预览

+
+ + +
+
+
+                  {generatedReport}
+                
+
+ )} +
+
+ )} + + {/* Templates Tab */} + {activeTab === "templates" && ( +
+
+

核验报告模板库

+
+ + {templates.map(template => ( +
+
+
+
+ +

{template.name}

+ {template.id} +
+

{template.description}

+
+
+ +
+ {template.sections.map((section, idx) => ( + + {section} + + ))} +
+
+ ))} +
+ )} +
+ ); +} diff --git a/src/app/report-assistant/page.tsx b/src/app/report-assistant/page.tsx new file mode 100644 index 0000000..0dd5535 --- /dev/null +++ b/src/app/report-assistant/page.tsx @@ -0,0 +1,370 @@ +"use client"; + +import { useState } from "react"; +import { FileText, Download, Eye, Edit, Plus, Trash2, CheckSquare, Square } from "lucide-react"; + +interface ReportTemplate { + id: string; + name: string; + description: string; + sections: string[]; + created_at: string; + usage_count: number; +} + +interface GeneratedReport { + template_name: string; + generated_at: string; + content: { + title: string; + sections: Array<{ + name: string; + content: string; + }>; + }; +} + +export default function ReportAssistantPage() { + const [activeTab, setActiveTab] = useState<"generate" | "templates">("generate"); + const [selectedTemplate, setSelectedTemplate] = useState(""); + const [projectInfo, setProjectInfo] = useState({ + customer_name: "远东新能源公交公司", + project_type: "对公新能源", + amount: 3.0, + risk_level: "medium" + }); + + const [loading, setLoading] = useState(false); + const [generatedReport, setGeneratedReport] = useState(null); + + // 模拟报告模板数据 + const [templates] = useState([ + { + id: "TPL-001", + name: "标准风险评审报告", + description: "适用于常规项目的风险评审报告,包含完整的风险分析和建议", + sections: ["项目概况", "风险识别", "财务分析", "法律合规性", "综合评审意见", "审批建议"], + created_at: "2025-01-15", + usage_count: 156 + }, + { + id: "TPL-002", + name: "新能源项目专项报告", + description: "针对新能源行业的专项评审报告,关注政策补贴和行业风险", + sections: ["项目概况", "行业政策分析", "技术可行性", "财务测算", "风险评估", "审批建议"], + created_at: "2025-02-10", + usage_count: 89 + }, + { + id: "TPL-003", + name: "大额授信专项报告", + description: "5亿元以上大额授信项目的详细评审报告", + sections: ["项目概况", "企业背景调查", "深度财务分析", "抵押物评估", "风险缓释措施", "总部审批建议"], + created_at: "2025-01-20", + usage_count: 34 + }, + { + id: "TPL-004", + name: "快速审批简报", + description: "低风险项目的快速审批简报,简化流程", + sections: ["项目基本信息", "快速风险评估", "审批建议"], + created_at: "2025-02-01", + usage_count: 203 + } + ]); + + const handleGenerateReport = async () => { + if (!selectedTemplate) { + alert("请先选择报告模板"); + return; + } + + setLoading(true); + setGeneratedReport(null); + + try { + // 模拟生成报告 + await new Promise(resolve => setTimeout(resolve, 2000)); + + const template = templates.find(t => t.id === selectedTemplate)!; + + const mockReport: GeneratedReport = { + template_name: template.name, + generated_at: new Date().toLocaleString('zh-CN'), + content: { + title: `${projectInfo.customer_name} - ${template.name}`, + sections: template.sections.map(sectionName => ({ + name: sectionName, + content: generateMockSectionContent(sectionName, projectInfo) + })) + } + }; + + setGeneratedReport(mockReport); + } catch (error) { + alert("生成报告失败"); + } finally { + setLoading(false); + } + }; + + const generateMockSectionContent = (sectionName: string, info: any): string => { + switch (sectionName) { + case "项目概况": + return `客户名称:${info.customer_name}\n项目类型:${info.project_type}\n授信金额:${info.amount}亿元\n风险等级:${info.risk_level === 'high' ? '高风险' : info.risk_level === 'medium' ? '中风险' : '低风险'}\n\n本项目为${info.project_type}类型融资租赁项目,客户${info.customer_name}申请授信金额${info.amount}亿元。经初步评估,项目风险等级为${info.risk_level === 'high' ? '高风险' : info.risk_level === 'medium' ? '中风险' : '低风险'}。`; + + case "风险识别": + case "风险评估": + return `基于双轨驱动分析(规则引擎+历史数据),识别出以下主要风险点:\n\n1. 财务风险:资产负债率接近行业警戒线,需关注偿债能力\n2. 信用风险:征信查询次数偏高,需核实融资需求真实性\n3. 法律风险:存在未结案涉诉记录,需评估潜在影响\n\n历史数据显示,类似项目的通过率为73%,其中60%需要附加风控措施。`; + + case "财务分析": + case "深度财务分析": + return `关键财务指标分析:\n\n• 资产负债率:65%(行业平均60%)\n• 流动比率:1.2(良好)\n• 净资产收益率:8.5%(中等)\n• 现金流覆盖倍数:1.8倍\n\n综合评估:财务状况整体可控,但需关注债务结构优化。`; + + case "审批建议": + case "综合评审意见": + return `综合评审意见:\n\n建议:附加条件通过\n\n理由:\n1. 客户基本面良好,行业前景明朗\n2. 财务指标整体可控,但需加强风险缓释\n3. 历史同类案例成功率较高\n\n附加条件:\n• 建议降低授信额度10-15%\n• 要求增加抵押物或第三方担保\n• 设置季度财务监控机制`; + + case "行业政策分析": + return `${info.project_type}行业政策环境:\n\n• 国家政策支持力度大,补贴政策稳定\n• 地方政府配套措施完善\n• 行业准入门槛提高,竞争格局改善\n• 需关注补贴退坡风险`; + + case "技术可行性": + return `技术方案评估:\n\n• 技术路线成熟,市场验证充分\n• 设备供应商资质良好\n• 运营团队经验丰富\n• 维护保养体系完善`; + + default: + return `${sectionName}相关内容...\n\n[此部分内容由AI根据项目实际情况和历史数据自动生成]`; + } + }; + + return ( +
+ {/* Header */} +
+
+
+

+ 报告助手 +

+

智能报告生成与模板管理

+
+
+
+ + {/* Tab Navigation */} +
+ + +
+ + {/* Generate Report Tab */} + {activeTab === "generate" && ( +
+ {/* Project Info Form */} +
+

项目信息

+
+
+ + setProjectInfo({ ...projectInfo, customer_name: e.target.value })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-purple-500" + /> +
+
+ + setProjectInfo({ ...projectInfo, project_type: e.target.value })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-purple-500" + /> +
+
+ + setProjectInfo({ ...projectInfo, amount: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-purple-500" + /> +
+
+ + +
+
+
+ + {/* Template Selection */} +
+

选择报告模板

+
+ {templates.map(template => ( +
setSelectedTemplate(template.id)} + className={`p-4 rounded-lg border-2 cursor-pointer transition-all ${ + selectedTemplate === template.id + ? "border-purple-500 bg-purple-900/30" + : "border-slate-700 bg-slate-900/50 hover:border-slate-600" + }`} + > +
+
+ {selectedTemplate === template.id ? ( + + ) : ( + + )} +

{template.name}

+
+ {template.id} +
+

{template.description}

+
+ 使用次数: {template.usage_count} + 创建: {template.created_at} +
+
+ 章节: {template.sections.join(" · ")} +
+
+ ))} +
+ + +
+ + {/* Generated Report */} + {generatedReport && ( +
+
+
+

{generatedReport.content.title}

+

生成时间: {generatedReport.generated_at}

+
+
+ + +
+
+ +
+ {generatedReport.content.sections.map((section, idx) => ( +
+

{section.name}

+

+ {section.content} +

+
+ ))} +
+
+ )} +
+ )} + + {/* Templates Tab */} + {activeTab === "templates" && ( +
+
+

报告模板库

+ +
+ +
+ {templates.map(template => ( +
+
+
+
+ +

{template.name}

+ {template.id} +
+

{template.description}

+
+
+ + +
+
+ +
+ {template.sections.map((section, idx) => ( + + {section} + + ))} +
+ +
+ 使用次数: {template.usage_count} + 创建时间: {template.created_at} +
+
+ ))} +
+
+ )} +
+ ); +} diff --git a/src/app/review-allocation/page.tsx b/src/app/review-allocation/page.tsx new file mode 100644 index 0000000..56c917e --- /dev/null +++ b/src/app/review-allocation/page.tsx @@ -0,0 +1,698 @@ +"use client"; + +import { useState } from "react"; +import { Users, TrendingUp, AlertTriangle, Calculator, Target, History, CheckCircle } from "lucide-react"; + +interface Reviewer { + id: string; + name: string; + specialty: string[]; + current_projects: number; + workload_capacity: number; + avg_processing_days: number; + quality_score: number; + status: "available" | "busy" | "full"; +} + +interface Recommendation { + reviewer: string; + score: number; + breakdown: { + history_match: number; + workload: number; + specialty: number; + quality: number; + }; + estimated_days: number; + reason: string; +} + +interface KeyIndicator { + name: string; + value: string; + unit: string; + warning?: string; + historical_avg?: string; +} + +export default function ReviewAllocationPage() { + const [activeTab, setActiveTab] = useState<"allocation" | "calculation">("allocation"); + + // 智能调配表单 + const [allocationForm, setAllocationForm] = useState({ + project_name: "远东新能源公交项目", + project_industry: "新能源", + project_amount: 3.0, + project_complexity: 3, + risk_level: "medium" + }); + + const [allocationResult, setAllocationResult] = useState<{ + reviewers: Reviewer[]; + recommendations: Recommendation[]; + } | null>(null); + + const [loadingAllocation, setLoadingAllocation] = useState(false); + + // 关键指标测算表单 + const [calculationForm, setCalculationForm] = useState({ + project_name: "远东新能源公交项目", + revenue_year1: 5000, + revenue_year2: 6000, + revenue_year3: 6500, + cost_ratio: 0.65, + depreciation: 800, + interest_rate: 0.045, + loan_amount: 25000, + irr_target: 0.08 + }); + + const [calculationResult, setCalculationResult] = useState<{ + indicators: KeyIndicator[]; + warnings: string[]; + historical_insights: string[]; + } | null>(null); + + const [loadingCalculation, setLoadingCalculation] = useState(false); + + // 处理智能调配 + const handleAllocation = async () => { + setLoadingAllocation(true); + setAllocationResult(null); + + try { + await new Promise(resolve => setTimeout(resolve, 1500)); + + // 模拟评审员数据 + const mockReviewers: Reviewer[] = [ + { + id: "REV-001", + name: "张建国", + specialty: ["新能源", "制造业", "大额授信"], + current_projects: 3, + workload_capacity: 5, + avg_processing_days: 4.2, + quality_score: 9.2, + status: "available" + }, + { + id: "REV-002", + name: "李明华", + specialty: ["新能源", "交通运输", "设备租赁"], + current_projects: 2, + workload_capacity: 5, + avg_processing_days: 3.8, + quality_score: 8.9, + status: "available" + }, + { + id: "REV-003", + name: "王晓红", + specialty: ["建筑工程", "房地产", "基础设施"], + current_projects: 5, + workload_capacity: 5, + avg_processing_days: 5.5, + quality_score: 8.5, + status: "full" + }, + { + id: "REV-004", + name: "赵强", + specialty: ["科技研发", "信息技术", "医疗器械"], + current_projects: 4, + workload_capacity: 5, + avg_processing_days: 4.8, + quality_score: 9.0, + status: "busy" + } + ]; + + // 智能推荐(根据行业匹配、工作负荷、历史质量) + const mockRecommendations: Recommendation[] = [ + { + reviewer: "李明华", + score: 92.5, + breakdown: { + history_match: 95, + workload: 90, + specialty: 98, + quality: 89 + }, + estimated_days: 4, + reason: "专长高度匹配(新能源+交通运输),当前负荷最轻,历史同类项目处理速度快" + }, + { + reviewer: "张建国", + score: 88.3, + breakdown: { + history_match: 88, + workload: 80, + specialty: 95, + quality: 92 + }, + estimated_days: 5, + reason: "新能源领域经验丰富,质量评分最高,适合处理复杂项目" + }, + { + reviewer: "赵强", + score: 65.2, + breakdown: { + history_match: 45, + workload: 60, + specialty: 40, + quality: 90 + }, + estimated_days: 6, + reason: "专长不完全匹配,但质量稳定,可作为备选" + } + ]; + + setAllocationResult({ + reviewers: mockReviewers, + recommendations: mockRecommendations + }); + + } catch (error) { + alert("智能调配失败"); + } finally { + setLoadingAllocation(false); + } + }; + + // 处理关键指标测算 + const handleCalculation = async () => { + setLoadingCalculation(true); + setCalculationResult(null); + + try { + await new Promise(resolve => setTimeout(resolve, 1500)); + + // 模拟计算关键指标 + const totalRevenue = calculationForm.revenue_year1 + calculationForm.revenue_year2 + calculationForm.revenue_year3; + const totalCost = totalRevenue * calculationForm.cost_ratio; + const interestExpense = calculationForm.loan_amount * calculationForm.interest_rate; + const netProfit = totalRevenue - totalCost - calculationForm.depreciation * 3 - interestExpense * 3; + const irr = ((netProfit / calculationForm.loan_amount) / 3); + + const mockIndicators: KeyIndicator[] = [ + { + name: "项目内部收益率 (IRR)", + value: (irr * 100).toFixed(2), + unit: "%", + warning: irr < calculationForm.irr_target ? `低于目标IRR ${(calculationForm.irr_target * 100).toFixed(1)}%` : undefined, + historical_avg: "8.5%" + }, + { + name: "净现值 (NPV)", + value: (netProfit - calculationForm.loan_amount * 0.05).toFixed(0), + unit: "万元", + historical_avg: "3200万元" + }, + { + name: "投资回收期", + value: (calculationForm.loan_amount / (totalRevenue / 3)).toFixed(1), + unit: "年", + warning: (calculationForm.loan_amount / (totalRevenue / 3)) > 5 ? "回收期较长" : undefined, + historical_avg: "4.2年" + }, + { + name: "资产负债率", + value: ((calculationForm.loan_amount / (calculationForm.loan_amount * 1.5)) * 100).toFixed(1), + unit: "%", + historical_avg: "62%" + }, + { + name: "利息保障倍数", + value: ((totalRevenue - totalCost) / (interestExpense * 3)).toFixed(2), + unit: "倍", + warning: ((totalRevenue - totalCost) / (interestExpense * 3)) < 2 ? "偿债能力偏弱" : undefined, + historical_avg: "3.5倍" + }, + { + name: "现金流覆盖率", + value: (1.8).toFixed(2), + unit: "倍", + historical_avg: "2.1倍" + } + ]; + + const mockWarnings: string[] = []; + const mockInsights: string[] = []; + + if (irr < calculationForm.irr_target) { + mockWarnings.push("⚠️ 项目IRR低于目标值,建议调整收入预测或降低成本"); + } + + if ((calculationForm.loan_amount / (totalRevenue / 3)) > 5) { + mockWarnings.push("⚠️ 投资回收期超过5年,需评估长期风险"); + } + + mockInsights.push("📊 根据历史数据,新能源行业项目平均IRR为8.5%,当前项目处于行业中位水平"); + mockInsights.push("💡 类似规模项目的成功案例中,70%采用了分期投资策略以降低风险"); + mockInsights.push("🎯 建议关注:补贴政策变化、设备折旧率、运营成本控制"); + + setCalculationResult({ + indicators: mockIndicators, + warnings: mockWarnings, + historical_insights: mockInsights + }); + + } catch (error) { + alert("指标测算失败"); + } finally { + setLoadingCalculation(false); + } + }; + + const getStatusBadge = (status: string) => { + switch (status) { + case "available": + return 可用; + case "busy": + return 繁忙; + case "full": + return 已满; + default: + return null; + } + }; + + return ( +
+ {/* Header */} +
+
+
+

+ 评审调配助手 +

+

智能评审员分配 + 关键指标测算

+
+
+
+ + {/* Tab Navigation */} +
+ + +
+ + {/* 智能调配 Tab */} + {activeTab === "allocation" && ( +
+ {/* 项目信息 */} +
+

项目信息

+
+
+ + setAllocationForm({ ...allocationForm, project_name: e.target.value })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ + setAllocationForm({ ...allocationForm, project_industry: e.target.value })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ + setAllocationForm({ ...allocationForm, project_amount: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ + setAllocationForm({ ...allocationForm, project_complexity: parseInt(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ + +
+
+ + +
+ + {/* 调配结果 */} + {allocationResult && ( + <> + {/* 评审员看板 */} +
+

+ + 评审员工作负荷看板 +

+
+ {allocationResult.reviewers.map(reviewer => ( +
+
+
+

{reviewer.name}

+ {reviewer.id} +
+ {getStatusBadge(reviewer.status)} +
+ +
+
+ 在手项目: + {reviewer.current_projects}/{reviewer.workload_capacity} +
+
+ 平均处理: + {reviewer.avg_processing_days}天 +
+
+ 质量评分: + {reviewer.quality_score}/10 +
+
+
专长领域:
+
+ {reviewer.specialty.map((spec, idx) => ( + {spec} + ))} +
+
+
+
+ ))} +
+
+ + {/* Top 3 推荐 */} +
+

+ + Top 3 推荐评审员 +

+
+ {allocationResult.recommendations.map((rec, idx) => ( +
+
+
+
+ {idx + 1} +
+
+

{rec.reviewer}

+

预计 {rec.estimated_days} 天完成

+
+
+
+
{rec.score.toFixed(1)}
+
综合评分
+
+
+ +
+
推荐理由
+
{rec.reason}
+
+ +
+
+
{rec.breakdown.history_match}
+
历史匹配
+
+
+
{rec.breakdown.workload}
+
负荷状态
+
+
+
{rec.breakdown.specialty}
+
专长匹配
+
+
+
{rec.breakdown.quality}
+
质量评分
+
+
+
+ ))} +
+
+ + )} +
+ )} + + {/* 关键指标测算 Tab */} + {activeTab === "calculation" && ( +
+ {/* 输入表单 */} +
+

+ + 项目财务指标输入 +

+ +
+
+ + setCalculationForm({ ...calculationForm, project_name: e.target.value })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+ +
+
+ + setCalculationForm({ ...calculationForm, revenue_year1: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ + setCalculationForm({ ...calculationForm, revenue_year2: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ + setCalculationForm({ ...calculationForm, revenue_year3: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ +
+
+ + setCalculationForm({ ...calculationForm, cost_ratio: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ + setCalculationForm({ ...calculationForm, depreciation: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ + setCalculationForm({ ...calculationForm, interest_rate: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ + setCalculationForm({ ...calculationForm, loan_amount: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ +
+ + setCalculationForm({ ...calculationForm, irr_target: parseFloat(e.target.value) })} + className="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-white focus:outline-none focus:border-orange-500" + /> +
+
+ + +
+ + {/* 测算结果 */} + {calculationResult && ( + <> + {/* 关键指标 */} +
+

+ + 关键财务指标 +

+
+ {calculationResult.indicators.map((indicator, idx) => ( +
+
{indicator.name}
+
+ {indicator.value} {indicator.unit} +
+ {indicator.historical_avg && ( +
+ 历史平均: {indicator.historical_avg} +
+ )} + {indicator.warning && ( +
+ + {indicator.warning} +
+ )} +
+ ))} +
+
+ + {/* 风险预警 */} + {calculationResult.warnings.length > 0 && ( +
+

+ + 风险预警 +

+
    + {calculationResult.warnings.map((warning, idx) => ( +
  • {warning}
  • + ))} +
+
+ )} + + {/* 历史洞察 */} +
+

+ + 基于历史数据的洞察 +

+
    + {calculationResult.historical_insights.map((insight, idx) => ( +
  • + + {insight} +
  • + ))} +
+
+ + )} +
+ )} +
+ ); +} diff --git a/src/app/review-assist/page.tsx b/src/app/review-assist/page.tsx new file mode 100644 index 0000000..0b6b8ac --- /dev/null +++ b/src/app/review-assist/page.tsx @@ -0,0 +1,1282 @@ +'use client'; + +import React, { useState } from 'react'; +import ReactMarkdown from 'react-markdown'; + +// ============ 类型定义 ============ + +interface ReviewerInfo { + name: string; + current_projects: number; + specialty: string; +} + +interface ReviewerStats { + total_projects: number; + avg_actual_days: number; + avg_quality_score: number; + pass_rate: number; + industry_expertise: { + [industry: string]: { + count: number; + avg_quality: number; + }; + }; +} + +interface HistoryProject { + project_name: string; + industry: string; + credit_amount: number; + complexity: number; + planned_days: number; + actual_days: number; + reviewer: string; + outcome: string; + quality_score: number; + review_date: string; +} + +interface RecommendationItem { + reviewer: string; + score: number; + reasons: string[]; + est_complete_days: number; + industry_match_score: number; + load_score: number; + quality_score: number; +} + +interface AllocateResult { + reviewers: Array<{ + name: string; + current_projects: number; + specialty: string; + avg_quality: number; + total_history_projects: number; + }>; + recommendation: RecommendationItem[]; + balance_before: number; + balance_after: number; + algorithm_version: string; +} + +interface TabConfig { + id: string; + title: string; + icon: string; + description: string; +} + +// 后端 API 基址 +const API_BASE = + typeof window !== 'undefined' && window.location.hostname + ? `http://${window.location.hostname}:8002/api/review-assist` + : 'http://localhost:8002/api/review-assist'; + +// ============ 标签页配置 ============ + +const TABS: TabConfig[] = [ + { + id: 'load-balance', + title: '评审负荷与分配', + icon: '⚖️', + description: '分析主审员负荷,推荐最优分配方案', + }, + { + id: 'due-diligence', + title: '尽调资料预处理', + icon: '📋', + description: '生成结构化客户情报卡,标注风险', + }, + { + id: 'rent-calculation', + title: '租金测算校验', + icon: '💹', + description: '二次核算,覆盖率验证,敏感性分析', + }, + { + id: 'report-draft', + title: '评审报告初稿', + icon: '📄', + description: '基于情报卡和测算结果生成报告框架', + }, + { + id: 'pre-screening', + title: '立项风险筛查', + icon: '🔍', + description: '资料完整性检查,风险点识别(不给结论)', + }, +]; + +// ============ 主组件 ============ + +export default function ReviewAssistPage() { + const [activeTab, setActiveTab] = useState('load-balance'); + const [loading, setLoading] = useState(false); + const [result, setResult] = useState(''); + + // 标签页1:评审负荷与分配 + const [reviewers, setReviewers] = useState([ + { name: '李明', current_projects: 5, specialty: '制造业' }, + { name: '王芳', current_projects: 3, specialty: '新能源' }, + { name: '张强', current_projects: 7, specialty: '交通运输' }, + { name: '刘洋', current_projects: 4, specialty: '医疗健康' }, + ]); + const [newProjectIndustry, setNewProjectIndustry] = useState('光伏设备制造'); + const [newProjectAmount, setNewProjectAmount] = useState(5000); + const [newProjectComplexity, setNewProjectComplexity] = useState(3); + const [reviewerStatsMap, setReviewerStatsMap] = useState<{ [name: string]: ReviewerStats }>({}); + const [allocateResult, setAllocateResult] = useState(null); + const [expandedReviewer, setExpandedReviewer] = useState(null); + const [reviewerHistory, setReviewerHistory] = useState<{ [name: string]: HistoryProject[] }>({}); + const [loadingStats, setLoadingStats] = useState(false); + const [loadingAllocate, setLoadingAllocate] = useState(false); + + // 标签页2:尽调资料预处理 + const [ddProjectName, setDdProjectName] = useState('阳光光伏设备融资租赁项目'); + const [ddClientName, setDdClientName] = useState('阳光光伏设备有限公司'); + const [ddIndustry, setDdIndustry] = useState('光伏设备制造'); + + // 标签页3:租金测算校验 + const [creditAmount, setCreditAmount] = useState(5000); + const [leaseTerm, setLeaseTerm] = useState(36); + const [interestRate, setInterestRate] = useState(5.8); + const [monthlyCashflow, setMonthlyCashflow] = useState(85); + const [projectType, setProjectType] = useState('对公'); + + // 标签页4:评审报告初稿 + const [reportProjectName, setReportProjectName] = useState('阳光光伏设备融资租赁项目'); + const [reportClientName, setReportClientName] = useState('阳光光伏设备有限公司'); + const [intelligenceCard, setIntelligenceCard] = useState(''); + const [calculationResult, setCalculationResult] = useState(''); + + // 标签页5:立项风险筛查 + const [psProjectName, setPsProjectName] = useState('阳光光伏设备融资租赁项目'); + const [psClientName, setPsClientName] = useState('阳光光伏设备有限公司'); + const [psIndustry, setPsIndustry] = useState('光伏设备制造'); + const [documentChecklist, setDocumentChecklist] = useState([ + '营业执照', + '财务报表(近3年)', + '征信报告', + '租赁物清单', + '担保函', + ]); + + // 加载示例数据 + const loadSampleData = async () => { + try { + const response = await fetch(`${API_BASE}/sample-data/${activeTab}`); + + if (!response.ok) { + throw new Error(`HTTP错误: ${response.status}`); + } + + const data = await response.json(); + + if (!data) { + throw new Error('服务器返回空数据'); + } + + if (data.success) { + if (!data.data) { + alert('⚠️ 未找到示例数据'); + return; + } + + const sampleData = data.data; + + switch (activeTab) { + case 'load-balance': + setReviewers(sampleData.reviewers); + setNewProjectIndustry(sampleData.new_project_industry); + setNewProjectAmount(sampleData.new_project_amount); + break; + case 'due-diligence': + setDdProjectName(sampleData.project_name); + setDdClientName(sampleData.client_name); + setDdIndustry(sampleData.industry); + break; + case 'rent-calculation': + setCreditAmount(sampleData.credit_amount); + setLeaseTerm(sampleData.lease_term); + setInterestRate(sampleData.interest_rate); + setMonthlyCashflow(sampleData.monthly_cashflow); + setProjectType(sampleData.project_type); + break; + case 'report-draft': + setReportProjectName(sampleData.project_name); + setReportClientName(sampleData.client_name); + setIntelligenceCard(sampleData.intelligence_card); + setCalculationResult(sampleData.calculation_result); + break; + case 'pre-screening': + setPsProjectName(sampleData.project_name); + setPsClientName(sampleData.client_name); + setPsIndustry(sampleData.industry); + setDocumentChecklist(sampleData.document_checklist); + break; + } + + alert('✅ 示例数据已填充'); + } else { + const errorMsg = data.message || data.error || '加载失败'; + alert(`⚠️ ${errorMsg}`); + } + } catch (err) { + console.error('加载示例数据失败:', err); + const errorMessage = err instanceof Error ? err.message : '请检查后端服务'; + alert(`⚠️ 加载示例数据失败:${errorMessage}`); + } + }; + + // 加载评审员统计数据(评审负荷与分配专用) + const loadReviewerStats = async () => { + setLoadingStats(true); + try { + console.log('开始加载评审员统计数据...'); + const response = await fetch(`${API_BASE}/reviewer-stats`); + + console.log('Response status:', response.status); + if (!response.ok) { + throw new Error(`HTTP错误: ${response.status}`); + } + + const data = await response.json(); + console.log('收到数据:', data); + + if (!data) { + throw new Error('服务器返回空数据'); + } + + if (data.success) { + if (!data.data || typeof data.data !== 'object' || Object.keys(data.data).length === 0) { + alert('⚠️ 加载成功,但未找到评审员数据'); + setLoadingStats(false); + return; + } + + const reviewerNames = Object.keys(data.data); + console.log('评审员列表:', reviewerNames); + + // 先更新统计数据 + setReviewerStatsMap(data.data); + + // 更新评审员列表(从统计数据中提取) + const updatedReviewers = reviewerNames.map((name) => { + const stats = data.data[name]; + // 获取主要专长(历史项目最多的行业) + const industries = Object.entries(stats.industry_expertise || {}); + const mainIndustry = industries.length > 0 + ? industries.sort((a, b) => (b[1] as any).count - (a[1] as any).count)[0][0] + : '通用'; + + // 保留现有的 current_projects,如果有的话 + const existing = reviewers.find(r => r.name === name); + return { + name, + current_projects: existing?.current_projects || Math.floor(Math.random() * 5) + 1, + specialty: mainIndustry, + }; + }); + + console.log('更新后的评审员:', updatedReviewers); + setReviewers(updatedReviewers); + + // 使用 setTimeout 确保状态更新后再显示 alert + setTimeout(() => { + alert(`✅ 已加载 ${reviewerNames.length} 位评审员历史画像\n\n评审员:${reviewerNames.join('、')}`); + }, 100); + } else { + // 处理 success: false 的情况 + const errorMsg = data.message || data.error || '未知错误'; + alert(`❌ 加载失败:${errorMsg}`); + } + } catch (err) { + console.error('加载评审员统计失败:', err); + const errorMessage = err instanceof Error ? err.message : '请检查后端服务'; + alert(`❌ 加载失败:${errorMessage}`); + } finally { + setLoadingStats(false); + } + }; + + // 智能分配 + const smartAllocate = async () => { + setLoadingAllocate(true); + setAllocateResult(null); + try { + console.log('开始智能分配...'); + console.log('当前评审员列表:', reviewers); + console.log('新项目信息:', { newProjectIndustry, newProjectAmount, newProjectComplexity }); + + const requestBody = { + reviewers, + new_project_industry: newProjectIndustry, + new_project_amount: newProjectAmount, + new_project_complexity: newProjectComplexity, + }; + + console.log('请求体:', requestBody); + + const response = await fetch(`${API_BASE}/allocate`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(requestBody), + }); + + console.log('Response status:', response.status); + + if (!response.ok) { + const errorText = await response.text(); + console.error('错误响应:', errorText); + throw new Error(`HTTP错误: ${response.status} - ${errorText}`); + } + + const data = await response.json(); + console.log('收到分配结果:', data); + + if (!data) { + throw new Error('服务器返回空数据'); + } + + if (data.success) { + if (!data.data || !data.data.recommendation || data.data.recommendation.length === 0) { + alert('⚠️ 分配完成,但未找到推荐结果'); + setLoadingAllocate(false); + return; + } + + const topReviewer = data.data.recommendation[0]; + console.log('首选推荐:', topReviewer); + + setAllocateResult(data.data); + + // 使用 setTimeout 确保状态更新后再显示 alert + setTimeout(() => { + alert(`✅ 智能分配完成!\n\n推荐主审员:${topReviewer.reviewer}\n综合评分:${topReviewer.score.toFixed(1)}分\n\n理由:\n${topReviewer.reasons.join('\n')}`); + }, 100); + } else { + // 处理 success: false 的情况 + const errorMsg = data.message || data.error || '未知错误'; + alert(`❌ 分配失败:${errorMsg}`); + } + } catch (err) { + console.error('智能分配失败:', err); + const errorMessage = err instanceof Error ? err.message : '请检查后端服务'; + alert(`❌ 分配失败:${errorMessage}`); + } finally { + setLoadingAllocate(false); + } + }; + + // 加载评审员历史 + const loadReviewerHistory = async (reviewerName: string) => { + if (reviewerHistory[reviewerName]) { + // 已加载,切换展开/收起 + setExpandedReviewer(expandedReviewer === reviewerName ? null : reviewerName); + return; + } + + try { + const response = await fetch(`${API_BASE}/history?reviewer=${encodeURIComponent(reviewerName)}`); + + if (!response.ok) { + throw new Error(`HTTP错误: ${response.status}`); + } + + const data = await response.json(); + + if (!data) { + throw new Error('服务器返回空数据'); + } + + if (data.success) { + const historyData = data.data || []; + setReviewerHistory((prev) => ({ + ...prev, + [reviewerName]: historyData, + })); + setExpandedReviewer(reviewerName); + + if (historyData.length === 0) { + alert(`ℹ️ ${reviewerName} 暂无历史项目记录`); + } + } else { + const errorMsg = data.message || data.error || '加载失败'; + alert(`❌ 加载 ${reviewerName} 历史失败:${errorMsg}`); + } + } catch (err) { + console.error('加载历史失败:', err); + const errorMessage = err instanceof Error ? err.message : '请检查后端服务'; + alert(`❌ 加载历史失败:${errorMessage}`); + } + }; + + // 调用助手 + const callAssistant = async () => { + setLoading(true); + setResult(''); + + try { + let endpoint = ''; + let requestBody: any = {}; + + switch (activeTab) { + case 'load-balance': + endpoint = `${API_BASE}/load-balance/stream`; + requestBody = { + reviewers, + new_project_industry: newProjectIndustry, + new_project_amount: newProjectAmount, + }; + break; + + case 'due-diligence': + endpoint = `${API_BASE}/due-diligence/stream`; + requestBody = { + project_name: ddProjectName, + client_name: ddClientName, + industry: ddIndustry, + financial_data: { + registered_capital: 8000, + established_years: 6, + asset_liability_ratio: 0.58, + current_ratio: 1.45, + net_profit_2024: 1200, + net_profit_2025: 1450, + revenue_growth_rate: 0.18, + }, + credit_report: { + overdue_count: 0, + query_count_recent_6m: 4, + credit_limit: 20000, + enforcement_records: 0, + }, + guarantee_info: { + type: '法人连带担保+设备抵押', + guarantor: '阳光集团有限公司', + guarantor_net_assets: 50000, + }, + external_risks: { + litigation_records: [], + negative_news: 0, + industry_policy: '国家支持光伏产业发展', + }, + }; + break; + + case 'rent-calculation': + endpoint = `${API_BASE}/rent-calculation/stream`; + requestBody = { + credit_amount: creditAmount, + lease_term: leaseTerm, + interest_rate: interestRate, + monthly_cashflow: monthlyCashflow, + project_type: projectType, + }; + break; + + case 'report-draft': + endpoint = `${API_BASE}/report-draft/stream`; + requestBody = { + project_name: reportProjectName, + client_name: reportClientName, + intelligence_card: intelligenceCard || '【请先生成情报卡】', + calculation_result: calculationResult || '【请先生成测算结果】', + }; + break; + + case 'pre-screening': + endpoint = `${API_BASE}/pre-screening/stream`; + requestBody = { + project_name: psProjectName, + client_name: psClientName, + industry: psIndustry, + document_checklist: documentChecklist, + financial_data: { + registered_capital: 8000, + asset_liability_ratio: 0.58, + net_profit_2025: 1450, + }, + similar_cases: [ + { + project_name: '某光伏企业融资租赁(2025年)', + rejection_reason: '担保方资产不足,要求补充第二担保人', + lesson: '光伏行业波动大,需要双重担保', + }, + ], + }; + break; + + default: + throw new Error('未知标签页'); + } + + const response = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(requestBody), + }); + + if (!response.ok) throw new Error('请求失败'); + + const reader = response.body?.getReader(); + const decoder = new TextDecoder(); + + if (!reader) throw new Error('无法读取响应流'); + + let buffer = ''; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop() || ''; + + for (const line of lines) { + if (line.startsWith('data: ')) { + const data = JSON.parse(line.slice(6)); + if (data.type === 'content') { + setResult((prev) => prev + data.data); + } else if (data.type === 'error') { + console.error('流式错误:', data.message); + } + } + } + } + + // 保存结果供报告初稿使用 + if (activeTab === 'due-diligence') { + setIntelligenceCard(result); + } else if (activeTab === 'rent-calculation') { + setCalculationResult(result); + } + } catch (err) { + console.error('调用助手失败:', err); + setResult('❌ 调用失败:' + (err as Error).message); + } finally { + setLoading(false); + } + }; + + // 渲染表单 + const renderForm = () => { + switch (activeTab) { + case 'load-balance': + return ( +
+ {/* 顶部概览 - 新项目输入 */} +
+

+ 🎯 + 待分配新项目 +

+
+
+ + +
+
+
+ + setNewProjectAmount(Number(e.target.value))} + className="glass-input px-3 py-2 rounded-lg w-full text-sm" + /> +
+
+ + setNewProjectComplexity(Number(e.target.value))} + className="glass-input px-3 py-2 rounded-lg w-full text-sm" + /> +
+
+
+
+ + {/* 操作按钮 */} +
+ + +
+ + {/* 评审员卡片网格 */} + {reviewers.length > 0 && ( +
+

评审员负荷看板

+
+ {reviewers.map((reviewer) => { + const stats = reviewerStatsMap[reviewer.name]; + const isExpanded = expandedReviewer === reviewer.name; + const history = reviewerHistory[reviewer.name] || []; + + // 配色方案(每人固定配色) + const colors = [ + { bg: 'bg-blue-500', text: 'text-blue-500', light: 'bg-blue-50' }, + { bg: 'bg-green-500', text: 'text-green-500', light: 'bg-green-50' }, + { bg: 'bg-purple-500', text: 'text-purple-500', light: 'bg-purple-50' }, + { bg: 'bg-orange-500', text: 'text-orange-500', light: 'bg-orange-50' }, + { bg: 'bg-pink-500', text: 'text-pink-500', light: 'bg-pink-50' }, + { bg: 'bg-indigo-500', text: 'text-indigo-500', light: 'bg-indigo-50' }, + ]; + const colorIndex = reviewers.indexOf(reviewer) % colors.length; + const color = colors[colorIndex]; + + return ( +
+
+ {/* 头像 */} +
+ {reviewer.name.charAt(0)} +
+ +
+ {/* 名称和专长 */} +
+
+

{reviewer.name}

+ + {reviewer.specialty} + +
+ {stats && ( +
+
历史项目
+
{stats.total_projects}个
+
+ )} +
+ + {/* 指标 */} + {stats && ( +
+ {/* 质量分 */} +
+
+ 历史质量 + = 90 ? 'text-green-600' : + stats.avg_quality_score >= 85 ? 'text-yellow-600' : 'text-red-600' + }`}> + {stats.avg_quality_score.toFixed(0)}分 + +
+
+
= 90 ? 'bg-green-500' : + stats.avg_quality_score >= 85 ? 'bg-yellow-500' : 'bg-red-500' + }`} + style={{ width: `${stats.avg_quality_score}%` }} + /> +
+
+ + {/* 通过率 */} +
+
+ 通过率 + + {(stats.pass_rate * 100).toFixed(0)}% + +
+
+
+
+
+ + {/* 当前负荷 */} +
+
+ 当前负荷 + = 6 ? 'text-red-600' : + reviewer.current_projects >= 4 ? 'text-yellow-600' : 'text-green-600' + }`}> + {reviewer.current_projects}个项目 + +
+
+
= 6 ? 'bg-red-500' : + reviewer.current_projects >= 4 ? 'bg-yellow-500' : 'bg-green-500' + }`} + style={{ width: `${Math.min(100, (reviewer.current_projects / 7) * 100)}%` }} + /> +
+
+
+ )} + + {/* 查看历史按钮 */} + + + {/* 历史列表 */} + {isExpanded && history.length > 0 && ( +
+ {history.slice(0, 5).map((project, idx) => ( +
+
+ {project.project_name} + + {project.outcome} + +
+
+
{project.industry} | {project.credit_amount}万元 | 质量{project.quality_score}分
+
用时{project.actual_days}天 | {project.review_date}
+
+
+ ))} + {history.length > 5 && ( +
+ 仅显示最近5条,共{history.length}条历史 +
+ )} +
+ )} +
+
+
+ ); + })} +
+
+ )} + + {/* 智能分配结果 */} + {allocateResult && ( +
+

+ ✨ + 智能分配结果 +

+ + {/* 推荐排序 */} +
+ {allocateResult.recommendation.map((rec, idx) => { + const isTop = idx === 0; + return ( +
+
+
+ + #{idx + 1} + + {rec.reviewer} + {isTop && ( + + 推荐 + + )} +
+
+
{rec.score.toFixed(1)}
+
综合分
+
+
+ + {/* 评分条 */} +
+
+
+
+
+ + {/* 推荐理由 */} +
+ {rec.reasons.map((reason, i) => ( + + {reason} + + ))} +
+ + {/* 预计完工 */} +
+ 预计 {rec.est_complete_days} 天完成 +
+
+ ); + })} +
+ + {/* 负荷均衡对比 */} +
+

负荷均衡度

+
+
+
分配前
+
+
+
+
+ + {allocateResult.balance_before.toFixed(2)} + +
+
+
+
分配后
+
+
+
+
+ + {allocateResult.balance_after.toFixed(2)} + +
+
+
+ {allocateResult.balance_after < allocateResult.balance_before && ( +
+ ✓ 均衡度提升,整体负荷更均衡 +
+ )} +
+ 算法版本:{allocateResult.algorithm_version} +
+
+
+ )} +
+ ); + + case 'due-diligence': + return ( +
+
+ + setDdProjectName(e.target.value)} + className="glass-input px-3 py-2 rounded-lg w-full" + /> +
+
+ + setDdClientName(e.target.value)} + className="glass-input px-3 py-2 rounded-lg w-full" + /> +
+
+ + setDdIndustry(e.target.value)} + className="glass-input px-3 py-2 rounded-lg w-full" + /> +
+

+ 💡 示例数据包含完整的财务数据、征信报告、担保信息等 +

+
+ ); + + case 'rent-calculation': + return ( +
+
+ + setCreditAmount(Number(e.target.value))} + className="glass-input px-3 py-2 rounded-lg w-full" + /> +
+
+ + setLeaseTerm(Number(e.target.value))} + className="glass-input px-3 py-2 rounded-lg w-full" + /> +
+
+ + setInterestRate(Number(e.target.value))} + className="glass-input px-3 py-2 rounded-lg w-full" + /> +
+
+ + setMonthlyCashflow(Number(e.target.value))} + className="glass-input px-3 py-2 rounded-lg w-full" + /> +
+
+ + +
+
+ ); + + case 'report-draft': + return ( +
+
+ + setReportProjectName(e.target.value)} + className="glass-input px-3 py-2 rounded-lg w-full" + /> +
+
+ + setReportClientName(e.target.value)} + className="glass-input px-3 py-2 rounded-lg w-full" + /> +
+
+ +