jerry 4 månader sedan
förälder
incheckning
2dc3d0d90e

+ 2 - 1
.gitignore

@@ -2,4 +2,5 @@ __pycache__
 debug_pages
 logs
 .DS_Store
-*.jpg
+*.jpg
+node_modules*

+ 6 - 0
config/accounts.json

@@ -495,6 +495,12 @@
             "username":"romicloud@163.com",
             "password": "Visafly@111",
             "lock_until": 0
+        },
+        {
+            "id": 1,
+            "username":"arket_zz@163.com",
+            "password": "Visafly@111",
+            "lock_until": 0
         }
     ],
     "ie_es": [

+ 0 - 1
config/bindings.json

@@ -1 +0,0 @@
-[]

+ 122 - 104
config/groups.json

@@ -3,15 +3,18 @@
         "identifier": "VFS_IE_NL",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "ie_nl",
+        "local_account_pool": "ie_nl",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -32,7 +35,6 @@
             "website": "https://visa.vfsglobal.com/irl/en/nld/login",
             "appointment_types": [
                 {
-                    "id": 573,
                     "routing_key": "slot.dub.nl.tourist",
                     "center_name": "Netherlands Visa Application Center - Dublin",
                     "city": "Dublin",
@@ -43,9 +45,7 @@
                     "category_name": "All Short stay Categories",
                     "category_code": "TA",
                     "subcategory_name": "Tourist",
-                    "subcategory_code": "To",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "To"
                 }
             ]
         }
@@ -54,15 +54,19 @@
         "identifier": "VFS_SG_FR",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "sg_fr",
+        "local_account_pool": "sg_fr",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
+
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -83,7 +87,6 @@
             "website": "https://visa.vfsglobal.com/sgp/en/fra/login",
             "appointment_types": [
                 {
-                    "id": 538,
                     "routing_key": "slot.sin.fr.tourist",
                     "center_name": "France Visa Application Center, Singapore",
                     "city": "Singapore",
@@ -94,9 +97,7 @@
                     "category_name": "Short Stay",
                     "category_code": "02",
                     "subcategory_name": "Short Stay Tourist, Family Visit",
-                    "subcategory_code": "Six",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "Six"
                 }
             ]
         }
@@ -105,15 +106,19 @@
         "identifier": "VFS_AU_FR",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "au_fr",
+        "local_account_pool": "au_fr",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
+
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -134,7 +139,6 @@
             "website": "https://visa.vfsglobal.com/aus/en/fra/login",
             "appointment_types": [
                 {
-                    "id": 506,
                     "routing_key": "slot.syd.fr.tourist",
                     "center_name": "France Visa Application Center - Sydney",
                     "city": "Sydney",
@@ -145,12 +149,9 @@
                     "category_name": "VISA",
                     "category_code": "VISA",
                     "subcategory_name": "Short Stay Schengen Visa",
-                    "subcategory_code": "ShortStaySchengenVisa",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "ShortStaySchengenVisa"
                 },
                 {
-                    "id": 513,
                     "routing_key": "slot.mel.fr.tourist",
                     "center_name": "France Visa Application Center - Melbourne",
                     "city": "Melbourne",
@@ -161,9 +162,7 @@
                     "category_name": "VISA",
                     "category_code": "VISA",
                     "subcategory_name": "Short Stay Schengen Visa",
-                    "subcategory_code": "ShortStaySchengenVisa",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "ShortStaySchengenVisa"
                 }
             ]
         }
@@ -172,15 +171,19 @@
         "identifier": "VFS_GB_IT",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "gb_it",
+        "local_account_pool": "gb_it",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
+
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -201,7 +204,6 @@
             "language": "en",
             "appointment_types": [
                 {
-                    "id": 706,
                     "routing_key": "slot.lon.it.tourist",
                     "center_name": "Italy Visa Application Centre, London",
                     "city": "London",
@@ -212,27 +214,20 @@
                     "category_name": "Italy UK VisaCategory",
                     "category_code": "UKITVED",
                     "subcategory_name": "Tourist/ Business/ EU Family",
-                    "subcategory_code": "TBE",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "TBE"
                 },
                 {
-                    "id": 708,
                     "routing_key": "slot.man.it.tourist",
                     "center_name": "Italy Visa Application Centre, Manchester",
                     "city": "Manchester",
                     "visa_type": "Tourist",
                     "country": "Italy",
                     "address": "50 Devonshire Street North, M12 6JH",
-                    "website": "https://visa.vfsglobal.com/gbr/en/ita/login",
                     "vac_code": "IMAN",
                     "category_name": "Italy UK VisaCategory",
                     "category_code": "UKITVED",
                     "subcategory_name": "Tourist/ Business/ EU Family",
-                    "subcategory_code": "TBE",
-                    "language": "en",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "TBE"
                 }
             ]
         }
@@ -241,15 +236,19 @@
         "identifier": "VFS_GB_NL",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "gb_nl",
+        "local_account_pool": "gb_nl",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
+
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -270,7 +269,6 @@
             "website": "https://visa.vfsglobal.com/gbr/en/nld/login",
             "appointment_types": [
                 {
-                    "id": 715,
                     "routing_key": "slot.lon.nl.tourist",
                     "center_name": "Netherlands Visa application centre - London",
                     "city": "London",
@@ -281,26 +279,20 @@
                     "category_name": "Schengen Visa",
                     "category_code": "Schengen Visa",
                     "subcategory_name": "Tourism",
-                    "subcategory_code": "TA",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "TA"
                 },
                 {
-                    "id": 723,
                     "routing_key": "slot.man.it.tourist",
                     "center_name": "Netherlands Visa application centre - Manchester",
                     "city": "Manchester",
                     "visa_type": "Tourist",
                     "country": "Netherlands",
                     "address": "50 Devonshire Street North, M12 6JH",
-                    "website": "https://visa.vfsglobal.com/gbr/en/nld/login",
                     "vac_code": "NAKT",
                     "category_name": "Schengen Visa",
                     "category_code": "Schengen Visa",
                     "subcategory_name": "Tourism",
-                    "subcategory_code": "TA",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "TA"
                 }
             ]
         }
@@ -309,15 +301,19 @@
         "identifier": "VFS_GB_NO",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "gb_no",
+        "local_account_pool": "gb_no",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
+
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -338,7 +334,6 @@
             "website": "https://visa.vfsglobal.com/gbr/en/nor/login",
             "appointment_types": [
                 {
-                    "id": 731,
                     "routing_key": "slot.lon.no.tourist",
                     "center_name": "Norway Visa Application Centre, London",
                     "city": "London",
@@ -349,9 +344,7 @@
                     "category_name": "Schengen Visa C",
                     "category_code": "SCHVISA",
                     "subcategory_name": "Tourist Visa",
-                    "subcategory_code": "TOU",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "TOU"
                 }
             ]
         }
@@ -360,15 +353,19 @@
         "identifier": "VFS_IE_AT",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "ie_at",
+        "local_account_pool": "ie_at",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
+
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -389,7 +386,6 @@
             "website": "https://visa.vfsglobal.com/irl/en/aut/login",
             "appointment_types": [
                 {
-                    "id": 551,
                     "routing_key": "slot.dub.at.tourist",
                     "center_name": "Austria / Switzerland / Liechtenstein/ Slovenia Visa Application Center, Dublin",
                     "city": "Dublin",
@@ -400,9 +396,7 @@
                     "category_name": "Other Visas",
                     "category_code": "Default_Austria_Ireland ",
                     "subcategory_name": "All Visas ",
-                    "subcategory_code": "TA",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "TA"
                 }
             ]
         }
@@ -411,15 +405,19 @@
         "identifier": "VFS_IE_DK",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "ie_dk",
+        "local_account_pool": "ie_dk",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
+
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -440,7 +438,6 @@
             "website": "https://visa.vfsglobal.com/irl/en/dnk/login",
             "appointment_types": [
                 {
-                    "id": 556,
                     "routing_key": "slot.dub.dk.tourist",
                     "center_name": "Denmark Visa Application Center, Dublin ",
                     "city": "Dublin",
@@ -451,9 +448,7 @@
                     "category_name": "Schengen Visa",
                     "category_code": "SV",
                     "subcategory_name": "Tourism",
-                    "subcategory_code": "TV",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "TV"
                 }
             ]
         }
@@ -462,15 +457,19 @@
         "identifier": "VFS_IE_FI",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "ie_fi",
+        "local_account_pool": "ie_fi",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
+
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -491,7 +490,6 @@
             "website": "https://visa.vfsglobal.com/irl/en/fin/login",
             "appointment_types": [
                 {
-                    "id": 559,
                     "routing_key": "slot.dub.fi.tourist",
                     "center_name": "Application Centre, Dublin",
                     "city": "Dublin",
@@ -502,9 +500,7 @@
                     "category_name": "VISA",
                     "category_code": "S S",
                     "subcategory_name": "Tourist Category",
-                    "subcategory_code": "Tourist Category",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "Tourist Category"
                 }
             ]
         }
@@ -513,15 +509,19 @@
         "identifier": "VFS_IE_HU",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "ie_hu",
+        "local_account_pool": "ie_hu",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
+
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -542,7 +542,6 @@
             "website": "https://visa.vfsglobal.com/irl/en/hun/login",
             "appointment_types": [
                 {
-                    "id": 566,
                     "routing_key": "slot.dub.hu.tourist",
                     "center_name": "Ireland Visa Application Center,Dublin",
                     "city": "Dublin",
@@ -553,9 +552,7 @@
                     "category_name": "Short Stay",
                     "category_code": "SS",
                     "subcategory_name": "Schengen Visa",
-                    "subcategory_code": "Schengen Visa",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "Schengen Visa"
                 }
             ]
         }
@@ -564,15 +561,19 @@
         "identifier": "VFS_IE_IS",
         "debug": false,
         "enable": false,
-        "account_built_in": true,
         "need_account": true,
-        "account_pool": "ie_is",
+        "local_account_pool": "ie_is",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
         "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 30,
+
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
             "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
@@ -593,7 +594,6 @@
             "website": "https://visa.vfsglobal.com/irl/en/isl/login",
             "appointment_types": [
                 {
-                    "id": 569,
                     "routing_key": "slot.dub.is.tourist",
                     "center_name": "Iceland Visa Application Center- Dublin",
                     "city": "Dublin",
@@ -604,9 +604,7 @@
                     "category_name": "C-Visa",
                     "category_code": "CVI",
                     "subcategory_name": "Tourism",
-                    "subcategory_code": "OTT",
-                    "fee": null,
-                    "currency": null
+                    "subcategory_code": "OTT"
                 }
             ]
         }
@@ -616,13 +614,18 @@
         "debug": false,
         "enable": true,
         "need_account": true,
-        "account_built_in": true,
-        "account_pool": "ie_es",
+        "local_account_pool": "ie_es",
         "need_proxy": true,
         "proxy_pool": "local",
         "target_instances": 1,
+        "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 60,
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
+            "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
         },
@@ -656,13 +659,18 @@
         "debug": false,
         "enable": false,
         "need_account": true,
-        "account_built_in": false,
-        "account_pool": "gb_es",
+        "local_account_pool": "gb_es",
         "need_proxy": true,
         "proxy_pool": "local",
         "target_instances": 1,
+        "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 60,
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
+            "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
         },
@@ -696,13 +704,18 @@
         "debug": false,
         "enable": true,
         "need_account": true,
-        "account_built_in": true,
-        "account_pool": "gb_fr",
+        "local_account_pool": "gb_fr",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
+        "account_login_interval": 30,
+        "order_account_routing": "auto.slot.lon.fr.tourist",
+        "order_account_online_limit": 5,
+        "account_bind_applicant": true,
+        "session_max_life": 30,
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
+            "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
         },
@@ -737,13 +750,18 @@
         "debug": false,
         "enable": true,
         "need_account": false,
-        "account_built_in": false,
-        "account_pool": "",
+        "local_account_pool": "",
         "need_proxy": true,
         "proxy_pool": "ireland_proxies",
         "target_instances": 1,
+        "account_login_interval": 30,
+        "order_account_routing": "",
+        "order_account_online_limit": 0,
+        "account_bind_applicant": false,
+        "session_max_life": 15,
         "query_wait": {
-            "mode": 2,
+            "mode": "Random",
+            "fixed_wait": 10,
             "random_min": 60,
             "random_max": 300
         },

+ 8 - 8
config/proxies.json

@@ -112,21 +112,21 @@
     "ireland_proxies": [
         {
             "id": 100029,
-            "ip": "95.135.130.73",
+            "ip": "95.135.130.175",
             "lock_until": 0,
-            "password": "RTBuPWx1CEr6DfD",
-            "port": 48306,
+            "password": "hmuROCk1FDebCnL",
+            "port": 46247,
             "scheme": "http",
-            "username": "9zMOkhCng5HG8SZ"
+            "username": "GB6o2vBrXFjz4ya"
         },
         {
             "id": 100029,
-            "ip": "95.135.130.76",
+            "ip": "95.135.130.29",
             "lock_until": 0,
-            "password": "5i4lV3VjNwE4bkL",
-            "port": 41553,
+            "password": "WmqFTSvRvtxChIT",
+            "port": 43740,
             "scheme": "http",
-            "username": "aNCaMFjblyODleO"
+            "username": "JUcjydi0HKZzWC6"
         }
     ],
     "local": [

+ 65 - 80
core/app_manager.py

@@ -5,7 +5,7 @@ import threading
 from typing import Dict, List, Optional
 
 # 引入核心组件
-from vs_types import GroupConfig
+from vs_types import GroupConfig, NotFoundError, BizLogicError
 from gco_wrapper import GCOWrapper
 from vs_log_macros import VSC_INFO, VSC_ERROR, VSC_WARN
 from core.plugin_reloader import reload_plugin_module
@@ -33,23 +33,18 @@ class AppManager:
     def load_configs(self):
         """读取并解析配置文件"""
         if not os.path.exists(self.config_file):
-            VSC_ERROR("app_mgr", f"Config file not found: {self.config_file}")
-            return
+            raise NotFoundError(message=f'Config file not found: {self.config_file}')
 
-        try:
-            with open(self.config_file, 'r', encoding='utf-8') as f:
-                data = json.load(f)
-            
-            for item in data:
-                # JSON -> GroupConfig 转换
-                grp_cfg = GroupConfig.from_json(item)
-                print(grp_cfg.free_config)
-                self.configs[grp_cfg.identifier] = grp_cfg
-                
-            VSC_INFO("app_mgr", f"Loaded {len(self.configs)} group configurations.")
+        with open(self.config_file, 'r', encoding='utf-8') as f:
+            data = json.load(f)
+        
+        for item in data:
+            # JSON -> GroupConfig 转换
+            grp_cfg = GroupConfig.from_json(item)
+            print(grp_cfg.free_config)
+            self.configs[grp_cfg.identifier] = grp_cfg
             
-        except Exception as e:
-            VSC_ERROR("app_mgr", f"Failed to load configs: {e}")
+        VSC_INFO("app_mgr", f"Loaded {len(self.configs)} group configurations.")
 
     def start_all(self):
         """启动所有 enable=True 的组"""
@@ -57,46 +52,35 @@ class AppManager:
             if cfg.enable and gid not in self.executors:
                 self.start_group(gid)
 
-    def start_group(self, group_id: str) -> bool:
+    def start_group(self, group_id: str):
         with self._lock:
             if group_id not in self.configs:
-                VSC_ERROR("app_mgr", f"Group {group_id} not found in config")
-                return False
+                raise NotFoundError(message=f"Group {group_id} not found in config")
             
             if group_id in self.executors:
                 VSC_WARN("app_mgr", f"Group {group_id} is already running")
-                return True
+                return
 
             cfg = self.configs[group_id]
-            try:
-                gco = GCOWrapper(cfg)
-                gco.load()
-                gco.start()
-                self.executors[group_id] = gco
-                VSC_INFO("app_mgr", f"Started group: {group_id}")
-                return True
-            except Exception as e:
-                VSC_ERROR("app_mgr", f"Failed to start group {group_id}: {e}")
-                return False
+            gco = GCOWrapper(cfg)
+            gco.load()
+            gco.start()
+            self.executors[group_id] = gco
+            VSC_INFO("app_mgr", f"Started group: {group_id}")
 
-    def stop_group(self, group_id: str) -> bool:
+    def stop_group(self, group_id: str):
         with self._lock:
             if group_id not in self.executors:
-                return False
+                raise NotFoundError(message=f"Group {group_id} not found in config")
             
             VSC_INFO("app_mgr", f"Stopping group: {group_id}...")
-            try:
-                self.executors[group_id].stop()
-                del self.executors[group_id]
-                VSC_INFO("app_mgr", f"Stopped group: {group_id}")
-                return True
-            except Exception as e:
-                VSC_ERROR("app_mgr", f"Error stopping group {group_id}: {e}")
-                return False
+            self.executors[group_id].stop()
+            del self.executors[group_id]
+            VSC_INFO("app_mgr", f"Stopped group: {group_id}")
 
-    def restart_group(self, group_id: str) -> bool:
+    def restart_group(self, group_id: str):
         self.stop_group(group_id)
-        return self.start_group(group_id)
+        self.start_group(group_id)
 
     def ota_upgrade_plugin(self, plugin_name: str) -> List[str]:
         """
@@ -107,13 +91,13 @@ class AppManager:
         4. 重新启动这些组
         """
         affected_groups = []
-        
-        # 1. 查找受影响的组
-        for gid, exec in self.executors.items():
-            # 注意:这里我们访问 coord 私有成员,实际工程中建议添加 getter
-            # 假设 config 存储在 m_cfg
-            if exec.m_cfg.plugin_config.plugin_name == plugin_name:
-                affected_groups.append(gid)
+        with self._lock:
+            # 1. 查找受影响的组
+            for gid, exec in self.executors.items():
+                # 注意:这里我们访问 coord 私有成员,实际工程中建议添加 getter
+                # 假设 config 存储在 m_cfg
+                if exec.m_cfg.plugin_config.plugin_name == plugin_name:
+                    affected_groups.append(gid)
         
         VSC_INFO("app_mgr", f"OTA Update for '{plugin_name}'. Affected groups: {affected_groups}")
         
@@ -140,65 +124,66 @@ class AppManager:
             return group_config.to_json()
     
     # ----------------- 更新配置(局部) -----------------
-    def ota_update_plugin_config(self, group_id: str, new_config_str: str) -> bool:
+    def ota_update_plugin_config(self, group_id: str, new_config_str: str):
         """更新某个 Executor 的配置(只影响单个 Executor)"""
-        new_config = GroupConfig.from_json(json.loads(new_config_str))
         with self._lock:
+            new_config = GroupConfig.from_json(json.loads(new_config_str))
             old_exe = self.executors.get(group_id)
             if not old_exe:
                 # Executor 没运行,直接创建启动
+                self.configs[group_id] = new_config
                 exe = GCOWrapper(new_config)
                 exe.load()
                 exe.start()
                 self.executors[group_id] = exe
-                return True
+                return
 
             # 创建新 Executor(插件不变,只更新配置)
             new_exe = GCOWrapper(new_config)
             new_exe.load()
-            try:
-                new_exe.start()
-                old_exe.stop()
-                self.executors[group_id] = new_exe
-                VSC_INFO("app_mgr", f"Config update applied for {group_id}")
-                return True
-            except Exception as e:
-                VSC_ERROR("app_mgr", f"Failed to update config for {group_id}: {e}")
-                return False
+            new_exe.start()
+            old_exe.stop()
+            self.executors[group_id] = new_exe
+            VSC_INFO("app_mgr", f"Config update applied for {group_id}")
+     
 
     def get_status(self):
         """获取所有组的状态"""
-        status_list = []
-        for gid, cfg in self.configs.items():
-            running = gid in self.executors
-            status_list.append({
-                "id": gid,
-                "plugin": cfg.plugin_config.plugin_name,
-                "running": running,
-                "instances": cfg.target_instances if running else 0,
-                "account_pool": cfg.account_pool
-            })
-        return status_list
+        with self._lock:
+            status_list = []
+            for gid, cfg in self.configs.items():
+                running = gid in self.executors
+                status_list.append({
+                    "id": gid,
+                    "plugin": cfg.plugin_config.plugin_name,
+                    "running": running,
+                    "instances": cfg.target_instances if running else 0,
+                    "local_account_pool": cfg.local_account_pool,
+                    "proxies_pool": cfg.proxy_pool
+                })
+            return status_list
     
     def subscribe_executor_logs(self, group_id: str, callback):
         """
         订阅某个 Executor 的日志
         callback: Callable[[str], None]
         """
-        if group_id not in self.executors:
-            raise ValueError(f"Executor {group_id} not running")
+        with self._lock:
+            if group_id not in self.executors:
+                raise ValueError(f"Executor {group_id} not running")
 
-        exe = self.executors[group_id]
-        exe.subscribe_logs(callback)
+            exe = self.executors[group_id]
+            exe.subscribe_logs(callback)
 
 
     def unsubscribe_executor_logs(self, group_id: str, callback):
         """
         取消订阅日志
         """
-        exe = self.executors.get(group_id)
-        if not exe:
-            return
-        exe.unsubscribe_logs(callback)
+        with self._lock:
+            exe = self.executors.get(group_id)
+            if not exe:
+                return
+            exe.unsubscribe_logs(callback)
 
     

+ 12 - 0
front/index.html

@@ -0,0 +1,12 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Visa Plugin Manager</title>
+  </head>
+  <body>
+    <div id="root"></div>
+    <script type="module" src="/src/main.jsx"></script>
+  </body>
+</html>

+ 6068 - 0
front/package-lock.json

@@ -0,0 +1,6068 @@
+{
+    "name": "visa-plugin-manager",
+    "version": "0.1.0",
+    "lockfileVersion": 3,
+    "requires": true,
+    "packages": {
+        "": {
+            "name": "visa-plugin-manager",
+            "version": "0.1.0",
+            "dependencies": {
+                "@ant-design/icons": "^5.6.1",
+                "@monaco-editor/react": "^4.7.0",
+                "@tanstack/react-query": "^5.90.16",
+                "antd": "^5.29.3",
+                "axios": "^1.13.2",
+                "react": "^18.2.0",
+                "react-dom": "^18.2.0"
+            },
+            "devDependencies": {
+                "@types/react": "^18.2.43",
+                "@types/react-dom": "^18.2.17",
+                "@vitejs/plugin-react": "^4.2.1",
+                "eslint": "^8.55.0",
+                "eslint-plugin-react": "^7.33.2",
+                "eslint-plugin-react-hooks": "^4.6.0",
+                "eslint-plugin-react-refresh": "^0.4.5",
+                "vite": "^5.0.8"
+            }
+        },
+        "node_modules/@ant-design/colors": {
+            "version": "7.2.1",
+            "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-7.2.1.tgz",
+            "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==",
+            "license": "MIT",
+            "dependencies": {
+                "@ant-design/fast-color": "^2.0.6"
+            }
+        },
+        "node_modules/@ant-design/cssinjs": {
+            "version": "1.24.0",
+            "resolved": "https://registry.npmmirror.com/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz",
+            "integrity": "sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.11.1",
+                "@emotion/hash": "^0.8.0",
+                "@emotion/unitless": "^0.7.5",
+                "classnames": "^2.3.1",
+                "csstype": "^3.1.3",
+                "rc-util": "^5.35.0",
+                "stylis": "^4.3.4"
+            },
+            "peerDependencies": {
+                "react": ">=16.0.0",
+                "react-dom": ">=16.0.0"
+            }
+        },
+        "node_modules/@ant-design/cssinjs-utils": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmmirror.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz",
+            "integrity": "sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==",
+            "license": "MIT",
+            "dependencies": {
+                "@ant-design/cssinjs": "^1.21.0",
+                "@babel/runtime": "^7.23.2",
+                "rc-util": "^5.38.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/@ant-design/fast-color": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmmirror.com/@ant-design/fast-color/-/fast-color-2.0.6.tgz",
+            "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.24.7"
+            },
+            "engines": {
+                "node": ">=8.x"
+            }
+        },
+        "node_modules/@ant-design/icons": {
+            "version": "5.6.1",
+            "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz",
+            "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==",
+            "license": "MIT",
+            "dependencies": {
+                "@ant-design/colors": "^7.0.0",
+                "@ant-design/icons-svg": "^4.4.0",
+                "@babel/runtime": "^7.24.8",
+                "classnames": "^2.2.6",
+                "rc-util": "^5.31.1"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "peerDependencies": {
+                "react": ">=16.0.0",
+                "react-dom": ">=16.0.0"
+            }
+        },
+        "node_modules/@ant-design/icons-svg": {
+            "version": "4.4.2",
+            "resolved": "https://registry.npmmirror.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz",
+            "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==",
+            "license": "MIT"
+        },
+        "node_modules/@ant-design/react-slick": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmmirror.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz",
+            "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.4",
+                "classnames": "^2.2.5",
+                "json2mq": "^0.2.0",
+                "resize-observer-polyfill": "^1.5.1",
+                "throttle-debounce": "^5.0.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0"
+            }
+        },
+        "node_modules/@babel/code-frame": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.28.6.tgz",
+            "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/helper-validator-identifier": "^7.28.5",
+                "js-tokens": "^4.0.0",
+                "picocolors": "^1.1.1"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/compat-data": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.6.tgz",
+            "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/core": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.28.6.tgz",
+            "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/code-frame": "^7.28.6",
+                "@babel/generator": "^7.28.6",
+                "@babel/helper-compilation-targets": "^7.28.6",
+                "@babel/helper-module-transforms": "^7.28.6",
+                "@babel/helpers": "^7.28.6",
+                "@babel/parser": "^7.28.6",
+                "@babel/template": "^7.28.6",
+                "@babel/traverse": "^7.28.6",
+                "@babel/types": "^7.28.6",
+                "@jridgewell/remapping": "^2.3.5",
+                "convert-source-map": "^2.0.0",
+                "debug": "^4.1.0",
+                "gensync": "^1.0.0-beta.2",
+                "json5": "^2.2.3",
+                "semver": "^6.3.1"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/babel"
+            }
+        },
+        "node_modules/@babel/generator": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.6.tgz",
+            "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/parser": "^7.28.6",
+                "@babel/types": "^7.28.6",
+                "@jridgewell/gen-mapping": "^0.3.12",
+                "@jridgewell/trace-mapping": "^0.3.28",
+                "jsesc": "^3.0.2"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/helper-compilation-targets": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+            "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/compat-data": "^7.28.6",
+                "@babel/helper-validator-option": "^7.27.1",
+                "browserslist": "^4.24.0",
+                "lru-cache": "^5.1.1",
+                "semver": "^6.3.1"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/helper-globals": {
+            "version": "7.28.0",
+            "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+            "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/helper-module-imports": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+            "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/traverse": "^7.28.6",
+                "@babel/types": "^7.28.6"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/helper-module-transforms": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+            "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/helper-module-imports": "^7.28.6",
+                "@babel/helper-validator-identifier": "^7.28.5",
+                "@babel/traverse": "^7.28.6"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            },
+            "peerDependencies": {
+                "@babel/core": "^7.0.0"
+            }
+        },
+        "node_modules/@babel/helper-plugin-utils": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
+            "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/helper-string-parser": {
+            "version": "7.27.1",
+            "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+            "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/helper-validator-identifier": {
+            "version": "7.28.5",
+            "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+            "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/helper-validator-option": {
+            "version": "7.27.1",
+            "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+            "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/helpers": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.28.6.tgz",
+            "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/template": "^7.28.6",
+                "@babel/types": "^7.28.6"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/parser": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.6.tgz",
+            "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/types": "^7.28.6"
+            },
+            "bin": {
+                "parser": "bin/babel-parser.js"
+            },
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/@babel/plugin-transform-react-jsx-self": {
+            "version": "7.27.1",
+            "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
+            "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/helper-plugin-utils": "^7.27.1"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            },
+            "peerDependencies": {
+                "@babel/core": "^7.0.0-0"
+            }
+        },
+        "node_modules/@babel/plugin-transform-react-jsx-source": {
+            "version": "7.27.1",
+            "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
+            "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/helper-plugin-utils": "^7.27.1"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            },
+            "peerDependencies": {
+                "@babel/core": "^7.0.0-0"
+            }
+        },
+        "node_modules/@babel/runtime": {
+            "version": "7.28.4",
+            "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.4.tgz",
+            "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/template": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.28.6.tgz",
+            "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/code-frame": "^7.28.6",
+                "@babel/parser": "^7.28.6",
+                "@babel/types": "^7.28.6"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/traverse": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.6.tgz",
+            "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/code-frame": "^7.28.6",
+                "@babel/generator": "^7.28.6",
+                "@babel/helper-globals": "^7.28.0",
+                "@babel/parser": "^7.28.6",
+                "@babel/template": "^7.28.6",
+                "@babel/types": "^7.28.6",
+                "debug": "^4.3.1"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/types": {
+            "version": "7.28.6",
+            "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.6.tgz",
+            "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/helper-string-parser": "^7.27.1",
+                "@babel/helper-validator-identifier": "^7.28.5"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@emotion/hash": {
+            "version": "0.8.0",
+            "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz",
+            "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
+            "license": "MIT"
+        },
+        "node_modules/@emotion/unitless": {
+            "version": "0.7.5",
+            "resolved": "https://registry.npmmirror.com/@emotion/unitless/-/unitless-0.7.5.tgz",
+            "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
+            "license": "MIT"
+        },
+        "node_modules/@esbuild/aix-ppc64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+            "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+            "cpu": [
+                "ppc64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "aix"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/android-arm": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+            "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+            "cpu": [
+                "arm"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "android"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/android-arm64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+            "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "android"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/android-x64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+            "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "android"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/darwin-arm64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+            "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "darwin"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/darwin-x64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+            "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "darwin"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/freebsd-arm64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+            "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "freebsd"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/freebsd-x64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+            "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "freebsd"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/linux-arm": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+            "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+            "cpu": [
+                "arm"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/linux-arm64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+            "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/linux-ia32": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+            "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+            "cpu": [
+                "ia32"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/linux-loong64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+            "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+            "cpu": [
+                "loong64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/linux-mips64el": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+            "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+            "cpu": [
+                "mips64el"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/linux-ppc64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+            "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+            "cpu": [
+                "ppc64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/linux-riscv64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+            "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+            "cpu": [
+                "riscv64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/linux-s390x": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+            "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+            "cpu": [
+                "s390x"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/linux-x64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+            "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/netbsd-x64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+            "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "netbsd"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/openbsd-x64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+            "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "openbsd"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/sunos-x64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+            "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "sunos"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/win32-arm64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+            "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "win32"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/win32-ia32": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+            "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+            "cpu": [
+                "ia32"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "win32"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@esbuild/win32-x64": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+            "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "win32"
+            ],
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@eslint-community/eslint-utils": {
+            "version": "4.9.1",
+            "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+            "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "eslint-visitor-keys": "^3.4.3"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            },
+            "peerDependencies": {
+                "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+            }
+        },
+        "node_modules/@eslint-community/regexpp": {
+            "version": "4.12.2",
+            "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+            "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+            }
+        },
+        "node_modules/@eslint/eslintrc": {
+            "version": "2.1.4",
+            "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+            "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "ajv": "^6.12.4",
+                "debug": "^4.3.2",
+                "espree": "^9.6.0",
+                "globals": "^13.19.0",
+                "ignore": "^5.2.0",
+                "import-fresh": "^3.2.1",
+                "js-yaml": "^4.1.0",
+                "minimatch": "^3.1.2",
+                "strip-json-comments": "^3.1.1"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/@eslint/js": {
+            "version": "8.57.1",
+            "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.57.1.tgz",
+            "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            }
+        },
+        "node_modules/@humanwhocodes/config-array": {
+            "version": "0.13.0",
+            "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,
+            "license": "Apache-2.0",
+            "dependencies": {
+                "@humanwhocodes/object-schema": "^2.0.3",
+                "debug": "^4.3.1",
+                "minimatch": "^3.0.5"
+            },
+            "engines": {
+                "node": ">=10.10.0"
+            }
+        },
+        "node_modules/@humanwhocodes/module-importer": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+            "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+            "dev": true,
+            "license": "Apache-2.0",
+            "engines": {
+                "node": ">=12.22"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/nzakas"
+            }
+        },
+        "node_modules/@humanwhocodes/object-schema": {
+            "version": "2.0.3",
+            "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,
+            "license": "BSD-3-Clause"
+        },
+        "node_modules/@jridgewell/gen-mapping": {
+            "version": "0.3.13",
+            "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",
+            "dependencies": {
+                "@jridgewell/sourcemap-codec": "^1.5.0",
+                "@jridgewell/trace-mapping": "^0.3.24"
+            }
+        },
+        "node_modules/@jridgewell/remapping": {
+            "version": "2.3.5",
+            "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+            "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@jridgewell/gen-mapping": "^0.3.5",
+                "@jridgewell/trace-mapping": "^0.3.24"
+            }
+        },
+        "node_modules/@jridgewell/resolve-uri": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+            "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/@jridgewell/sourcemap-codec": {
+            "version": "1.5.5",
+            "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+            "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/@jridgewell/trace-mapping": {
+            "version": "0.3.31",
+            "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+            "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@jridgewell/resolve-uri": "^3.1.0",
+                "@jridgewell/sourcemap-codec": "^1.4.14"
+            }
+        },
+        "node_modules/@monaco-editor/loader": {
+            "version": "1.7.0",
+            "resolved": "https://registry.npmmirror.com/@monaco-editor/loader/-/loader-1.7.0.tgz",
+            "integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==",
+            "license": "MIT",
+            "dependencies": {
+                "state-local": "^1.0.6"
+            }
+        },
+        "node_modules/@monaco-editor/react": {
+            "version": "4.7.0",
+            "resolved": "https://registry.npmmirror.com/@monaco-editor/react/-/react-4.7.0.tgz",
+            "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
+            "license": "MIT",
+            "dependencies": {
+                "@monaco-editor/loader": "^1.5.0"
+            },
+            "peerDependencies": {
+                "monaco-editor": ">= 0.25.0 < 1",
+                "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+                "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+            }
+        },
+        "node_modules/@nodelib/fs.scandir": {
+            "version": "2.1.5",
+            "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+            "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@nodelib/fs.stat": "2.0.5",
+                "run-parallel": "^1.1.9"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@nodelib/fs.stat": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+            "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@nodelib/fs.walk": {
+            "version": "1.2.8",
+            "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+            "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@nodelib/fs.scandir": "2.1.5",
+                "fastq": "^1.6.0"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@rc-component/async-validator": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmmirror.com/@rc-component/async-validator/-/async-validator-5.1.0.tgz",
+            "integrity": "sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.24.4"
+            },
+            "engines": {
+                "node": ">=14.x"
+            }
+        },
+        "node_modules/@rc-component/color-picker": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmmirror.com/@rc-component/color-picker/-/color-picker-2.0.1.tgz",
+            "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==",
+            "license": "MIT",
+            "dependencies": {
+                "@ant-design/fast-color": "^2.0.6",
+                "@babel/runtime": "^7.23.6",
+                "classnames": "^2.2.6",
+                "rc-util": "^5.38.1"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/@rc-component/context": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmmirror.com/@rc-component/context/-/context-1.4.0.tgz",
+            "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "rc-util": "^5.27.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/@rc-component/mini-decimal": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz",
+            "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.18.0"
+            },
+            "engines": {
+                "node": ">=8.x"
+            }
+        },
+        "node_modules/@rc-component/mutate-observer": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz",
+            "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.18.0",
+                "classnames": "^2.3.2",
+                "rc-util": "^5.24.4"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/@rc-component/portal": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmmirror.com/@rc-component/portal/-/portal-1.1.2.tgz",
+            "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.18.0",
+                "classnames": "^2.3.2",
+                "rc-util": "^5.24.4"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/@rc-component/qrcode": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmmirror.com/@rc-component/qrcode/-/qrcode-1.1.1.tgz",
+            "integrity": "sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.24.7"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/@rc-component/tour": {
+            "version": "1.15.1",
+            "resolved": "https://registry.npmmirror.com/@rc-component/tour/-/tour-1.15.1.tgz",
+            "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.18.0",
+                "@rc-component/portal": "^1.0.0-9",
+                "@rc-component/trigger": "^2.0.0",
+                "classnames": "^2.3.2",
+                "rc-util": "^5.24.4"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/@rc-component/trigger": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmmirror.com/@rc-component/trigger/-/trigger-2.3.1.tgz",
+            "integrity": "sha512-ORENF39PeXTzM+gQEshuk460Z8N4+6DkjpxlpE7Q3gYy1iBpLrx0FOJz3h62ryrJZ/3zCAUIkT1Pb/8hHWpb3A==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.23.2",
+                "@rc-component/portal": "^1.1.0",
+                "classnames": "^2.3.2",
+                "rc-motion": "^2.0.0",
+                "rc-resize-observer": "^1.3.1",
+                "rc-util": "^5.44.0"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/@rolldown/pluginutils": {
+            "version": "1.0.0-beta.27",
+            "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
+            "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/@rollup/rollup-android-arm-eabi": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz",
+            "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==",
+            "cpu": [
+                "arm"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "android"
+            ]
+        },
+        "node_modules/@rollup/rollup-android-arm64": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz",
+            "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "android"
+            ]
+        },
+        "node_modules/@rollup/rollup-darwin-arm64": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz",
+            "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "darwin"
+            ]
+        },
+        "node_modules/@rollup/rollup-darwin-x64": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz",
+            "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "darwin"
+            ]
+        },
+        "node_modules/@rollup/rollup-freebsd-arm64": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz",
+            "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "freebsd"
+            ]
+        },
+        "node_modules/@rollup/rollup-freebsd-x64": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz",
+            "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "freebsd"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz",
+            "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==",
+            "cpu": [
+                "arm"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz",
+            "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==",
+            "cpu": [
+                "arm"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-arm64-gnu": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz",
+            "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-arm64-musl": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz",
+            "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-loong64-gnu": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz",
+            "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==",
+            "cpu": [
+                "loong64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-loong64-musl": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz",
+            "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==",
+            "cpu": [
+                "loong64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz",
+            "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==",
+            "cpu": [
+                "ppc64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-ppc64-musl": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz",
+            "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==",
+            "cpu": [
+                "ppc64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz",
+            "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==",
+            "cpu": [
+                "riscv64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-riscv64-musl": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz",
+            "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==",
+            "cpu": [
+                "riscv64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-s390x-gnu": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz",
+            "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==",
+            "cpu": [
+                "s390x"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-x64-gnu": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz",
+            "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-linux-x64-musl": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz",
+            "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "linux"
+            ]
+        },
+        "node_modules/@rollup/rollup-openbsd-x64": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz",
+            "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "openbsd"
+            ]
+        },
+        "node_modules/@rollup/rollup-openharmony-arm64": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz",
+            "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "openharmony"
+            ]
+        },
+        "node_modules/@rollup/rollup-win32-arm64-msvc": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz",
+            "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==",
+            "cpu": [
+                "arm64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "win32"
+            ]
+        },
+        "node_modules/@rollup/rollup-win32-ia32-msvc": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz",
+            "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==",
+            "cpu": [
+                "ia32"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "win32"
+            ]
+        },
+        "node_modules/@rollup/rollup-win32-x64-gnu": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz",
+            "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "win32"
+            ]
+        },
+        "node_modules/@rollup/rollup-win32-x64-msvc": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz",
+            "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==",
+            "cpu": [
+                "x64"
+            ],
+            "dev": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "win32"
+            ]
+        },
+        "node_modules/@tanstack/query-core": {
+            "version": "5.90.16",
+            "resolved": "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.90.16.tgz",
+            "integrity": "sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww==",
+            "license": "MIT",
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/tannerlinsley"
+            }
+        },
+        "node_modules/@tanstack/react-query": {
+            "version": "5.90.16",
+            "resolved": "https://registry.npmmirror.com/@tanstack/react-query/-/react-query-5.90.16.tgz",
+            "integrity": "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ==",
+            "license": "MIT",
+            "dependencies": {
+                "@tanstack/query-core": "5.90.16"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/tannerlinsley"
+            },
+            "peerDependencies": {
+                "react": "^18 || ^19"
+            }
+        },
+        "node_modules/@types/babel__core": {
+            "version": "7.20.5",
+            "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz",
+            "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/parser": "^7.20.7",
+                "@babel/types": "^7.20.7",
+                "@types/babel__generator": "*",
+                "@types/babel__template": "*",
+                "@types/babel__traverse": "*"
+            }
+        },
+        "node_modules/@types/babel__generator": {
+            "version": "7.27.0",
+            "resolved": "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+            "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/types": "^7.0.0"
+            }
+        },
+        "node_modules/@types/babel__template": {
+            "version": "7.4.4",
+            "resolved": "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz",
+            "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/parser": "^7.1.0",
+                "@babel/types": "^7.0.0"
+            }
+        },
+        "node_modules/@types/babel__traverse": {
+            "version": "7.28.0",
+            "resolved": "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+            "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/types": "^7.28.2"
+            }
+        },
+        "node_modules/@types/estree": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
+            "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/@types/prop-types": {
+            "version": "15.7.15",
+            "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.15.tgz",
+            "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/@types/react": {
+            "version": "18.3.27",
+            "resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.27.tgz",
+            "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@types/prop-types": "*",
+                "csstype": "^3.2.2"
+            }
+        },
+        "node_modules/@types/react-dom": {
+            "version": "18.3.7",
+            "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz",
+            "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
+            "dev": true,
+            "license": "MIT",
+            "peerDependencies": {
+                "@types/react": "^18.0.0"
+            }
+        },
+        "node_modules/@types/trusted-types": {
+            "version": "2.0.7",
+            "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+            "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+            "license": "MIT",
+            "optional": true,
+            "peer": true
+        },
+        "node_modules/@ungap/structured-clone": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+            "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+            "dev": true,
+            "license": "ISC"
+        },
+        "node_modules/@vitejs/plugin-react": {
+            "version": "4.7.0",
+            "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+            "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@babel/core": "^7.28.0",
+                "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+                "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+                "@rolldown/pluginutils": "1.0.0-beta.27",
+                "@types/babel__core": "^7.20.5",
+                "react-refresh": "^0.17.0"
+            },
+            "engines": {
+                "node": "^14.18.0 || >=16.0.0"
+            },
+            "peerDependencies": {
+                "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+            }
+        },
+        "node_modules/acorn": {
+            "version": "8.15.0",
+            "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
+            "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+            "dev": true,
+            "license": "MIT",
+            "bin": {
+                "acorn": "bin/acorn"
+            },
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/acorn-jsx": {
+            "version": "5.3.2",
+            "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+            "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+            "dev": true,
+            "license": "MIT",
+            "peerDependencies": {
+                "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+            }
+        },
+        "node_modules/ajv": {
+            "version": "6.12.6",
+            "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
+            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.1",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
+                "uri-js": "^4.2.2"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/epoberezkin"
+            }
+        },
+        "node_modules/ansi-regex": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/ansi-styles": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+            "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "color-convert": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+            }
+        },
+        "node_modules/antd": {
+            "version": "5.29.3",
+            "resolved": "https://registry.npmmirror.com/antd/-/antd-5.29.3.tgz",
+            "integrity": "sha512-3DdbGCa9tWAJGcCJ6rzR8EJFsv2CtyEbkVabZE14pfgUHfCicWCj0/QzQVLDYg8CPfQk9BH7fHCoTXHTy7MP/A==",
+            "license": "MIT",
+            "dependencies": {
+                "@ant-design/colors": "^7.2.1",
+                "@ant-design/cssinjs": "^1.23.0",
+                "@ant-design/cssinjs-utils": "^1.1.3",
+                "@ant-design/fast-color": "^2.0.6",
+                "@ant-design/icons": "^5.6.1",
+                "@ant-design/react-slick": "~1.1.2",
+                "@babel/runtime": "^7.26.0",
+                "@rc-component/color-picker": "~2.0.1",
+                "@rc-component/mutate-observer": "^1.1.0",
+                "@rc-component/qrcode": "~1.1.0",
+                "@rc-component/tour": "~1.15.1",
+                "@rc-component/trigger": "^2.3.0",
+                "classnames": "^2.5.1",
+                "copy-to-clipboard": "^3.3.3",
+                "dayjs": "^1.11.11",
+                "rc-cascader": "~3.34.0",
+                "rc-checkbox": "~3.5.0",
+                "rc-collapse": "~3.9.0",
+                "rc-dialog": "~9.6.0",
+                "rc-drawer": "~7.3.0",
+                "rc-dropdown": "~4.2.1",
+                "rc-field-form": "~2.7.1",
+                "rc-image": "~7.12.0",
+                "rc-input": "~1.8.0",
+                "rc-input-number": "~9.5.0",
+                "rc-mentions": "~2.20.0",
+                "rc-menu": "~9.16.1",
+                "rc-motion": "^2.9.5",
+                "rc-notification": "~5.6.4",
+                "rc-pagination": "~5.1.0",
+                "rc-picker": "~4.11.3",
+                "rc-progress": "~4.0.0",
+                "rc-rate": "~2.13.1",
+                "rc-resize-observer": "^1.4.3",
+                "rc-segmented": "~2.7.0",
+                "rc-select": "~14.16.8",
+                "rc-slider": "~11.1.9",
+                "rc-steps": "~6.0.1",
+                "rc-switch": "~4.1.0",
+                "rc-table": "~7.54.0",
+                "rc-tabs": "~15.7.0",
+                "rc-textarea": "~1.10.2",
+                "rc-tooltip": "~6.4.0",
+                "rc-tree": "~5.13.1",
+                "rc-tree-select": "~5.27.0",
+                "rc-upload": "~4.11.0",
+                "rc-util": "^5.44.4",
+                "scroll-into-view-if-needed": "^3.1.0",
+                "throttle-debounce": "^5.0.2"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/ant-design"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/argparse": {
+            "version": "2.0.1",
+            "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/array-buffer-byte-length": {
+            "version": "1.0.2",
+            "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",
+            "dependencies": {
+                "call-bound": "^1.0.3",
+                "is-array-buffer": "^3.0.5"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/array-includes": {
+            "version": "3.1.9",
+            "resolved": "https://registry.npmmirror.com/array-includes/-/array-includes-3.1.9.tgz",
+            "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.4",
+                "define-properties": "^1.2.1",
+                "es-abstract": "^1.24.0",
+                "es-object-atoms": "^1.1.1",
+                "get-intrinsic": "^1.3.0",
+                "is-string": "^1.1.1",
+                "math-intrinsics": "^1.1.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/array.prototype.findlast": {
+            "version": "1.2.5",
+            "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",
+            "dependencies": {
+                "call-bind": "^1.0.7",
+                "define-properties": "^1.2.1",
+                "es-abstract": "^1.23.2",
+                "es-errors": "^1.3.0",
+                "es-object-atoms": "^1.0.0",
+                "es-shim-unscopables": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/array.prototype.flat": {
+            "version": "1.3.3",
+            "resolved": "https://registry.npmmirror.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+            "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "define-properties": "^1.2.1",
+                "es-abstract": "^1.23.5",
+                "es-shim-unscopables": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/array.prototype.flatmap": {
+            "version": "1.3.3",
+            "resolved": "https://registry.npmmirror.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+            "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "define-properties": "^1.2.1",
+                "es-abstract": "^1.23.5",
+                "es-shim-unscopables": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/array.prototype.tosorted": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmmirror.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+            "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.7",
+                "define-properties": "^1.2.1",
+                "es-abstract": "^1.23.3",
+                "es-errors": "^1.3.0",
+                "es-shim-unscopables": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/arraybuffer.prototype.slice": {
+            "version": "1.0.4",
+            "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",
+            "dependencies": {
+                "array-buffer-byte-length": "^1.0.1",
+                "call-bind": "^1.0.8",
+                "define-properties": "^1.2.1",
+                "es-abstract": "^1.23.5",
+                "es-errors": "^1.3.0",
+                "get-intrinsic": "^1.2.6",
+                "is-array-buffer": "^3.0.4"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/async-function": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmmirror.com/async-function/-/async-function-1.0.0.tgz",
+            "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/asynckit": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+            "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+            "license": "MIT"
+        },
+        "node_modules/available-typed-arrays": {
+            "version": "1.0.7",
+            "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",
+            "dependencies": {
+                "possible-typed-array-names": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/axios": {
+            "version": "1.13.2",
+            "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.2.tgz",
+            "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
+            "license": "MIT",
+            "dependencies": {
+                "follow-redirects": "^1.15.6",
+                "form-data": "^4.0.4",
+                "proxy-from-env": "^1.1.0"
+            }
+        },
+        "node_modules/balanced-match": {
+            "version": "1.0.2",
+            "resolved": "https://registry.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.14",
+            "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz",
+            "integrity": "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==",
+            "dev": true,
+            "license": "Apache-2.0",
+            "bin": {
+                "baseline-browser-mapping": "dist/cli.js"
+            }
+        },
+        "node_modules/brace-expansion": {
+            "version": "1.1.12",
+            "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz",
+            "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
+            }
+        },
+        "node_modules/browserslist": {
+            "version": "4.28.1",
+            "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz",
+            "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/browserslist"
+                },
+                {
+                    "type": "tidelift",
+                    "url": "https://tidelift.com/funding/github/npm/browserslist"
+                },
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/ai"
+                }
+            ],
+            "license": "MIT",
+            "dependencies": {
+                "baseline-browser-mapping": "^2.9.0",
+                "caniuse-lite": "^1.0.30001759",
+                "electron-to-chromium": "^1.5.263",
+                "node-releases": "^2.0.27",
+                "update-browserslist-db": "^1.2.0"
+            },
+            "bin": {
+                "browserslist": "cli.js"
+            },
+            "engines": {
+                "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+            }
+        },
+        "node_modules/call-bind": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz",
+            "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind-apply-helpers": "^1.0.0",
+                "es-define-property": "^1.0.0",
+                "get-intrinsic": "^1.2.4",
+                "set-function-length": "^1.2.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/call-bind-apply-helpers": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+            "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+            "license": "MIT",
+            "dependencies": {
+                "es-errors": "^1.3.0",
+                "function-bind": "^1.1.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/call-bound": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz",
+            "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind-apply-helpers": "^1.0.2",
+                "get-intrinsic": "^1.3.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/callsites": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
+            "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/caniuse-lite": {
+            "version": "1.0.30001764",
+            "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz",
+            "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/browserslist"
+                },
+                {
+                    "type": "tidelift",
+                    "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+                },
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/ai"
+                }
+            ],
+            "license": "CC-BY-4.0"
+        },
+        "node_modules/chalk": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+            "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-styles": "^4.1.0",
+                "supports-color": "^7.1.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/chalk?sponsor=1"
+            }
+        },
+        "node_modules/classnames": {
+            "version": "2.5.1",
+            "resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz",
+            "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+            "license": "MIT"
+        },
+        "node_modules/color-convert": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+            "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "color-name": "~1.1.4"
+            },
+            "engines": {
+                "node": ">=7.0.0"
+            }
+        },
+        "node_modules/color-name": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+            "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/combined-stream": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+            "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+            "license": "MIT",
+            "dependencies": {
+                "delayed-stream": "~1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/compute-scroll-into-view": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
+            "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==",
+            "license": "MIT"
+        },
+        "node_modules/concat-map": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+            "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/convert-source-map": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
+            "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/copy-to-clipboard": {
+            "version": "3.3.3",
+            "resolved": "https://registry.npmmirror.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+            "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+            "license": "MIT",
+            "dependencies": {
+                "toggle-selection": "^1.0.6"
+            }
+        },
+        "node_modules/cross-spawn": {
+            "version": "7.0.6",
+            "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
+            "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "path-key": "^3.1.0",
+                "shebang-command": "^2.0.0",
+                "which": "^2.0.1"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/csstype": {
+            "version": "3.2.3",
+            "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
+            "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+            "license": "MIT"
+        },
+        "node_modules/data-view-buffer": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+            "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.3",
+                "es-errors": "^1.3.0",
+                "is-data-view": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/data-view-byte-length": {
+            "version": "1.0.2",
+            "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",
+            "dependencies": {
+                "call-bound": "^1.0.3",
+                "es-errors": "^1.3.0",
+                "is-data-view": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/inspect-js"
+            }
+        },
+        "node_modules/data-view-byte-offset": {
+            "version": "1.0.1",
+            "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",
+            "dependencies": {
+                "call-bound": "^1.0.2",
+                "es-errors": "^1.3.0",
+                "is-data-view": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/dayjs": {
+            "version": "1.11.19",
+            "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz",
+            "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
+            "license": "MIT"
+        },
+        "node_modules/debug": {
+            "version": "4.4.3",
+            "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
+            "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "ms": "^2.1.3"
+            },
+            "engines": {
+                "node": ">=6.0"
+            },
+            "peerDependenciesMeta": {
+                "supports-color": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/deep-is": {
+            "version": "0.1.4",
+            "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.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",
+            "dependencies": {
+                "es-define-property": "^1.0.0",
+                "es-errors": "^1.3.0",
+                "gopd": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/define-properties": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz",
+            "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "define-data-property": "^1.0.1",
+                "has-property-descriptors": "^1.0.0",
+                "object-keys": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/delayed-stream": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+            "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/doctrine": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz",
+            "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+            "dev": true,
+            "license": "Apache-2.0",
+            "dependencies": {
+                "esutils": "^2.0.2"
+            },
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/dompurify": {
+            "version": "3.2.7",
+            "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.2.7.tgz",
+            "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==",
+            "license": "(MPL-2.0 OR Apache-2.0)",
+            "peer": true,
+            "optionalDependencies": {
+                "@types/trusted-types": "^2.0.7"
+            }
+        },
+        "node_modules/dunder-proto": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
+            "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+            "license": "MIT",
+            "dependencies": {
+                "call-bind-apply-helpers": "^1.0.1",
+                "es-errors": "^1.3.0",
+                "gopd": "^1.2.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/electron-to-chromium": {
+            "version": "1.5.267",
+            "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+            "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+            "dev": true,
+            "license": "ISC"
+        },
+        "node_modules/es-abstract": {
+            "version": "1.24.1",
+            "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.1.tgz",
+            "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "array-buffer-byte-length": "^1.0.2",
+                "arraybuffer.prototype.slice": "^1.0.4",
+                "available-typed-arrays": "^1.0.7",
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.4",
+                "data-view-buffer": "^1.0.2",
+                "data-view-byte-length": "^1.0.2",
+                "data-view-byte-offset": "^1.0.1",
+                "es-define-property": "^1.0.1",
+                "es-errors": "^1.3.0",
+                "es-object-atoms": "^1.1.1",
+                "es-set-tostringtag": "^2.1.0",
+                "es-to-primitive": "^1.3.0",
+                "function.prototype.name": "^1.1.8",
+                "get-intrinsic": "^1.3.0",
+                "get-proto": "^1.0.1",
+                "get-symbol-description": "^1.1.0",
+                "globalthis": "^1.0.4",
+                "gopd": "^1.2.0",
+                "has-property-descriptors": "^1.0.2",
+                "has-proto": "^1.2.0",
+                "has-symbols": "^1.1.0",
+                "hasown": "^2.0.2",
+                "internal-slot": "^1.1.0",
+                "is-array-buffer": "^3.0.5",
+                "is-callable": "^1.2.7",
+                "is-data-view": "^1.0.2",
+                "is-negative-zero": "^2.0.3",
+                "is-regex": "^1.2.1",
+                "is-set": "^2.0.3",
+                "is-shared-array-buffer": "^1.0.4",
+                "is-string": "^1.1.1",
+                "is-typed-array": "^1.1.15",
+                "is-weakref": "^1.1.1",
+                "math-intrinsics": "^1.1.0",
+                "object-inspect": "^1.13.4",
+                "object-keys": "^1.1.1",
+                "object.assign": "^4.1.7",
+                "own-keys": "^1.0.1",
+                "regexp.prototype.flags": "^1.5.4",
+                "safe-array-concat": "^1.1.3",
+                "safe-push-apply": "^1.0.0",
+                "safe-regex-test": "^1.1.0",
+                "set-proto": "^1.0.0",
+                "stop-iteration-iterator": "^1.1.0",
+                "string.prototype.trim": "^1.2.10",
+                "string.prototype.trimend": "^1.0.9",
+                "string.prototype.trimstart": "^1.0.8",
+                "typed-array-buffer": "^1.0.3",
+                "typed-array-byte-length": "^1.0.3",
+                "typed-array-byte-offset": "^1.0.4",
+                "typed-array-length": "^1.0.7",
+                "unbox-primitive": "^1.1.0",
+                "which-typed-array": "^1.1.19"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/es-define-property": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
+            "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/es-errors": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
+            "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/es-iterator-helpers": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmmirror.com/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz",
+            "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.4",
+                "define-properties": "^1.2.1",
+                "es-abstract": "^1.24.1",
+                "es-errors": "^1.3.0",
+                "es-set-tostringtag": "^2.1.0",
+                "function-bind": "^1.1.2",
+                "get-intrinsic": "^1.3.0",
+                "globalthis": "^1.0.4",
+                "gopd": "^1.2.0",
+                "has-property-descriptors": "^1.0.2",
+                "has-proto": "^1.2.0",
+                "has-symbols": "^1.1.0",
+                "internal-slot": "^1.1.0",
+                "iterator.prototype": "^1.1.5",
+                "safe-array-concat": "^1.1.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/es-object-atoms": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+            "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+            "license": "MIT",
+            "dependencies": {
+                "es-errors": "^1.3.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/es-set-tostringtag": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+            "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+            "license": "MIT",
+            "dependencies": {
+                "es-errors": "^1.3.0",
+                "get-intrinsic": "^1.2.6",
+                "has-tostringtag": "^1.0.2",
+                "hasown": "^2.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/es-shim-unscopables": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+            "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "hasown": "^2.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/es-to-primitive": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+            "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "is-callable": "^1.2.7",
+                "is-date-object": "^1.0.5",
+                "is-symbol": "^1.0.4"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/esbuild": {
+            "version": "0.21.5",
+            "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
+            "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+            "dev": true,
+            "hasInstallScript": true,
+            "license": "MIT",
+            "bin": {
+                "esbuild": "bin/esbuild"
+            },
+            "engines": {
+                "node": ">=12"
+            },
+            "optionalDependencies": {
+                "@esbuild/aix-ppc64": "0.21.5",
+                "@esbuild/android-arm": "0.21.5",
+                "@esbuild/android-arm64": "0.21.5",
+                "@esbuild/android-x64": "0.21.5",
+                "@esbuild/darwin-arm64": "0.21.5",
+                "@esbuild/darwin-x64": "0.21.5",
+                "@esbuild/freebsd-arm64": "0.21.5",
+                "@esbuild/freebsd-x64": "0.21.5",
+                "@esbuild/linux-arm": "0.21.5",
+                "@esbuild/linux-arm64": "0.21.5",
+                "@esbuild/linux-ia32": "0.21.5",
+                "@esbuild/linux-loong64": "0.21.5",
+                "@esbuild/linux-mips64el": "0.21.5",
+                "@esbuild/linux-ppc64": "0.21.5",
+                "@esbuild/linux-riscv64": "0.21.5",
+                "@esbuild/linux-s390x": "0.21.5",
+                "@esbuild/linux-x64": "0.21.5",
+                "@esbuild/netbsd-x64": "0.21.5",
+                "@esbuild/openbsd-x64": "0.21.5",
+                "@esbuild/sunos-x64": "0.21.5",
+                "@esbuild/win32-arm64": "0.21.5",
+                "@esbuild/win32-ia32": "0.21.5",
+                "@esbuild/win32-x64": "0.21.5"
+            }
+        },
+        "node_modules/escalade": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
+            "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/escape-string-regexp": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+            "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/eslint": {
+            "version": "8.57.1",
+            "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,
+            "license": "MIT",
+            "dependencies": {
+                "@eslint-community/eslint-utils": "^4.2.0",
+                "@eslint-community/regexpp": "^4.6.1",
+                "@eslint/eslintrc": "^2.1.4",
+                "@eslint/js": "8.57.1",
+                "@humanwhocodes/config-array": "^0.13.0",
+                "@humanwhocodes/module-importer": "^1.0.1",
+                "@nodelib/fs.walk": "^1.2.8",
+                "@ungap/structured-clone": "^1.2.0",
+                "ajv": "^6.12.4",
+                "chalk": "^4.0.0",
+                "cross-spawn": "^7.0.2",
+                "debug": "^4.3.2",
+                "doctrine": "^3.0.0",
+                "escape-string-regexp": "^4.0.0",
+                "eslint-scope": "^7.2.2",
+                "eslint-visitor-keys": "^3.4.3",
+                "espree": "^9.6.1",
+                "esquery": "^1.4.2",
+                "esutils": "^2.0.2",
+                "fast-deep-equal": "^3.1.3",
+                "file-entry-cache": "^6.0.1",
+                "find-up": "^5.0.0",
+                "glob-parent": "^6.0.2",
+                "globals": "^13.19.0",
+                "graphemer": "^1.4.0",
+                "ignore": "^5.2.0",
+                "imurmurhash": "^0.1.4",
+                "is-glob": "^4.0.0",
+                "is-path-inside": "^3.0.3",
+                "js-yaml": "^4.1.0",
+                "json-stable-stringify-without-jsonify": "^1.0.1",
+                "levn": "^0.4.1",
+                "lodash.merge": "^4.6.2",
+                "minimatch": "^3.1.2",
+                "natural-compare": "^1.4.0",
+                "optionator": "^0.9.3",
+                "strip-ansi": "^6.0.1",
+                "text-table": "^0.2.0"
+            },
+            "bin": {
+                "eslint": "bin/eslint.js"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/eslint-plugin-react": {
+            "version": "7.37.5",
+            "resolved": "https://registry.npmmirror.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+            "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "array-includes": "^3.1.8",
+                "array.prototype.findlast": "^1.2.5",
+                "array.prototype.flatmap": "^1.3.3",
+                "array.prototype.tosorted": "^1.1.4",
+                "doctrine": "^2.1.0",
+                "es-iterator-helpers": "^1.2.1",
+                "estraverse": "^5.3.0",
+                "hasown": "^2.0.2",
+                "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+                "minimatch": "^3.1.2",
+                "object.entries": "^1.1.9",
+                "object.fromentries": "^2.0.8",
+                "object.values": "^1.2.1",
+                "prop-types": "^15.8.1",
+                "resolve": "^2.0.0-next.5",
+                "semver": "^6.3.1",
+                "string.prototype.matchall": "^4.0.12",
+                "string.prototype.repeat": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            },
+            "peerDependencies": {
+                "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+            }
+        },
+        "node_modules/eslint-plugin-react-hooks": {
+            "version": "4.6.2",
+            "resolved": "https://registry.npmmirror.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz",
+            "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=10"
+            },
+            "peerDependencies": {
+                "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
+            }
+        },
+        "node_modules/eslint-plugin-react-refresh": {
+            "version": "0.4.26",
+            "resolved": "https://registry.npmmirror.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz",
+            "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==",
+            "dev": true,
+            "license": "MIT",
+            "peerDependencies": {
+                "eslint": ">=8.40"
+            }
+        },
+        "node_modules/eslint-plugin-react/node_modules/doctrine": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-2.1.0.tgz",
+            "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+            "dev": true,
+            "license": "Apache-2.0",
+            "dependencies": {
+                "esutils": "^2.0.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/eslint-scope": {
+            "version": "7.2.2",
+            "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",
+            "dependencies": {
+                "esrecurse": "^4.3.0",
+                "estraverse": "^5.2.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/eslint-visitor-keys": {
+            "version": "3.4.3",
+            "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",
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/espree": {
+            "version": "9.6.1",
+            "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz",
+            "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+            "dev": true,
+            "license": "BSD-2-Clause",
+            "dependencies": {
+                "acorn": "^8.9.0",
+                "acorn-jsx": "^5.3.2",
+                "eslint-visitor-keys": "^3.4.1"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
+            }
+        },
+        "node_modules/esquery": {
+            "version": "1.7.0",
+            "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",
+            "dependencies": {
+                "estraverse": "^5.1.0"
+            },
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/esrecurse": {
+            "version": "4.3.0",
+            "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",
+            "dependencies": {
+                "estraverse": "^5.2.0"
+            },
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/estraverse": {
+            "version": "5.3.0",
+            "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
+            "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+            "dev": true,
+            "license": "BSD-2-Clause",
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/esutils": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
+            "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+            "dev": true,
+            "license": "BSD-2-Clause",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/fast-deep-equal": {
+            "version": "3.1.3",
+            "resolved": "https://registry.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-json-stable-stringify": {
+            "version": "2.1.0",
+            "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.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.npmmirror.com/fastq/-/fastq-1.20.1.tgz",
+            "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+            "dev": true,
+            "license": "ISC",
+            "dependencies": {
+                "reusify": "^1.0.4"
+            }
+        },
+        "node_modules/file-entry-cache": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+            "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "flat-cache": "^3.0.4"
+            },
+            "engines": {
+                "node": "^10.12.0 || >=12.0.0"
+            }
+        },
+        "node_modules/find-up": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz",
+            "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "locate-path": "^6.0.0",
+                "path-exists": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/flat-cache": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz",
+            "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "flatted": "^3.2.9",
+                "keyv": "^4.5.3",
+                "rimraf": "^3.0.2"
+            },
+            "engines": {
+                "node": "^10.12.0 || >=12.0.0"
+            }
+        },
+        "node_modules/flatted": {
+            "version": "3.3.3",
+            "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz",
+            "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+            "dev": true,
+            "license": "ISC"
+        },
+        "node_modules/follow-redirects": {
+            "version": "1.15.11",
+            "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
+            "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+            "funding": [
+                {
+                    "type": "individual",
+                    "url": "https://github.com/sponsors/RubenVerborgh"
+                }
+            ],
+            "license": "MIT",
+            "engines": {
+                "node": ">=4.0"
+            },
+            "peerDependenciesMeta": {
+                "debug": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/for-each": {
+            "version": "0.3.5",
+            "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz",
+            "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "is-callable": "^1.2.7"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/form-data": {
+            "version": "4.0.5",
+            "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
+            "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+            "license": "MIT",
+            "dependencies": {
+                "asynckit": "^0.4.0",
+                "combined-stream": "^1.0.8",
+                "es-set-tostringtag": "^2.1.0",
+                "hasown": "^2.0.2",
+                "mime-types": "^2.1.12"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/fs.realpath": {
+            "version": "1.0.0",
+            "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.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+            "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+            "dev": true,
+            "hasInstallScript": true,
+            "license": "MIT",
+            "optional": true,
+            "os": [
+                "darwin"
+            ],
+            "engines": {
+                "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+            }
+        },
+        "node_modules/function-bind": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+            "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+            "license": "MIT",
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/function.prototype.name": {
+            "version": "1.1.8",
+            "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+            "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.3",
+                "define-properties": "^1.2.1",
+                "functions-have-names": "^1.2.3",
+                "hasown": "^2.0.2",
+                "is-callable": "^1.2.7"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/functions-have-names": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz",
+            "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+            "dev": true,
+            "license": "MIT",
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/generator-function": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz",
+            "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/gensync": {
+            "version": "1.0.0-beta.2",
+            "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
+            "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/get-intrinsic": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+            "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+            "license": "MIT",
+            "dependencies": {
+                "call-bind-apply-helpers": "^1.0.2",
+                "es-define-property": "^1.0.1",
+                "es-errors": "^1.3.0",
+                "es-object-atoms": "^1.1.1",
+                "function-bind": "^1.1.2",
+                "get-proto": "^1.0.1",
+                "gopd": "^1.2.0",
+                "has-symbols": "^1.1.0",
+                "hasown": "^2.0.2",
+                "math-intrinsics": "^1.1.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/get-proto": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
+            "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+            "license": "MIT",
+            "dependencies": {
+                "dunder-proto": "^1.0.1",
+                "es-object-atoms": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/get-symbol-description": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+            "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.3",
+                "es-errors": "^1.3.0",
+                "get-intrinsic": "^1.2.6"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/glob": {
+            "version": "7.2.3",
+            "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
+            "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+            "deprecated": "Glob versions prior to v9 are no longer supported",
+            "dev": true,
+            "license": "ISC",
+            "dependencies": {
+                "fs.realpath": "^1.0.0",
+                "inflight": "^1.0.4",
+                "inherits": "2",
+                "minimatch": "^3.1.1",
+                "once": "^1.3.0",
+                "path-is-absolute": "^1.0.0"
+            },
+            "engines": {
+                "node": "*"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/glob-parent": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
+            "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+            "dev": true,
+            "license": "ISC",
+            "dependencies": {
+                "is-glob": "^4.0.3"
+            },
+            "engines": {
+                "node": ">=10.13.0"
+            }
+        },
+        "node_modules/globals": {
+            "version": "13.24.0",
+            "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz",
+            "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "type-fest": "^0.20.2"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/globalthis": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz",
+            "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "define-properties": "^1.2.1",
+                "gopd": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/gopd": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
+            "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/graphemer": {
+            "version": "1.4.0",
+            "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.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz",
+            "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-flag": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+            "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/has-property-descriptors": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+            "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "es-define-property": "^1.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-proto": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.2.0.tgz",
+            "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "dunder-proto": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-symbols": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
+            "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-tostringtag": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+            "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+            "license": "MIT",
+            "dependencies": {
+                "has-symbols": "^1.0.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/hasown": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
+            "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+            "license": "MIT",
+            "dependencies": {
+                "function-bind": "^1.1.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/ignore": {
+            "version": "5.3.2",
+            "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz",
+            "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/import-fresh": {
+            "version": "3.3.1",
+            "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz",
+            "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "parent-module": "^1.0.0",
+                "resolve-from": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/imurmurhash": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
+            "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.8.19"
+            }
+        },
+        "node_modules/inflight": {
+            "version": "1.0.6",
+            "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,
+            "license": "ISC",
+            "dependencies": {
+                "once": "^1.3.0",
+                "wrappy": "1"
+            }
+        },
+        "node_modules/inherits": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+            "dev": true,
+            "license": "ISC"
+        },
+        "node_modules/internal-slot": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz",
+            "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "es-errors": "^1.3.0",
+                "hasown": "^2.0.2",
+                "side-channel": "^1.1.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/is-array-buffer": {
+            "version": "3.0.5",
+            "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",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.3",
+                "get-intrinsic": "^1.2.6"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-async-function": {
+            "version": "2.1.1",
+            "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",
+            "dependencies": {
+                "async-function": "^1.0.0",
+                "call-bound": "^1.0.3",
+                "get-proto": "^1.0.1",
+                "has-tostringtag": "^1.0.2",
+                "safe-regex-test": "^1.1.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-bigint": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz",
+            "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "has-bigints": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-boolean-object": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+            "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.3",
+                "has-tostringtag": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-callable": {
+            "version": "1.2.7",
+            "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz",
+            "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-core-module": {
+            "version": "2.16.1",
+            "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz",
+            "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "hasown": "^2.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-data-view": {
+            "version": "1.0.2",
+            "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",
+            "dependencies": {
+                "call-bound": "^1.0.2",
+                "get-intrinsic": "^1.2.6",
+                "is-typed-array": "^1.1.13"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-date-object": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz",
+            "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.2",
+                "has-tostringtag": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-extglob": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+            "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-finalizationregistry": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+            "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-generator-function": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.2.tgz",
+            "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.4",
+                "generator-function": "^2.0.0",
+                "get-proto": "^1.0.1",
+                "has-tostringtag": "^1.0.2",
+                "safe-regex-test": "^1.1.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-glob": {
+            "version": "4.0.3",
+            "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+            "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "is-extglob": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-map": {
+            "version": "2.0.3",
+            "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",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-negative-zero": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+            "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-number-object": {
+            "version": "1.1.1",
+            "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",
+            "dependencies": {
+                "call-bound": "^1.0.3",
+                "has-tostringtag": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-path-inside": {
+            "version": "3.0.3",
+            "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",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/is-regex": {
+            "version": "1.2.1",
+            "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",
+            "dependencies": {
+                "call-bound": "^1.0.2",
+                "gopd": "^1.2.0",
+                "has-tostringtag": "^1.0.2",
+                "hasown": "^2.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-set": {
+            "version": "2.0.3",
+            "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",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-shared-array-buffer": {
+            "version": "1.0.4",
+            "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",
+            "dependencies": {
+                "call-bound": "^1.0.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-string": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.1.1.tgz",
+            "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.3",
+                "has-tostringtag": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-symbol": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.1.1.tgz",
+            "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.2",
+                "has-symbols": "^1.1.0",
+                "safe-regex-test": "^1.1.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array": {
+            "version": "1.1.15",
+            "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz",
+            "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "which-typed-array": "^1.1.16"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-weakmap": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.2.tgz",
+            "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-weakref": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.1.1.tgz",
+            "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-weakset": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz",
+            "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.3",
+                "get-intrinsic": "^1.2.6"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/isarray": {
+            "version": "2.0.5",
+            "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.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.npmmirror.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+            "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "define-data-property": "^1.1.4",
+                "es-object-atoms": "^1.0.0",
+                "get-intrinsic": "^1.2.6",
+                "get-proto": "^1.0.0",
+                "has-symbols": "^1.1.0",
+                "set-function-name": "^2.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/js-tokens": {
+            "version": "4.0.0",
+            "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.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz",
+            "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "argparse": "^2.0.1"
+            },
+            "bin": {
+                "js-yaml": "bin/js-yaml.js"
+            }
+        },
+        "node_modules/jsesc": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
+            "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+            "dev": true,
+            "license": "MIT",
+            "bin": {
+                "jsesc": "bin/jsesc"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/json-buffer": {
+            "version": "3.0.1",
+            "resolved": "https://registry.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.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.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/json2mq": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmmirror.com/json2mq/-/json2mq-0.2.0.tgz",
+            "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
+            "license": "MIT",
+            "dependencies": {
+                "string-convert": "^0.2.0"
+            }
+        },
+        "node_modules/json5": {
+            "version": "2.2.3",
+            "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
+            "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+            "dev": true,
+            "license": "MIT",
+            "bin": {
+                "json5": "lib/cli.js"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/jsx-ast-utils": {
+            "version": "3.3.5",
+            "resolved": "https://registry.npmmirror.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+            "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "array-includes": "^3.1.6",
+                "array.prototype.flat": "^1.3.1",
+                "object.assign": "^4.1.4",
+                "object.values": "^1.1.6"
+            },
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/keyv": {
+            "version": "4.5.4",
+            "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
+            "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "json-buffer": "3.0.1"
+            }
+        },
+        "node_modules/levn": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz",
+            "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "prelude-ls": "^1.2.1",
+                "type-check": "~0.4.0"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/locate-path": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz",
+            "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "p-locate": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/lodash.merge": {
+            "version": "4.6.2",
+            "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/loose-envify": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz",
+            "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+            "license": "MIT",
+            "dependencies": {
+                "js-tokens": "^3.0.0 || ^4.0.0"
+            },
+            "bin": {
+                "loose-envify": "cli.js"
+            }
+        },
+        "node_modules/lru-cache": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
+            "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+            "dev": true,
+            "license": "ISC",
+            "dependencies": {
+                "yallist": "^3.0.2"
+            }
+        },
+        "node_modules/marked": {
+            "version": "14.0.0",
+            "resolved": "https://registry.npmmirror.com/marked/-/marked-14.0.0.tgz",
+            "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
+            "license": "MIT",
+            "peer": true,
+            "bin": {
+                "marked": "bin/marked.js"
+            },
+            "engines": {
+                "node": ">= 18"
+            }
+        },
+        "node_modules/math-intrinsics": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+            "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/mime-db": {
+            "version": "1.52.0",
+            "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+            "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/mime-types": {
+            "version": "2.1.35",
+            "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+            "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+            "license": "MIT",
+            "dependencies": {
+                "mime-db": "1.52.0"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/minimatch": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+            "dev": true,
+            "license": "ISC",
+            "dependencies": {
+                "brace-expansion": "^1.1.7"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/monaco-editor": {
+            "version": "0.55.1",
+            "resolved": "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.55.1.tgz",
+            "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
+            "license": "MIT",
+            "peer": true,
+            "dependencies": {
+                "dompurify": "3.2.7",
+                "marked": "14.0.0"
+            }
+        },
+        "node_modules/ms": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
+            "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/nanoid": {
+            "version": "3.3.11",
+            "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
+            "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/ai"
+                }
+            ],
+            "license": "MIT",
+            "bin": {
+                "nanoid": "bin/nanoid.cjs"
+            },
+            "engines": {
+                "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+            }
+        },
+        "node_modules/natural-compare": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz",
+            "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/node-releases": {
+            "version": "2.0.27",
+            "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz",
+            "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/object-assign": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
+            "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/object-inspect": {
+            "version": "1.13.4",
+            "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
+            "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/object-keys": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz",
+            "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/object.assign": {
+            "version": "4.1.7",
+            "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz",
+            "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.3",
+                "define-properties": "^1.2.1",
+                "es-object-atoms": "^1.0.0",
+                "has-symbols": "^1.1.0",
+                "object-keys": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/object.entries": {
+            "version": "1.1.9",
+            "resolved": "https://registry.npmmirror.com/object.entries/-/object.entries-1.1.9.tgz",
+            "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.4",
+                "define-properties": "^1.2.1",
+                "es-object-atoms": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/object.fromentries": {
+            "version": "2.0.8",
+            "resolved": "https://registry.npmmirror.com/object.fromentries/-/object.fromentries-2.0.8.tgz",
+            "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.7",
+                "define-properties": "^1.2.1",
+                "es-abstract": "^1.23.2",
+                "es-object-atoms": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/object.values": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmmirror.com/object.values/-/object.values-1.2.1.tgz",
+            "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.3",
+                "define-properties": "^1.2.1",
+                "es-object-atoms": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/once": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+            "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+            "dev": true,
+            "license": "ISC",
+            "dependencies": {
+                "wrappy": "1"
+            }
+        },
+        "node_modules/optionator": {
+            "version": "0.9.4",
+            "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz",
+            "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "deep-is": "^0.1.3",
+                "fast-levenshtein": "^2.0.6",
+                "levn": "^0.4.1",
+                "prelude-ls": "^1.2.1",
+                "type-check": "^0.4.0",
+                "word-wrap": "^1.2.5"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/own-keys": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz",
+            "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "get-intrinsic": "^1.2.6",
+                "object-keys": "^1.1.1",
+                "safe-push-apply": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/p-limit": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz",
+            "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "yocto-queue": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/p-locate": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz",
+            "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "p-limit": "^3.0.2"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/parent-module": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
+            "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "callsites": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/path-exists": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
+            "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/path-is-absolute": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+            "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/path-key": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+            "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/path-parse": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
+            "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+            "dev": true,
+            "license": "MIT"
+        },
+        "node_modules/picocolors": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+            "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+            "dev": true,
+            "license": "ISC"
+        },
+        "node_modules/possible-typed-array-names": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+            "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/postcss": {
+            "version": "8.5.6",
+            "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
+            "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/postcss/"
+                },
+                {
+                    "type": "tidelift",
+                    "url": "https://tidelift.com/funding/github/npm/postcss"
+                },
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/ai"
+                }
+            ],
+            "license": "MIT",
+            "dependencies": {
+                "nanoid": "^3.3.11",
+                "picocolors": "^1.1.1",
+                "source-map-js": "^1.2.1"
+            },
+            "engines": {
+                "node": "^10 || ^12 || >=14"
+            }
+        },
+        "node_modules/prelude-ls": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
+            "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/prop-types": {
+            "version": "15.8.1",
+            "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz",
+            "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "loose-envify": "^1.4.0",
+                "object-assign": "^4.1.1",
+                "react-is": "^16.13.1"
+            }
+        },
+        "node_modules/proxy-from-env": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+            "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+            "license": "MIT"
+        },
+        "node_modules/punycode": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+            "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/queue-microtask": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
+            "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ],
+            "license": "MIT"
+        },
+        "node_modules/rc-cascader": {
+            "version": "3.34.0",
+            "resolved": "https://registry.npmmirror.com/rc-cascader/-/rc-cascader-3.34.0.tgz",
+            "integrity": "sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.25.7",
+                "classnames": "^2.3.1",
+                "rc-select": "~14.16.2",
+                "rc-tree": "~5.13.0",
+                "rc-util": "^5.43.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-checkbox": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmmirror.com/rc-checkbox/-/rc-checkbox-3.5.0.tgz",
+            "integrity": "sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "classnames": "^2.3.2",
+                "rc-util": "^5.25.2"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-collapse": {
+            "version": "3.9.0",
+            "resolved": "https://registry.npmmirror.com/rc-collapse/-/rc-collapse-3.9.0.tgz",
+            "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "classnames": "2.x",
+                "rc-motion": "^2.3.4",
+                "rc-util": "^5.27.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-dialog": {
+            "version": "9.6.0",
+            "resolved": "https://registry.npmmirror.com/rc-dialog/-/rc-dialog-9.6.0.tgz",
+            "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "@rc-component/portal": "^1.0.0-8",
+                "classnames": "^2.2.6",
+                "rc-motion": "^2.3.0",
+                "rc-util": "^5.21.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-drawer": {
+            "version": "7.3.0",
+            "resolved": "https://registry.npmmirror.com/rc-drawer/-/rc-drawer-7.3.0.tgz",
+            "integrity": "sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.23.9",
+                "@rc-component/portal": "^1.1.1",
+                "classnames": "^2.2.6",
+                "rc-motion": "^2.6.1",
+                "rc-util": "^5.38.1"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-dropdown": {
+            "version": "4.2.1",
+            "resolved": "https://registry.npmmirror.com/rc-dropdown/-/rc-dropdown-4.2.1.tgz",
+            "integrity": "sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.18.3",
+                "@rc-component/trigger": "^2.0.0",
+                "classnames": "^2.2.6",
+                "rc-util": "^5.44.1"
+            },
+            "peerDependencies": {
+                "react": ">=16.11.0",
+                "react-dom": ">=16.11.0"
+            }
+        },
+        "node_modules/rc-field-form": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmmirror.com/rc-field-form/-/rc-field-form-2.7.1.tgz",
+            "integrity": "sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.18.0",
+                "@rc-component/async-validator": "^5.0.3",
+                "rc-util": "^5.32.2"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-image": {
+            "version": "7.12.0",
+            "resolved": "https://registry.npmmirror.com/rc-image/-/rc-image-7.12.0.tgz",
+            "integrity": "sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.11.2",
+                "@rc-component/portal": "^1.0.2",
+                "classnames": "^2.2.6",
+                "rc-dialog": "~9.6.0",
+                "rc-motion": "^2.6.2",
+                "rc-util": "^5.34.1"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-input": {
+            "version": "1.8.0",
+            "resolved": "https://registry.npmmirror.com/rc-input/-/rc-input-1.8.0.tgz",
+            "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.11.1",
+                "classnames": "^2.2.1",
+                "rc-util": "^5.18.1"
+            },
+            "peerDependencies": {
+                "react": ">=16.0.0",
+                "react-dom": ">=16.0.0"
+            }
+        },
+        "node_modules/rc-input-number": {
+            "version": "9.5.0",
+            "resolved": "https://registry.npmmirror.com/rc-input-number/-/rc-input-number-9.5.0.tgz",
+            "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "@rc-component/mini-decimal": "^1.0.1",
+                "classnames": "^2.2.5",
+                "rc-input": "~1.8.0",
+                "rc-util": "^5.40.1"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-mentions": {
+            "version": "2.20.0",
+            "resolved": "https://registry.npmmirror.com/rc-mentions/-/rc-mentions-2.20.0.tgz",
+            "integrity": "sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.22.5",
+                "@rc-component/trigger": "^2.0.0",
+                "classnames": "^2.2.6",
+                "rc-input": "~1.8.0",
+                "rc-menu": "~9.16.0",
+                "rc-textarea": "~1.10.0",
+                "rc-util": "^5.34.1"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-menu": {
+            "version": "9.16.1",
+            "resolved": "https://registry.npmmirror.com/rc-menu/-/rc-menu-9.16.1.tgz",
+            "integrity": "sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "@rc-component/trigger": "^2.0.0",
+                "classnames": "2.x",
+                "rc-motion": "^2.4.3",
+                "rc-overflow": "^1.3.1",
+                "rc-util": "^5.27.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-motion": {
+            "version": "2.9.5",
+            "resolved": "https://registry.npmmirror.com/rc-motion/-/rc-motion-2.9.5.tgz",
+            "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.11.1",
+                "classnames": "^2.2.1",
+                "rc-util": "^5.44.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-notification": {
+            "version": "5.6.4",
+            "resolved": "https://registry.npmmirror.com/rc-notification/-/rc-notification-5.6.4.tgz",
+            "integrity": "sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "classnames": "2.x",
+                "rc-motion": "^2.9.0",
+                "rc-util": "^5.20.1"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-overflow": {
+            "version": "1.5.0",
+            "resolved": "https://registry.npmmirror.com/rc-overflow/-/rc-overflow-1.5.0.tgz",
+            "integrity": "sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.11.1",
+                "classnames": "^2.2.1",
+                "rc-resize-observer": "^1.0.0",
+                "rc-util": "^5.37.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-pagination": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmmirror.com/rc-pagination/-/rc-pagination-5.1.0.tgz",
+            "integrity": "sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "classnames": "^2.3.2",
+                "rc-util": "^5.38.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-picker": {
+            "version": "4.11.3",
+            "resolved": "https://registry.npmmirror.com/rc-picker/-/rc-picker-4.11.3.tgz",
+            "integrity": "sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.24.7",
+                "@rc-component/trigger": "^2.0.0",
+                "classnames": "^2.2.1",
+                "rc-overflow": "^1.3.2",
+                "rc-resize-observer": "^1.4.0",
+                "rc-util": "^5.43.0"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "date-fns": ">= 2.x",
+                "dayjs": ">= 1.x",
+                "luxon": ">= 3.x",
+                "moment": ">= 2.x",
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            },
+            "peerDependenciesMeta": {
+                "date-fns": {
+                    "optional": true
+                },
+                "dayjs": {
+                    "optional": true
+                },
+                "luxon": {
+                    "optional": true
+                },
+                "moment": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/rc-progress": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmmirror.com/rc-progress/-/rc-progress-4.0.0.tgz",
+            "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "classnames": "^2.2.6",
+                "rc-util": "^5.16.1"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-rate": {
+            "version": "2.13.1",
+            "resolved": "https://registry.npmmirror.com/rc-rate/-/rc-rate-2.13.1.tgz",
+            "integrity": "sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "classnames": "^2.2.5",
+                "rc-util": "^5.0.1"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-resize-observer": {
+            "version": "1.4.3",
+            "resolved": "https://registry.npmmirror.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz",
+            "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.20.7",
+                "classnames": "^2.2.1",
+                "rc-util": "^5.44.1",
+                "resize-observer-polyfill": "^1.5.1"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-segmented": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmmirror.com/rc-segmented/-/rc-segmented-2.7.1.tgz",
+            "integrity": "sha512-izj1Nw/Dw2Vb7EVr+D/E9lUTkBe+kKC+SAFSU9zqr7WV2W5Ktaa9Gc7cB2jTqgk8GROJayltaec+DBlYKc6d+g==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.11.1",
+                "classnames": "^2.2.1",
+                "rc-motion": "^2.4.4",
+                "rc-util": "^5.17.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.0.0",
+                "react-dom": ">=16.0.0"
+            }
+        },
+        "node_modules/rc-select": {
+            "version": "14.16.8",
+            "resolved": "https://registry.npmmirror.com/rc-select/-/rc-select-14.16.8.tgz",
+            "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "@rc-component/trigger": "^2.1.1",
+                "classnames": "2.x",
+                "rc-motion": "^2.0.1",
+                "rc-overflow": "^1.3.1",
+                "rc-util": "^5.16.1",
+                "rc-virtual-list": "^3.5.2"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": "*",
+                "react-dom": "*"
+            }
+        },
+        "node_modules/rc-slider": {
+            "version": "11.1.9",
+            "resolved": "https://registry.npmmirror.com/rc-slider/-/rc-slider-11.1.9.tgz",
+            "integrity": "sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "classnames": "^2.2.5",
+                "rc-util": "^5.36.0"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-steps": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmmirror.com/rc-steps/-/rc-steps-6.0.1.tgz",
+            "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.16.7",
+                "classnames": "^2.2.3",
+                "rc-util": "^5.16.1"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-switch": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmmirror.com/rc-switch/-/rc-switch-4.1.0.tgz",
+            "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.21.0",
+                "classnames": "^2.2.1",
+                "rc-util": "^5.30.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-table": {
+            "version": "7.54.0",
+            "resolved": "https://registry.npmmirror.com/rc-table/-/rc-table-7.54.0.tgz",
+            "integrity": "sha512-/wDTkki6wBTjwylwAGjpLKYklKo9YgjZwAU77+7ME5mBoS32Q4nAwoqhA2lSge6fobLW3Tap6uc5xfwaL2p0Sw==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "@rc-component/context": "^1.4.0",
+                "classnames": "^2.2.5",
+                "rc-resize-observer": "^1.1.0",
+                "rc-util": "^5.44.3",
+                "rc-virtual-list": "^3.14.2"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-tabs": {
+            "version": "15.7.0",
+            "resolved": "https://registry.npmmirror.com/rc-tabs/-/rc-tabs-15.7.0.tgz",
+            "integrity": "sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.11.2",
+                "classnames": "2.x",
+                "rc-dropdown": "~4.2.0",
+                "rc-menu": "~9.16.0",
+                "rc-motion": "^2.6.2",
+                "rc-resize-observer": "^1.0.0",
+                "rc-util": "^5.34.1"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-textarea": {
+            "version": "1.10.2",
+            "resolved": "https://registry.npmmirror.com/rc-textarea/-/rc-textarea-1.10.2.tgz",
+            "integrity": "sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "classnames": "^2.2.1",
+                "rc-input": "~1.8.0",
+                "rc-resize-observer": "^1.0.0",
+                "rc-util": "^5.27.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-tooltip": {
+            "version": "6.4.0",
+            "resolved": "https://registry.npmmirror.com/rc-tooltip/-/rc-tooltip-6.4.0.tgz",
+            "integrity": "sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.11.2",
+                "@rc-component/trigger": "^2.0.0",
+                "classnames": "^2.3.1",
+                "rc-util": "^5.44.3"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-tree": {
+            "version": "5.13.1",
+            "resolved": "https://registry.npmmirror.com/rc-tree/-/rc-tree-5.13.1.tgz",
+            "integrity": "sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.10.1",
+                "classnames": "2.x",
+                "rc-motion": "^2.0.1",
+                "rc-util": "^5.16.1",
+                "rc-virtual-list": "^3.5.1"
+            },
+            "engines": {
+                "node": ">=10.x"
+            },
+            "peerDependencies": {
+                "react": "*",
+                "react-dom": "*"
+            }
+        },
+        "node_modules/rc-tree-select": {
+            "version": "5.27.0",
+            "resolved": "https://registry.npmmirror.com/rc-tree-select/-/rc-tree-select-5.27.0.tgz",
+            "integrity": "sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.25.7",
+                "classnames": "2.x",
+                "rc-select": "~14.16.2",
+                "rc-tree": "~5.13.0",
+                "rc-util": "^5.43.0"
+            },
+            "peerDependencies": {
+                "react": "*",
+                "react-dom": "*"
+            }
+        },
+        "node_modules/rc-upload": {
+            "version": "4.11.0",
+            "resolved": "https://registry.npmmirror.com/rc-upload/-/rc-upload-4.11.0.tgz",
+            "integrity": "sha512-ZUyT//2JAehfHzjWowqROcwYJKnZkIUGWaTE/VogVrepSl7AFNbQf4+zGfX4zl9Vrj/Jm8scLO0R6UlPDKK4wA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.18.3",
+                "classnames": "^2.2.5",
+                "rc-util": "^5.2.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-util": {
+            "version": "5.44.4",
+            "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.44.4.tgz",
+            "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.18.3",
+                "react-is": "^18.2.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/rc-util/node_modules/react-is": {
+            "version": "18.3.1",
+            "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz",
+            "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+            "license": "MIT"
+        },
+        "node_modules/rc-virtual-list": {
+            "version": "3.19.2",
+            "resolved": "https://registry.npmmirror.com/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz",
+            "integrity": "sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA==",
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.20.0",
+                "classnames": "^2.2.6",
+                "rc-resize-observer": "^1.0.0",
+                "rc-util": "^5.36.0"
+            },
+            "engines": {
+                "node": ">=8.x"
+            },
+            "peerDependencies": {
+                "react": ">=16.9.0",
+                "react-dom": ">=16.9.0"
+            }
+        },
+        "node_modules/react": {
+            "version": "18.3.1",
+            "resolved": "https://registry.npmmirror.com/react/-/react-18.3.1.tgz",
+            "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+            "license": "MIT",
+            "dependencies": {
+                "loose-envify": "^1.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/react-dom": {
+            "version": "18.3.1",
+            "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz",
+            "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+            "license": "MIT",
+            "dependencies": {
+                "loose-envify": "^1.1.0",
+                "scheduler": "^0.23.2"
+            },
+            "peerDependencies": {
+                "react": "^18.3.1"
+            }
+        },
+        "node_modules/react-is": {
+            "version": "16.13.1",
+            "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-refresh": {
+            "version": "0.17.0",
+            "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz",
+            "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/reflect.getprototypeof": {
+            "version": "1.0.10",
+            "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",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "define-properties": "^1.2.1",
+                "es-abstract": "^1.23.9",
+                "es-errors": "^1.3.0",
+                "es-object-atoms": "^1.0.0",
+                "get-intrinsic": "^1.2.7",
+                "get-proto": "^1.0.1",
+                "which-builtin-type": "^1.2.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/regexp.prototype.flags": {
+            "version": "1.5.4",
+            "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",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "define-properties": "^1.2.1",
+                "es-errors": "^1.3.0",
+                "get-proto": "^1.0.1",
+                "gopd": "^1.2.0",
+                "set-function-name": "^2.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/resize-observer-polyfill": {
+            "version": "1.5.1",
+            "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+            "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
+            "license": "MIT"
+        },
+        "node_modules/resolve": {
+            "version": "2.0.0-next.5",
+            "resolved": "https://registry.npmmirror.com/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/resolve-from": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
+            "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/reusify": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz",
+            "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "iojs": ">=1.0.0",
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/rimraf": {
+            "version": "3.0.2",
+            "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,
+            "license": "ISC",
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/rollup": {
+            "version": "4.55.1",
+            "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.55.1.tgz",
+            "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@types/estree": "1.0.8"
+            },
+            "bin": {
+                "rollup": "dist/bin/rollup"
+            },
+            "engines": {
+                "node": ">=18.0.0",
+                "npm": ">=8.0.0"
+            },
+            "optionalDependencies": {
+                "@rollup/rollup-android-arm-eabi": "4.55.1",
+                "@rollup/rollup-android-arm64": "4.55.1",
+                "@rollup/rollup-darwin-arm64": "4.55.1",
+                "@rollup/rollup-darwin-x64": "4.55.1",
+                "@rollup/rollup-freebsd-arm64": "4.55.1",
+                "@rollup/rollup-freebsd-x64": "4.55.1",
+                "@rollup/rollup-linux-arm-gnueabihf": "4.55.1",
+                "@rollup/rollup-linux-arm-musleabihf": "4.55.1",
+                "@rollup/rollup-linux-arm64-gnu": "4.55.1",
+                "@rollup/rollup-linux-arm64-musl": "4.55.1",
+                "@rollup/rollup-linux-loong64-gnu": "4.55.1",
+                "@rollup/rollup-linux-loong64-musl": "4.55.1",
+                "@rollup/rollup-linux-ppc64-gnu": "4.55.1",
+                "@rollup/rollup-linux-ppc64-musl": "4.55.1",
+                "@rollup/rollup-linux-riscv64-gnu": "4.55.1",
+                "@rollup/rollup-linux-riscv64-musl": "4.55.1",
+                "@rollup/rollup-linux-s390x-gnu": "4.55.1",
+                "@rollup/rollup-linux-x64-gnu": "4.55.1",
+                "@rollup/rollup-linux-x64-musl": "4.55.1",
+                "@rollup/rollup-openbsd-x64": "4.55.1",
+                "@rollup/rollup-openharmony-arm64": "4.55.1",
+                "@rollup/rollup-win32-arm64-msvc": "4.55.1",
+                "@rollup/rollup-win32-ia32-msvc": "4.55.1",
+                "@rollup/rollup-win32-x64-gnu": "4.55.1",
+                "@rollup/rollup-win32-x64-msvc": "4.55.1",
+                "fsevents": "~2.3.2"
+            }
+        },
+        "node_modules/run-parallel": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
+            "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ],
+            "license": "MIT",
+            "dependencies": {
+                "queue-microtask": "^1.2.2"
+            }
+        },
+        "node_modules/safe-array-concat": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+            "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.2",
+                "get-intrinsic": "^1.2.6",
+                "has-symbols": "^1.1.0",
+                "isarray": "^2.0.5"
+            },
+            "engines": {
+                "node": ">=0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/safe-push-apply": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+            "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "es-errors": "^1.3.0",
+                "isarray": "^2.0.5"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/safe-regex-test": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+            "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.2",
+                "es-errors": "^1.3.0",
+                "is-regex": "^1.2.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/scheduler": {
+            "version": "0.23.2",
+            "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz",
+            "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+            "license": "MIT",
+            "dependencies": {
+                "loose-envify": "^1.1.0"
+            }
+        },
+        "node_modules/scroll-into-view-if-needed": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz",
+            "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==",
+            "license": "MIT",
+            "dependencies": {
+                "compute-scroll-into-view": "^3.0.2"
+            }
+        },
+        "node_modules/semver": {
+            "version": "6.3.1",
+            "resolved": "https://registry.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/set-function-length": {
+            "version": "1.2.2",
+            "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",
+            "dependencies": {
+                "define-data-property": "^1.1.4",
+                "es-errors": "^1.3.0",
+                "function-bind": "^1.1.2",
+                "get-intrinsic": "^1.2.4",
+                "gopd": "^1.0.1",
+                "has-property-descriptors": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/set-function-name": {
+            "version": "2.0.2",
+            "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",
+            "dependencies": {
+                "define-data-property": "^1.1.4",
+                "es-errors": "^1.3.0",
+                "functions-have-names": "^1.2.3",
+                "has-property-descriptors": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/set-proto": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmmirror.com/set-proto/-/set-proto-1.0.0.tgz",
+            "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "dunder-proto": "^1.0.1",
+                "es-errors": "^1.3.0",
+                "es-object-atoms": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/shebang-command": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+            "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "shebang-regex": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/shebang-regex": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+            "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/side-channel": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
+            "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "es-errors": "^1.3.0",
+                "object-inspect": "^1.13.3",
+                "side-channel-list": "^1.0.0",
+                "side-channel-map": "^1.0.1",
+                "side-channel-weakmap": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/side-channel-list": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz",
+            "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "es-errors": "^1.3.0",
+                "object-inspect": "^1.13.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/side-channel-map": {
+            "version": "1.0.1",
+            "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",
+            "dependencies": {
+                "call-bound": "^1.0.2",
+                "es-errors": "^1.3.0",
+                "get-intrinsic": "^1.2.5",
+                "object-inspect": "^1.13.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/side-channel-weakmap": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+            "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.2",
+                "es-errors": "^1.3.0",
+                "get-intrinsic": "^1.2.5",
+                "object-inspect": "^1.13.3",
+                "side-channel-map": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/source-map-js": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+            "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+            "dev": true,
+            "license": "BSD-3-Clause",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/state-local": {
+            "version": "1.0.7",
+            "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.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+            "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "es-errors": "^1.3.0",
+                "internal-slot": "^1.1.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/string-convert": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmmirror.com/string-convert/-/string-convert-0.2.1.tgz",
+            "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==",
+            "license": "MIT"
+        },
+        "node_modules/string.prototype.matchall": {
+            "version": "4.0.12",
+            "resolved": "https://registry.npmmirror.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+            "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.3",
+                "define-properties": "^1.2.1",
+                "es-abstract": "^1.23.6",
+                "es-errors": "^1.3.0",
+                "es-object-atoms": "^1.0.0",
+                "get-intrinsic": "^1.2.6",
+                "gopd": "^1.2.0",
+                "has-symbols": "^1.1.0",
+                "internal-slot": "^1.1.0",
+                "regexp.prototype.flags": "^1.5.3",
+                "set-function-name": "^2.0.2",
+                "side-channel": "^1.1.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/string.prototype.repeat": {
+            "version": "1.0.0",
+            "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",
+            "dependencies": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5"
+            }
+        },
+        "node_modules/string.prototype.trim": {
+            "version": "1.2.10",
+            "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+            "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.2",
+                "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"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/string.prototype.trimend": {
+            "version": "1.0.9",
+            "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+            "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.2",
+                "define-properties": "^1.2.1",
+                "es-object-atoms": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/string.prototype.trimstart": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+            "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bind": "^1.0.7",
+                "define-properties": "^1.2.1",
+                "es-object-atoms": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/strip-ansi": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-regex": "^5.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/strip-json-comments": {
+            "version": "3.1.1",
+            "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",
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/stylis": {
+            "version": "4.3.6",
+            "resolved": "https://registry.npmmirror.com/stylis/-/stylis-4.3.6.tgz",
+            "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
+            "license": "MIT"
+        },
+        "node_modules/supports-color": {
+            "version": "7.2.0",
+            "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
+            "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "has-flag": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/supports-preserve-symlinks-flag": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+            "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/text-table": {
+            "version": "0.2.0",
+            "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/throttle-debounce": {
+            "version": "5.0.2",
+            "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
+            "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==",
+            "license": "MIT",
+            "engines": {
+                "node": ">=12.22"
+            }
+        },
+        "node_modules/toggle-selection": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmmirror.com/toggle-selection/-/toggle-selection-1.0.6.tgz",
+            "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==",
+            "license": "MIT"
+        },
+        "node_modules/type-check": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
+            "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "prelude-ls": "^1.2.1"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/type-fest": {
+            "version": "0.20.2",
+            "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)",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/typed-array-buffer": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+            "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.3",
+                "es-errors": "^1.3.0",
+                "is-typed-array": "^1.1.14"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/typed-array-byte-length": {
+            "version": "1.0.3",
+            "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",
+            "dependencies": {
+                "call-bind": "^1.0.8",
+                "for-each": "^0.3.3",
+                "gopd": "^1.2.0",
+                "has-proto": "^1.2.0",
+                "is-typed-array": "^1.1.14"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/typed-array-byte-offset": {
+            "version": "1.0.4",
+            "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",
+            "dependencies": {
+                "available-typed-arrays": "^1.0.7",
+                "call-bind": "^1.0.8",
+                "for-each": "^0.3.3",
+                "gopd": "^1.2.0",
+                "has-proto": "^1.2.0",
+                "is-typed-array": "^1.1.15",
+                "reflect.getprototypeof": "^1.0.9"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/typed-array-length": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.7.tgz",
+            "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+            "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"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/unbox-primitive": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+            "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "call-bound": "^1.0.3",
+                "has-bigints": "^1.0.2",
+                "has-symbols": "^1.1.0",
+                "which-boxed-primitive": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/update-browserslist-db": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+            "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/browserslist"
+                },
+                {
+                    "type": "tidelift",
+                    "url": "https://tidelift.com/funding/github/npm/browserslist"
+                },
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/ai"
+                }
+            ],
+            "license": "MIT",
+            "dependencies": {
+                "escalade": "^3.2.0",
+                "picocolors": "^1.1.1"
+            },
+            "bin": {
+                "update-browserslist-db": "cli.js"
+            },
+            "peerDependencies": {
+                "browserslist": ">= 4.21.0"
+            }
+        },
+        "node_modules/uri-js": {
+            "version": "4.4.1",
+            "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+            "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+            "dev": true,
+            "license": "BSD-2-Clause",
+            "dependencies": {
+                "punycode": "^2.1.0"
+            }
+        },
+        "node_modules/vite": {
+            "version": "5.4.21",
+            "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz",
+            "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "esbuild": "^0.21.3",
+                "postcss": "^8.4.43",
+                "rollup": "^4.20.0"
+            },
+            "bin": {
+                "vite": "bin/vite.js"
+            },
+            "engines": {
+                "node": "^18.0.0 || >=20.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/vitejs/vite?sponsor=1"
+            },
+            "optionalDependencies": {
+                "fsevents": "~2.3.3"
+            },
+            "peerDependencies": {
+                "@types/node": "^18.0.0 || >=20.0.0",
+                "less": "*",
+                "lightningcss": "^1.21.0",
+                "sass": "*",
+                "sass-embedded": "*",
+                "stylus": "*",
+                "sugarss": "*",
+                "terser": "^5.4.0"
+            },
+            "peerDependenciesMeta": {
+                "@types/node": {
+                    "optional": true
+                },
+                "less": {
+                    "optional": true
+                },
+                "lightningcss": {
+                    "optional": true
+                },
+                "sass": {
+                    "optional": true
+                },
+                "sass-embedded": {
+                    "optional": true
+                },
+                "stylus": {
+                    "optional": true
+                },
+                "sugarss": {
+                    "optional": true
+                },
+                "terser": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/which": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+            "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+            "dev": true,
+            "license": "ISC",
+            "dependencies": {
+                "isexe": "^2.0.0"
+            },
+            "bin": {
+                "node-which": "bin/node-which"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/which-boxed-primitive": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+            "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "is-bigint": "^1.1.0",
+                "is-boolean-object": "^1.2.1",
+                "is-number-object": "^1.1.1",
+                "is-string": "^1.1.1",
+                "is-symbol": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-builtin-type": {
+            "version": "1.2.1",
+            "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",
+            "dependencies": {
+                "call-bound": "^1.0.2",
+                "function.prototype.name": "^1.1.6",
+                "has-tostringtag": "^1.0.2",
+                "is-async-function": "^2.0.0",
+                "is-date-object": "^1.1.0",
+                "is-finalizationregistry": "^1.1.0",
+                "is-generator-function": "^1.0.10",
+                "is-regex": "^1.2.1",
+                "is-weakref": "^1.0.2",
+                "isarray": "^2.0.5",
+                "which-boxed-primitive": "^1.1.0",
+                "which-collection": "^1.0.2",
+                "which-typed-array": "^1.1.16"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-collection": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz",
+            "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "is-map": "^2.0.3",
+                "is-set": "^2.0.3",
+                "is-weakmap": "^2.0.2",
+                "is-weakset": "^2.0.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-typed-array": {
+            "version": "1.1.19",
+            "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.19.tgz",
+            "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "available-typed-arrays": "^1.0.7",
+                "call-bind": "^1.0.8",
+                "call-bound": "^1.0.4",
+                "for-each": "^0.3.5",
+                "get-proto": "^1.0.1",
+                "gopd": "^1.2.0",
+                "has-tostringtag": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/word-wrap": {
+            "version": "1.2.5",
+            "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz",
+            "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/wrappy": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+            "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+            "dev": true,
+            "license": "ISC"
+        },
+        "node_modules/yallist": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
+            "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+            "dev": true,
+            "license": "ISC"
+        },
+        "node_modules/yocto-queue": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
+            "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        }
+    }
+}

+ 31 - 0
front/package.json

@@ -0,0 +1,31 @@
+{
+    "name": "visa-plugin-manager",
+    "private": true,
+    "version": "0.1.0",
+    "type": "module",
+    "scripts": {
+        "dev": "vite",
+        "build": "vite build",
+        "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
+        "preview": "vite preview"
+    },
+    "dependencies": {
+        "@ant-design/icons": "^5.6.1",
+        "@monaco-editor/react": "^4.7.0",
+        "@tanstack/react-query": "^5.90.16",
+        "antd": "^5.29.3",
+        "axios": "^1.13.2",
+        "react": "^18.2.0",
+        "react-dom": "^18.2.0"
+    },
+    "devDependencies": {
+        "@types/react": "^18.2.43",
+        "@types/react-dom": "^18.2.17",
+        "@vitejs/plugin-react": "^4.2.1",
+        "eslint": "^8.55.0",
+        "eslint-plugin-react": "^7.33.2",
+        "eslint-plugin-react-hooks": "^4.6.0",
+        "eslint-plugin-react-refresh": "^0.4.5",
+        "vite": "^5.0.8"
+    }
+}

+ 51 - 0
front/src/App.css

@@ -0,0 +1,51 @@
+#root {
+    margin: 0;
+    padding: 0;
+    width: 100%;
+    height: 100%;
+  }
+  
+  body {
+    margin: 0;
+    padding: 0;
+    background-color: #f5f5f5;
+    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
+  }
+  
+  /* 覆盖 Ant Design 的一些默认间距,如果需要的话 */
+  .ant-layout-header {
+    padding-inline: 24px !important;
+  }
+  
+  /* 滚动条样式优化 (可选) */
+  ::-webkit-scrollbar {
+    width: 8px;
+    height: 8px;
+  }
+  ::-webkit-scrollbar-thumb {
+    background: #c1c1c1;
+    border-radius: 4px;
+  }
+  ::-webkit-scrollbar-track {
+    background: #f1f1f1;
+  }
+
+  /* 让表格悬停时效果更好 */
+.ant-table-tbody > tr.ant-table-row:hover > td {
+    background: #fafafa !important;
+  }
+  
+  /* 表格内垂直居中 */
+  .align-middle td {
+    vertical-align: middle !important;
+  }
+  
+  /* 调整 Modal 的圆角,看起来更圆润 */
+  .ant-modal-content {
+    border-radius: 12px !important;
+    overflow: hidden;
+  }
+  
+  .ant-btn {
+    border-radius: 6px;
+  }

+ 25 - 0
front/src/App.jsx

@@ -0,0 +1,25 @@
+// 文件路径: src/App.jsx
+import React from 'react';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import Dashboard from './pages/Dashboard';
+import './App.css'; 
+
+// 创建 React Query 客户端
+const queryClient = new QueryClient({
+  defaultOptions: {
+    queries: {
+      retry: 1,
+      refetchOnWindowFocus: false,
+    },
+  },
+});
+
+function App() {
+  return (
+    <QueryClientProvider client={queryClient}>
+      <Dashboard />
+    </QueryClientProvider>
+  );
+}
+
+export default App;

+ 30 - 0
front/src/api/index.js

@@ -0,0 +1,30 @@
+import axios from 'axios';
+
+// 基础配置,配合 vite.config.js 的代理
+const api = axios.create({
+  baseURL: '/api',
+  timeout: 10000,
+});
+
+api.interceptors.response.use(
+  (response) => response.data, // 把 axios 的外壳去掉,直接返回 { code: 0, data: [...] }
+  (error) => Promise.reject(error)
+);
+
+export const getStatus = () => api.get('/status');
+
+export const startGroup = (group_id) => api.post('/start', { group_id });
+export const stopGroup = (group_id) => api.post('/stop', { group_id });
+export const restartGroup = (group_id) => api.post('/restart', { group_id });
+
+export const getGroupConfig = (group_id) => api.post('/group_config', { group_id });
+
+// 特殊处理:API 要求 new_config_str 是字符串
+export const updateConfig = (group_id, fullConfigObj) => {
+  return api.post('/ota/update_config', {
+    group_id,
+    new_config_str: JSON.stringify(fullConfigObj)
+  });
+};
+
+export const upgradePlugin = (data) => api.post('/ota/upgrade_plugin', data);

+ 119 - 0
front/src/components/ConfigModal.jsx

@@ -0,0 +1,119 @@
+import React, { useState, useEffect } from 'react';
+import { Modal, message, Spin, Button, Typography } from 'antd';
+import { useQuery, useMutation } from '@tanstack/react-query';
+import Editor from "@monaco-editor/react";
+import { getGroupConfig, updateConfig } from '../api';
+
+const { Text } = Typography;
+
+const ConfigModal = ({ open, groupId, onClose }) => {
+  const [jsonContent, setJsonContent] = useState("");
+
+  // 1. 获取当前配置
+  const { data: configResp, isLoading } = useQuery({
+    queryKey: ['config', groupId],
+    queryFn: () => getGroupConfig(groupId),
+    enabled: !!groupId && open,
+    staleTime: 0, // 每次打开都重新获取
+  });
+
+  // 2. 数据回显:收到数据后直接转为格式化的 JSON 字符串
+  useEffect(() => {
+    if (configResp?.data) {
+      // JSON.stringify(data, null, 2) 会生成带缩进的漂亮格式
+      setJsonContent(JSON.stringify(configResp.data, null, 2));
+    }
+  }, [configResp]);
+
+  // 3. 保存操作
+  const updateMutation = useMutation({
+    mutationFn: (data) => updateConfig(groupId, data),
+    onSuccess: () => {
+      message.success('Configuration updated successfully!');
+      onClose();
+    },
+    onError: (err) => {
+      message.error('Failed to update: ' + (err.message || 'Unknown error'));
+    }
+  });
+
+  const handleSave = () => {
+    try {
+      // A. 校验 JSON 格式
+      const configObj = JSON.parse(jsonContent);
+
+      // B. 安全检查:确保 group_id 没有被用户误删或修改
+      // 如果用户在 JSON 里改了 group_id,这里强制覆盖回正确的 ID,或者报错
+      if (configObj.group_id && configObj.group_id !== groupId) {
+        message.warning(`Group ID mismatch. Reverting to original ID: ${groupId}`);
+      }
+      configObj.group_id = groupId;
+
+      // C. 提交
+      updateMutation.mutate(configObj);
+
+    } catch (e) {
+      // JSON 解析失败
+      message.error('Invalid JSON format. Please check your syntax.');
+    }
+  };
+
+  return (
+    <Modal
+      title={
+        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', paddingRight: 30 }}>
+          <span>Config: <Text code>{groupId}</Text></span>
+          <Text type="secondary" style={{ fontSize: 12, fontWeight: 'normal' }}>
+            Raw JSON Mode
+          </Text>
+        </div>
+      }
+      open={open}
+      onCancel={onClose}
+      footer={[
+        <Button key="cancel" onClick={onClose}>
+          Cancel
+        </Button>,
+        <Button 
+          key="submit" 
+          type="primary" 
+          loading={updateMutation.isPending} 
+          onClick={handleSave}
+        >
+          Save Changes
+        </Button>
+      ]}
+      width={800}
+      centered
+      destroyOnClose
+      maskClosable={false}
+      bodyStyle={{ padding: 0 }} // 去掉内边距,让编辑器撑满
+    >
+      {isLoading ? (
+        <div style={{ textAlign: 'center', padding: '100px' }}>
+          <Spin size="large" tip="Loading configuration..." />
+        </div>
+      ) : (
+        <div style={{ height: '600px', borderTop: '1px solid #f0f0f0', borderBottom: '1px solid #f0f0f0' }}>
+          <Editor
+            height="100%"
+            defaultLanguage="json"
+            value={jsonContent}
+            onChange={(val) => setJsonContent(val)}
+            theme="vs-light"
+            options={{ 
+              minimap: { enabled: true },      // 显示右侧缩略图
+              scrollBeyondLastLine: false,     // 滚动限制
+              fontSize: 13,                    // 字体大小
+              formatOnPaste: true,             // 粘贴自动格式化
+              automaticLayout: true,           // 自动适应容器大小
+              tabSize: 2                       // 缩进空格数
+            }}
+          />
+        </div>
+      )}
+    </Modal>
+  );
+};
+
+export default ConfigModal;

+ 156 - 0
front/src/components/CreateGroupModal.jsx

@@ -0,0 +1,156 @@
+import React, { useState, useEffect } from 'react';
+import { Modal, Input, message, Button, Typography, Space } from 'antd';
+import { ThunderboltOutlined } from '@ant-design/icons';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import Editor from "@monaco-editor/react";
+import { updateConfig } from '../api';
+
+const { Text } = Typography;
+
+// 提供一个默认的模版,方便用户直接修改,而不是从零开始写 JSON
+const DEFAULT_TEMPLATE = {
+    "identifier": "",
+    "debug": false,
+    "enable": false,
+    "need_account": false,
+    "local_account_pool": "",
+    "need_proxy": false,
+    "proxy_pool": "",
+    "target_instances": 0,
+    "account_login_interval": 0,
+    "order_account_routing": "",
+    "order_account_online_limit": 0,
+    "account_bind_applicant": false,
+    "query_wait": {
+        "mode": "Random",
+        "fixed_wait": 10,
+        "random_min": 60,
+        "random_max": 300
+    },
+    "plugin_config": {
+        "lib_path": "plugins",
+        "plugin_name": "vfs_plugin",
+        "plugin_bin": "vfs_plugin.py",
+        "plugin_proto": "IVSPlg"
+    },
+    "free_config": {
+    }
+};
+
+const CreateGroupModal = ({ open, onClose }) => {
+  const queryClient = useQueryClient();
+  
+  // 两个核心状态:ID 和 JSON内容
+  const [groupId, setGroupId] = useState("");
+  const [jsonContent, setJsonContent] = useState("");
+
+  // 每次打开弹窗时,重置数据
+  useEffect(() => {
+    if (open) {
+      setGroupId("");
+      // 格式化默认模版
+      setJsonContent(JSON.stringify(DEFAULT_TEMPLATE, null, 2));
+    }
+  }, [open]);
+
+  // 提交 Mutation
+  const createMutation = useMutation({
+    mutationFn: (data) => updateConfig(data.group_id, data.config),
+    onSuccess: () => {
+      message.success(`Group [${groupId}] created successfully!`);
+      queryClient.invalidateQueries(['status']);
+      onClose();
+    },
+    onError: (err) => {
+      message.error('Failed to create: ' + (err.message || 'Unknown error'));
+    }
+  });
+
+  const handleCreate = () => {
+    // 1. 基础校验
+    if (!groupId || groupId.trim() === "") {
+      message.error("Group ID is required!");
+      return;
+    }
+
+    try {
+      // 2. 解析 JSON
+      const configObj = JSON.parse(jsonContent);
+
+      // 3. 强制合并 Group ID (确保 JSON 内部的 ID 与输入框一致)
+      configObj.group_id = groupId.trim();
+      // 如果 identifier 没填,默认用 ID
+      if (!configObj.identifier) {
+        configObj.identifier = groupId.trim();
+      }
+
+      // 4. 发送请求
+      createMutation.mutate({
+        group_id: groupId.trim(),
+        config: configObj
+      });
+
+    } catch (e) {
+      message.error("Invalid JSON format. Please check syntax.");
+    }
+  };
+
+  return (
+    <Modal
+      title="Create New Group"
+      open={open}
+      onCancel={onClose}
+      footer={[
+        <Button key="cancel" onClick={onClose}>
+          Cancel
+        </Button>,
+        <Button 
+          key="submit" 
+          type="primary" 
+          loading={createMutation.isPending} 
+          onClick={handleCreate}
+        >
+          Create Group
+        </Button>
+      ]}
+      width={800}
+      centered
+      maskClosable={false}
+    >
+      <Space direction="vertical" style={{ width: '100%', marginBottom: 16 }}>
+        <div>
+          <Text strong>Group ID (Unique Key)</Text>
+          <Input 
+            size="large"
+            placeholder="e.g. VFS_CN_DE" 
+            prefix={<ThunderboltOutlined style={{ color: '#bfbfbf' }} />}
+            value={groupId}
+            onChange={(e) => setGroupId(e.target.value.toUpperCase())} // 强制大写(可选)
+            style={{ marginTop: 8 }}
+          />
+        </div>
+        
+        <div>
+          <Text strong>Configuration (JSON)</Text>
+          <div style={{ marginTop: 8, height: '450px', border: '1px solid #d9d9d9', borderRadius: 6, overflow: 'hidden' }}>
+            <Editor
+              height="100%"
+              defaultLanguage="json"
+              value={jsonContent}
+              onChange={setJsonContent}
+              theme="vs-light"
+              options={{ 
+                minimap: { enabled: false },
+                fontSize: 13,
+                formatOnPaste: true,
+                tabSize: 2
+              }}
+            />
+          </div>
+        </div>
+      </Space>
+    </Modal>
+  );
+};
+
+export default CreateGroupModal;

+ 125 - 0
front/src/components/LogViewer.jsx

@@ -0,0 +1,125 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { Modal, Button, Empty, Tag } from 'antd';
+import { PauseCircleOutlined, PlayCircleOutlined, DeleteOutlined } from '@ant-design/icons';
+
+const LogViewer = ({ open, groupId, onClose }) => {
+  const [logs, setLogs] = useState([]);
+  const [isConnected, setIsConnected] = useState(false);
+  const [isPaused, setIsPaused] = useState(false);
+  
+  const wsRef = useRef(null);
+  const logEndRef = useRef(null);
+
+// 建立 WebSocket 连接
+useEffect(() => {
+    if (open && groupId) {
+      const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
+      const host = window.location.host; 
+      const wsUrl = `${protocol}//${host}/ws/logs/${groupId}`;
+
+      const ws = new WebSocket(wsUrl);
+      wsRef.current = ws;
+
+      ws.onopen = () => {
+        setIsConnected(true);
+        setLogs(prev => [...prev, `[SYSTEM] Connected to log stream for ${groupId}...`]);
+      };
+
+      ws.onmessage = (event) => {
+        if (isPaused) return; 
+        let newLog = event.data;
+        setLogs(prev => {
+            const updated = [...prev, newLog];
+            if (updated.length > 1000) {
+                return updated.slice(updated.length - 1000);
+            }
+            return updated;
+        });
+      };
+
+      ws.onclose = () => {
+        setIsConnected(false);
+        setLogs(prev => [...prev, `[SYSTEM] Connection closed.`]);
+      };
+
+      ws.onerror = (err) => {
+        console.error("WS Error", err);
+        setLogs(prev => [...prev, `[SYSTEM] WebSocket Error.`]);
+      };
+
+      // 修复双重打印的核心:
+      return () => {
+        ws.onmessage = null; // 断开回调引用
+        ws.close();          // 强制关闭,不管状态是 0 还是 1
+      };
+    }
+  }, [open, groupId]);
+
+  // 自动滚动到底部
+  useEffect(() => {
+    if (!isPaused && logEndRef.current) {
+      logEndRef.current.scrollIntoView({ behavior: "smooth" });
+    }
+  }, [logs, isPaused]);
+
+  // 清空日志
+  const handleClear = () => setLogs([]);
+
+  return (
+    <Modal
+      title={
+        <div className="flex items-center gap-2">
+          <span>Real-time Logs: {groupId}</span>
+          <Tag color={isConnected ? "green" : "red"}>
+            {isConnected ? "LIVE" : "DISCONNECTED"}
+          </Tag>
+        </div>
+      }
+      open={open}
+      onCancel={onClose}
+      footer={null}
+      width={900}
+      bodyStyle={{ padding: 0 }}
+      destroyOnClose
+    >
+      {/* 工具栏 */}
+      <div style={{ padding: '8px 16px', background: '#f0f0f0', display: 'flex', gap: '8px', borderBottom: '1px solid #d9d9d9' }}>
+        <Button 
+            size="small" 
+            icon={isPaused ? <PlayCircleOutlined /> : <PauseCircleOutlined />} 
+            onClick={() => setIsPaused(!isPaused)}
+        >
+            {isPaused ? "Resume Auto-scroll" : "Pause Output"}
+        </Button>
+        <Button size="small" icon={<DeleteOutlined />} onClick={handleClear}>Clear</Button>
+      </div>
+
+      {/* 终端界面 */}
+      <div style={{
+        backgroundColor: '#1e1e1e',
+        color: '#d4d4d4',
+        fontFamily: "'Menlo', 'Monaco', 'Courier New', monospace",
+        fontSize: '12px',
+        height: '500px',
+        overflowY: 'auto',
+        padding: '16px',
+        whiteSpace: 'pre-wrap', // 保持换行
+        wordBreak: 'break-all'
+      }}>
+        {logs.length === 0 && <div className="text-gray-500 text-center mt-20">Waiting for logs...</div>}
+        
+        {logs.map((log, index) => (
+          <div key={index} style={{ marginBottom: '2px', borderBottom: '1px solid #333' }}>
+             {/* 简单的时间戳+内容显示,你也可以引入 ansi-to-react 处理颜色 */}
+             <span style={{color: '#888', marginRight: '8px'}}>{new Date().toLocaleTimeString()}</span>
+             {log}
+          </div>
+        ))}
+        {/* 锚点用于自动滚动 */}
+        <div ref={logEndRef} />
+      </div>
+    </Modal>
+  );
+};
+
+export default LogViewer;

+ 58 - 0
front/src/components/UpgradeModal.jsx

@@ -0,0 +1,58 @@
+import React from 'react';
+import { Modal, Form, Input, message } from 'antd';
+import { useMutation } from '@tanstack/react-query';
+import { upgradePlugin } from '../api';
+
+const UpgradeModal = ({ open, onClose }) => {
+  const [form] = Form.useForm();
+
+  const mutation = useMutation({
+    mutationFn: upgradePlugin,
+    onSuccess: (data) => {
+      message.success(`Upgrade successful. Restarted: ${data.data.restarted.join(', ')}`);
+      form.resetFields();
+      onClose();
+    },
+    onError: (err) => {
+      message.error('Upgrade failed');
+    }
+  });
+
+  const handleOk = async () => {
+    try {
+      const values = await form.validateFields();
+      mutation.mutate(values);
+    } catch (e) {
+      // Validate failed
+    }
+  };
+
+  return (
+    <Modal
+      title="OTA Plugin Upgrade"
+      open={open}
+      onOk={handleOk}
+      onCancel={onClose}
+      confirmLoading={mutation.isPending}
+    >
+      <Form form={form} layout="vertical">
+        <Form.Item 
+            name="plugin_name" 
+            label="Plugin Name" 
+            rules={[{ required: true, message: 'Please input plugin name' }]}
+        >
+          <Input placeholder="e.g. bls_spain" />
+        </Form.Item>
+        <Form.Item 
+            name="plugin_bin" 
+            label="Plugin Binary Path/Name" 
+            rules={[{ required: true, message: 'Please input binary path' }]}
+        >
+          <Input placeholder="e.g. plugins/bls_v2.py" />
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+};
+
+export default UpgradeModal;

+ 11 - 0
front/src/main.jsx

@@ -0,0 +1,11 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './App.jsx'
+import './App.css'
+// Ant Design 5.x 默认自动引入样式,不需要手动 import 'antd/dist/reset.css'
+
+ReactDOM.createRoot(document.getElementById('root')).render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>,
+)

+ 297 - 0
front/src/pages/Dashboard.jsx

@@ -0,0 +1,297 @@
+import React, { useState, useMemo } from 'react';
+import { 
+  Table, Button, Space, message, Layout, Typography, 
+  Card, Tooltip, Badge, Row, Col, Statistic, Dropdown, Avatar 
+} from 'antd';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import { 
+  PlayCircleFilled, 
+  PauseCircleFilled, 
+  ReloadOutlined, 
+  SettingOutlined,
+  CloudUploadOutlined,
+  FileTextOutlined,
+  PlusOutlined,
+  MoreOutlined,
+  GlobalOutlined,
+  UserOutlined,
+  AppstoreOutlined,
+  ThunderboltFilled
+} from '@ant-design/icons';
+
+// API & Components
+import { getStatus, startGroup, stopGroup, restartGroup } from '../api';
+import ConfigModal from '../components/ConfigModal';
+import UpgradeModal from '../components/UpgradeModal';
+import LogViewer from '../components/LogViewer';
+import CreateGroupModal from '../components/CreateGroupModal';
+
+const { Header, Content } = Layout;
+const { Title, Text } = Typography;
+
+const Dashboard = () => {
+  const queryClient = useQueryClient();
+  
+  // State
+  const [configGroupId, setConfigGroupId] = useState(null);
+  const [logGroupId, setLogGroupId] = useState(null);
+  const [isUpgradeOpen, setIsUpgradeOpen] = useState(false);
+  const [isCreateOpen, setIsCreateOpen] = useState(false);
+
+  // 1. Data Fetching
+  const { data: statusResp, isLoading } = useQuery({
+    queryKey: ['status'],
+    queryFn: getStatus,
+    refetchInterval: 3000,
+    refetchOnWindowFocus: true,
+  });
+
+  const dataSource = statusResp?.data || [];
+
+  // 2. Statistics Calculation
+  const stats = useMemo(() => {
+    const total = dataSource.length;
+    const running = dataSource.filter(d => d.running).length;
+    const stopped = total - running;
+    const totalInstances = dataSource.reduce((acc, curr) => acc + (curr.instances || 0), 0);
+    return { total, running, stopped, totalInstances };
+  }, [dataSource]);
+
+  // 3. Actions
+  const actionMutation = useMutation({
+    mutationFn: ({ fn, id }) => fn(id),
+    onSuccess: () => {
+      message.success('Command sent successfully');
+      queryClient.invalidateQueries(['status']);
+    },
+    onError: (err) => message.error('Operation failed: ' + err.message),
+  });
+
+  const handleAction = (fn, id) => actionMutation.mutate({ fn, id });
+
+  // 4. Columns Definition
+  const columns = [
+    {
+      title: 'Identity',
+      key: 'identity',
+      width: 220,
+      render: (_, record) => (
+        <Space>
+           <Avatar shape="square" size="large" icon={<AppstoreOutlined />} style={{ backgroundColor: record.running ? '#e6f7ff' : '#f5f5f5', color: record.running ? '#1890ff' : '#ccc' }} />
+           <div style={{ display: 'flex', flexDirection: 'column' }}>
+             <Text strong style={{ fontSize: '15px' }}>{record.id}</Text>
+             <Text type="secondary" style={{ fontSize: '12px' }}>{record.plugin}</Text>
+           </div>
+        </Space>
+      )
+    },
+    {
+      title: 'Status',
+      key: 'status',
+      width: 120,
+      render: (_, record) => (
+         <Badge 
+            status={record.running ? "processing" : "default"} 
+            text={
+                <span style={{ 
+                    color: record.running ? '#52c41a' : '#999', 
+                    fontWeight: 500 
+                }}>
+                    {record.running ? 'Running' : 'Stopped'}
+                </span>
+            } 
+         />
+      ),
+    },
+    { 
+      title: 'Target', 
+      dataIndex: 'instances', 
+      key: 'instances', 
+      width: 100,
+      align: 'center',
+      render: (val) => <TagPill value={val} label="Inst" />
+    },
+    { 
+      title: 'Resources', 
+      key: 'pools',
+      render: (_, record) => (
+        <Space direction="vertical" size={0}>
+          <Space size={4}>
+            <UserOutlined style={{ color: '#8c8c8c', fontSize: '12px' }} />
+            <Text style={{ fontSize: '13px', color: record.local_account_pool ? '#595959' : '#d9d9d9' }}>
+               {record.local_account_pool || 'N/A'}
+            </Text>
+          </Space>
+          <Space size={4}>
+            <GlobalOutlined style={{ color: '#8c8c8c', fontSize: '12px' }} />
+             <Text style={{ fontSize: '13px', color: record.proxies_pool ? '#595959' : '#d9d9d9' }}>
+               {record.proxies_pool || 'N/A'}
+            </Text>
+          </Space>
+        </Space>
+      )
+    },
+    {
+      title: 'Actions',
+      key: 'action',
+      width: 200,
+      align: 'right',
+      render: (_, record) => {
+        // Dropdown Menu for secondary actions
+        const menuItems = [
+            { key: 'logs', label: 'View Logs', icon: <FileTextOutlined />, onClick: () => setLogGroupId(record.id) },
+            { key: 'config', label: 'Configuration', icon: <SettingOutlined />, onClick: () => setConfigGroupId(record.id) },
+        ];
+
+        return (
+            <Space size="small">
+                {record.running ? (
+                    <Tooltip title="Stop">
+                        <Button 
+                            type="text" 
+                            danger 
+                            icon={<PauseCircleFilled style={{ fontSize: '18px' }} />} 
+                            onClick={() => handleAction(stopGroup, record.id)} 
+                            style={{ background: '#fff1f0', border: '1px solid #ffa39e' }}
+                        />
+                    </Tooltip>
+                ) : (
+                    <Tooltip title="Start">
+                        <Button 
+                            type="text" 
+                            icon={<PlayCircleFilled style={{ fontSize: '18px', color: '#52c41a' }} />} 
+                            onClick={() => handleAction(startGroup, record.id)}
+                            style={{ background: '#f6ffed', border: '1px solid #b7eb8f' }}
+                        />
+                    </Tooltip>
+                )}
+                
+                <Tooltip title="Restart">
+                     <Button 
+                        icon={<ReloadOutlined />} 
+                        onClick={() => handleAction(restartGroup, record.id)} 
+                    />
+                </Tooltip>
+
+                <Dropdown menu={{ items: menuItems }} trigger={['click']}>
+                    <Button icon={<MoreOutlined />} />
+                </Dropdown>
+            </Space>
+        );
+      },
+    },
+  ];
+
+  return (
+    <Layout style={{ minHeight: '100vh', background: '#f0f2f5' }}>
+      {/* 1. Modern Header */}
+      <Header style={{ 
+        background: '#fff', 
+        padding: '0 24px', 
+        boxShadow: '0 2px 8px #f0f1f2', 
+        display: 'flex', 
+        alignItems: 'center', 
+        justifyContent: 'space-between',
+        zIndex: 10
+      }}>
+        <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
+            <div style={{ 
+                width: '36px', height: '36px', 
+                background: 'linear-gradient(135deg, #1890ff 0%, #096dd9 100%)', 
+                borderRadius: '8px', 
+                display: 'flex', alignItems: 'center', justifyContent: 'center',
+                color: '#fff', fontSize: '20px'
+            }}>
+                <ThunderboltFilled />
+            </div>
+            <Title level={4} style={{ margin: 0, fontWeight: 600 }}>Visa Manager</Title>
+        </div>
+
+        <Space size="middle">
+            <Button 
+                onClick={() => setIsUpgradeOpen(true)} 
+                icon={<CloudUploadOutlined />}
+            >
+                OTA Update
+            </Button>
+            <Button 
+                type="primary" 
+                icon={<PlusOutlined />} 
+                onClick={() => setIsCreateOpen(true)}
+                style={{ borderRadius: '6px', height: '36px', padding: '0 20px' }}
+            >
+                Create Group
+            </Button>
+        </Space>
+      </Header>
+
+      <Content style={{ padding: '24px', maxWidth: '1600px', margin: '0 auto', width: '100%' }}>
+        
+        {/* 2. Stats Overview Cards */}
+        <Row gutter={16} style={{ marginBottom: '24px' }}>
+            <Col span={6}>
+                <StatCard title="Total Groups" value={stats.total} icon={<AppstoreOutlined />} color="#1890ff" />
+            </Col>
+            <Col span={6}>
+                <StatCard title="Running" value={stats.running} icon={<PlayCircleFilled />} color="#52c41a" />
+            </Col>
+            <Col span={6}>
+                <StatCard title="Stopped" value={stats.stopped} icon={<PauseCircleFilled />} color="#ff4d4f" />
+            </Col>
+            <Col span={6}>
+                <StatCard title="Total Instances" value={stats.totalInstances} icon={<ThunderboltFilled />} color="#faad14" />
+            </Col>
+        </Row>
+
+        {/* 3. Main Data Table */}
+        <Card 
+            bordered={false} 
+            bodyStyle={{ padding: '0' }}
+            style={{ borderRadius: '12px', boxShadow: '0 2px 8px rgba(0,0,0,0.04)', overflow: 'hidden' }}
+        >
+          <Table 
+            dataSource={dataSource} 
+            columns={columns} 
+            loading={isLoading} 
+            rowKey="id" 
+            pagination={false}
+            rowClassName="align-middle"
+            size="middle"
+          />
+        </Card>
+      </Content>
+
+      {/* Modals */}
+      {configGroupId && <ConfigModal groupId={configGroupId} open={!!configGroupId} onClose={() => setConfigGroupId(null)} />}
+      {logGroupId && <LogViewer groupId={logGroupId} open={!!logGroupId} onClose={() => setLogGroupId(null)} />}
+      <UpgradeModal open={isUpgradeOpen} onClose={() => setIsUpgradeOpen(false)} />
+      <CreateGroupModal open={isCreateOpen} onClose={() => setIsCreateOpen(false)} />
+    </Layout>
+  );
+};
+
+// --- Helper Components ---
+
+const StatCard = ({ title, value, icon, color }) => (
+    <Card bordered={false} style={{ borderRadius: '12px', boxShadow: '0 2px 8px rgba(0,0,0,0.02)' }}>
+        <Statistic 
+            title={<span style={{ fontSize: '13px', fontWeight: 500, color: '#8c8c8c' }}>{title}</span>}
+            value={value}
+            valueStyle={{ fontWeight: 'bold', fontSize: '24px', color: '#262626' }}
+            prefix={<span style={{ color, marginRight: '8px', fontSize: '20px', position: 'relative', top: '2px' }}>{icon}</span>}
+        />
+    </Card>
+);
+
+const TagPill = ({ value, label }) => (
+    <div style={{ 
+        background: '#f5f5f5', borderRadius: '4px', 
+        padding: '2px 8px', display: 'inline-block',
+        fontSize: '12px', color: '#595959', border: '1px solid #d9d9d9'
+    }}>
+        <span style={{ fontWeight: 'bold', marginRight: '4px' }}>{value}</span> 
+        <span style={{ fontSize: '10px', color: '#8c8c8c' }}>{label}</span>
+    </div>
+);
+
+export default Dashboard;

+ 22 - 0
front/vite.config.js

@@ -0,0 +1,22 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+  plugins: [react()],
+  server: {
+    proxy: {
+      // API 请求代理
+      '/api': {
+        target: 'http://127.0.0.1:8000',
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/api/, '')
+      },
+      // WebSocket 请求代理 (新增部分)
+      '/ws': {
+        target: 'ws://127.0.0.1:8000',
+        ws: true, // 开启 WebSocket 代理支持
+        changeOrigin: true
+      }
+    }
+  }
+})

+ 360 - 177
gco.py

@@ -4,7 +4,7 @@ import time
 import json
 import random
 import threading
-from typing import List, Optional, Callable
+from typing import List, Dict, Tuple, Any, Optional, Callable
 from concurrent.futures import wait
 
 # 导入所有依赖
@@ -13,7 +13,6 @@ from vs_plg import IVSPlg
 from vs_plg_factory import VSPlgFactory 
 from toolkit.account_manager import AccountManager 
 from toolkit.proxy_manager import ProxyManager 
-from toolkit.binding_manager import BindingManager 
 from toolkit.thread_pool import ThreadPool 
 from toolkit.vs_cloud_api import VSCloudApi
 
@@ -36,6 +35,9 @@ class GCO:
         
         self.m_monitor_thread: Optional[threading.Thread] = None
         self.m_creator_thread: Optional[threading.Thread] = None
+        
+        self.m_pending_builtin = 0  # 正在创建中的内置账号数量
+        self.m_pending_orders = 0   # 正在创建中的订单账号数量
 
     def start(self):
         """
@@ -95,52 +97,88 @@ class GCO:
 
     def _monitor_loop(self):
         """
-        @brief 监控循环:定期检查实例健康状况,执行查询任务,并根据结果触发预订。
+        @brief 监控循环:定期检查实例健康状况,执行查询任务。
+               一旦发现号源,立即触发所有实例进行批量预订。
         """
         self._log("[START] monitor loop starting...")
         rng = random.Random()
         
+        # 创建一个临时线程池用于并发抢票,避免阻塞监控循环太久
+        # max_workers 根据你的最大并发账号数调整,或者设为 None (默认 CPU核心数 * 5)
+        
         while not self.m_stop_event.is_set():
             sleep_ms = 0.1 + rng.randint(0, 20) / 1000.0
             time.sleep(sleep_ms)
             
             now = time.time()
             
-            # 拷贝任务列表
+            # 1. 拷贝任务列表 (Snapshot)
             tasks_to_process = []
             with self.m_lock:
                 tasks_to_process = list(self.m_tasks)
             
+            # 标记本轮循环是否触发了批量抢票
+            batch_booking_triggered = False
+
             for task in tasks_to_process:
+                # 二次检查任务是否还在列表中(防止在遍历过程中被移除)
                 with self.m_lock:
                     if task not in self.m_tasks:
                         continue 
 
+                # 健康检查
                 if not task.instance.health_check():
                     continue 
                 
+                # 检查时间窗口
                 if now < task.next_run:
                     continue
                 
-                # 执行查询
-                is_booking_triggered = False
+                # === 执行查询 ===
                 try:
+                    # 这里的 task 充当了“哨兵”的角色
                     result = task.instance.query()
+                    
                     if result.success:
-                        # === 关键修改:_on_query_result 现在会阻塞直到抢票结束 ===
-                        self._on_query_result(task.instance, result)
-                        is_booking_triggered = True
+                        self._log(f"🔥 Slot Found by [{task.instance.get_group_id()}]! Triggering BATCH BOOKING for {len(tasks_to_process)} workers.")
+                        
+                        # === [核心修改]:一人发现,全员出击 ===
+                        
+                        # 1. 准备并发任务
+                        # 我们使用刚刚快照的 tasks_to_process,或者重新获取一次全量列表
+                        # 重点:所有实例 (w.instance) 都使用同一份查询结果 (result) 去抢
+                        futures = []
+                        for worker in tasks_to_process:
+                            # 提交到线程池并发执行,极大减少时间差
+                            f = ThreadPool.getInstance().enqueue(
+                                self._on_query_result, 
+                                worker, 
+                                result
+                            )
+                            futures.append(f)
+                        
+                        # 2. 等待所有抢票任务结束 (可选,根据业务需求决定是否阻塞监控)
+                        # 如果不wait,监控线程会立刻继续,可能导致重复触发
+                        # 建议 wait,确保这一波抢票彻底结束
+                        for f in futures:
+                            try:
+                                f.result() # 获取结果,捕获异常
+                            except Exception as e:
+                                self._log(f"Batch booking exception: {e}")
+
+                        # 3. 标记触发状态
+                        batch_booking_triggered = True
+                        break 
+                    
                     else:
-                        self._log("Query done, No availability found")
+                        # 没查到,仅当前 task 记录日志
+                        self._log(f"Query done by {task.instance.get_group_id()}, No availability")
+
                 except Exception as e:
                     self._log(f"Exception during query: {e}")
 
-                # 计算下次运行时间
-                # 如果刚刚触发了抢票(无论成功失败),建议强制加长一点冷却时间,防止反爬
-                if is_booking_triggered:
-                    interval = rng.randint(30, 60) # 抢完票休息 30-60 秒
-                    self._log(f"Booking attempted, entering cooldown for {interval} sec.")
-                else:
+                # === 计算下次运行时间 (仅针对当前 query 的 task,除非触发了 batch) ===
+                if not batch_booking_triggered:
                     interval = 30
                     mode = task.qw_cfg.mode
                     if mode == QueryWaitMode.Loop:
@@ -149,8 +187,20 @@ class GCO:
                         interval = task.qw_cfg.fixed_wait
                     elif mode == QueryWaitMode.Random:
                         interval = rng.randint(task.qw_cfg.random_min, task.qw_cfg.random_max)
+                    
+                    task.next_run = time.time() + interval
+
+            # === [批量冷却逻辑] ===
+            # 如果触发了批量抢票,所有人都需要进入冷却,防止接口被瞬间打死
+            if batch_booking_triggered:
+                cooldown = rng.randint(30, 60) # 全员冷却 30-60 秒
+                self._log(f"Batch booking finished. All workers entering cooldown for {cooldown}s.")
                 
-                task.next_run = time.time() + interval
+                now_ts = time.time()
+                with self.m_lock:
+                    # 更新所有任务的下次运行时间
+                    for t in self.m_tasks:
+                        t.next_run = now_ts + cooldown
 
             # 清理不健康实例
             with self.m_lock:
@@ -163,100 +213,178 @@ class GCO:
 
     def _creator_loop(self):
         """
-        @brief 创建者循环:根据目标实例数量,创建和补充新的插件实例。
+        @brief 创建者循环:双轨制并发控制 + Pending 计数防超发
         """
         self._log("[START] creator loop starting...")
         
         while not self.m_stop_event.is_set():
-            time.sleep(0.1) # 避免空转太快
+            time.sleep(1.0) # 保持 1秒间隔,给资源管理器缓冲时间
+
+            # -------------------------------------------------------------
+            # 1. 统计当前状态 (Active + Pending)
+            # -------------------------------------------------------------
+            current_builtin = 0
+            current_orders = 0
             
-            diff = 0
             with self.m_lock:
-                current_instances_count = len(self.m_tasks)
-                diff = self.m_cfg.target_instances - current_instances_count
-            
-            if diff > 0:
-                self._log(f"Need to create {diff} new instance(s). Current: {current_instances_count}")
+                for t in self.m_tasks:
+                    if t.task_ref:
+                        current_orders += 1
+                    else:
+                        current_builtin += 1
                 
-                # 准备配置
-                plg_cfg = self._make_plg_config()
-                if not plg_cfg:
-                    self._log("Failed to prepare plugin configuration, sleeping 30s.")
-                    time.sleep(30) # 等待资源 (账户/代理) 恢复
-                    continue
+                # 读取正在创建中的计数 (快照)
+                pending_builtin = self.m_pending_builtin
+                pending_orders = self.m_pending_orders
 
-                # 在线程池中创建实例,模拟C++的异步创建
-                future = ThreadPool.getInstance().enqueue(self._create_instance, plg_cfg)
-                inst = future.result() # 等待创建完成
-
-                if inst:
-                    with self.m_lock:
-                        # 确保在添加到任务列表之前,实例数量仍然低于目标值
-                        if len(self.m_tasks) < self.m_cfg.target_instances:
-                            new_task = Task(
-                                instance=inst,
-                                qw_cfg=self.m_cfg.query_wait,
-                                next_run=time.time() # 立即执行第一次查询
-                            )
-                            self.m_tasks.append(new_task)
-                            self._log(f"New instance added. Total instances: {len(self.m_tasks)}")
-                        else:
-                            self._log("Target instances already met, discarding newly created instance.")
+            # -------------------------------------------------------------
+            # 2. 计算缺口
+            # -------------------------------------------------------------
+            
+            # === [内置账号缺口] ===
+            needed_builtin = 0
+            # 逻辑:只要目标数量 > 0,就尝试补充 (兼容需要账号和不需要账号的模式)
+            if self.m_cfg.target_instances > 0:
+                total_builtin_proj = current_builtin + pending_builtin
+                needed_builtin = self.m_cfg.target_instances - total_builtin_proj
+
+            # === [订单账号缺口] ===
+            needed_order = 0
+            # 逻辑:必须需要账号(need_account=True) 且 限制数量大于 0
+            # (order_account_enable 已移除,直接看 limit)
+            if self.m_cfg.need_account and self.m_cfg.order_account_online_limit > 0:
+                total_order_proj = current_orders + pending_orders
+                needed_order = self.m_cfg.order_account_online_limit - total_order_proj
+
+            # 如果两边都满了,跳过本轮
+            if needed_builtin <= 0 and needed_order <= 0:
+                continue
+
+            # -------------------------------------------------------------
+            # 3. 准备配置
+            # -------------------------------------------------------------
+            config_data = self._prepare_next_config(
+                need_builtin=(needed_builtin > 0),
+                need_order=(needed_order > 0),
+            )
+
+            if not config_data:
+                time.sleep(2.0)
+                continue
+
+            plg_cfg, task_ref = config_data
+            
+            # -------------------------------------------------------------
+            # 4. [绝对核心] 提交前立即增加 Pending 计数
+            # -------------------------------------------------------------
+            # 必须在这里加!防止下一秒循环重复创建!
+            with self.m_lock:
+                if task_ref:
+                    self.m_pending_orders += 1
                 else:
-                    self._log("Failed to create plugin instance.")
-                    # 可以在这里添加重试逻辑或错误处理
+                    self.m_pending_builtin += 1
+            
+            p_type = "Order" if task_ref else "Built-in"
+            self._log(f"+++ Spawning {p_type} (Pending: {self.m_pending_builtin}/{self.m_pending_orders})...")
 
-            # 模拟创建间隔,避免瞬间创建过多实例
-            time.sleep(random.uniform(1.0, 5.0))
+            # 5. 异步提交
+            try:
+                ThreadPool.getInstance().enqueue(
+                    self._create_and_register_plg_worker, 
+                    plg_cfg, 
+                    task_ref
+                )
+            except Exception as e:
+                # 提交失败回滚计数
+                self._log(f"Failed to enqueue task: {e}")
+                with self.m_lock:
+                    if task_ref: self.m_pending_orders -= 1
+                    else: self.m_pending_builtin -= 1
+            
+            # 错开并发
+            time.sleep(random.uniform(0.5, 1.0))
 
         self._log("[STOP] creator loop exiting...")
 
-    def _make_plg_config(self) -> Optional[VSPlgConfig]:
+    def _prepare_next_config(self, need_builtin: bool, need_order: bool) -> Optional[Tuple[VSPlgConfig, Optional[Dict[str, Any]]]]:
         """
-        @brief 准备插件配置 (账号、代理等)。
+        @brief 准备下一个插件实例的配置
         """
-        self._log("Preparing plugin configuration...")
         plg_cfg = VSPlgConfig()
         plg_cfg.debug = self.m_cfg.debug
+        plg_cfg.free_config = self.m_cfg.free_config
+        plg_cfg.session_max_life = self.m_cfg.session_max_life
         
-        # 账号配置
-        if self.m_cfg.need_account:
-            account = AccountManager.Instance().get_next_account(self.m_cfg.account_pool)
-            if not account:
-                self._log(f"No available accounts for pool {self.m_cfg.account_pool}")
-                return None
-            plg_cfg.account.id = account["id"]
-            plg_cfg.account.username = account["username"]
-            plg_cfg.account.password = account["password"]
-            plg_cfg.account.lock_until = account.get("lock_until", "")
-            self._log(f"Using account ID {plg_cfg.account.id}, username {plg_cfg.account.username}")
-
-        # 代理配置
+        task_ref = None 
+        config_ready = False
+        pool_name = self.m_cfg.local_account_pool 
+
+        # =================================================================
+        # 1. 账号获取
+        # =================================================================
+        if not self.m_cfg.need_account:
+            # === 游客模式 (无需账号) ===
+            if need_builtin:
+                plg_cfg.account.id = 0
+                plg_cfg.account.username = "Guest"
+                config_ready = True
+                task_ref = None
+        else:
+            # === 标准模式 (需要账号) ===
+            
+            # A. 优先补充内置账号 (只要 target_instances 还有缺口)
+            if need_builtin:
+                # 获取并锁定账号
+                account = AccountManager.Instance().next(
+                    pool_name, 
+                    lock_duration=self.m_cfg.account_login_interval * 60
+                )
+                if account:
+                    plg_cfg.account.id = account["id"]
+                    plg_cfg.account.username = account["username"]
+                    plg_cfg.account.password = account["password"]
+                    plg_cfg.account.lock_until = account.get("lock_until", 0)
+                    config_ready = True
+                    task_ref = None
+                    self._log(f"Selected Built-in: {plg_cfg.account.username}")
+
+            # B. 次选补充订单账号 (如果内置不需要 或 池子空了)
+            # 只有当 limit > 0 时才尝试
+            if not config_ready and need_order and self.m_cfg.order_account_online_limit > 0:
+                try:
+                    routing_key = self.m_cfg.order_account_routing
+                    task_ref = VSCloudApi.Instance().get_vas_task_pop(routing_key)
+                    
+                    if task_ref:
+                        user_inputs = task_ref.get('user_inputs', {})
+                        plg_cfg.account.id = 0 # 临时账号
+                        plg_cfg.account.username = user_inputs.get(self.m_cfg.input_map_username, "")
+                        plg_cfg.account.password = user_inputs.get(self.m_cfg.input_map_password, "")
+                        
+                        if plg_cfg.account.username:
+                            config_ready = True
+                            self._log(f"Selected Order Acc: {plg_cfg.account.username}")
+                        else:
+                            VSCloudApi.Instance().return_vas_task_to_queue(task_ref['id'])
+                            return None
+                except Exception as e:
+                    self._log(f"Get Order task exception, e={e}")
+
+        if not config_ready:
+            return None
+
+        # =================================================================
+        # 2. 代理配置
+        # =================================================================
         if self.m_cfg.need_proxy:
-            proxy = None
-            if self.m_cfg.need_ip_bind:
-                proxy_id = BindingManager.Instance().get_bounded_proxy_id(self.m_cfg.account_pool, plg_cfg.account.id)
-                if proxy_id is None: # 没有绑定代理,需要获取一个新的并绑定
-                    bounded_ids = BindingManager.Instance().get_bounded_proxies_ids(self.m_cfg.account_pool, self.m_cfg.proxy_pool)
-                    proxy = ProxyManager.Instance().get_unbind_proxy(self.m_cfg.proxy_pool, bounded_ids)
-                    if not proxy:
-                        self._log(f"No available unbind proxy in pool {self.m_cfg.proxy_pool}")
-                        return None
-                    BindingManager.Instance().create_binding(
-                        self.m_cfg.account_pool, plg_cfg.account.id,
-                        self.m_cfg.proxy_pool, proxy["id"], "dynamic")
-                    self._log(f"Created dynamic binding: account {plg_cfg.account.id} -> proxy {proxy['id']}")
-                else:
-                    all_proxies_in_pool = ProxyManager.Instance()._proxies.get(self.m_cfg.proxy_pool, [])
-                    proxy = next((p for p in all_proxies_in_pool if p["id"] == proxy_id), None)
-                    if not proxy:
-                        self._log(f"Bounded proxy ID {proxy_id} not found in pool {self.m_cfg.proxy_pool}")
-                        return None
-            else:
-                proxy = ProxyManager.Instance().get_next_proxy(self.m_cfg.proxy_pool)
-                if not proxy:
-                    self._log(f"No available proxy in pool {self.m_cfg.proxy_pool}")
+            # 轮询代理
+            proxy = ProxyManager.Instance().next(self.m_cfg.proxy_pool)
+            if not proxy:
+                try:
+                    if task_ref: VSCloudApi.Instance().return_vas_task_to_queue(task_ref['id'])
                     return None
+                except Exception as e:
+                    self._log(f"Return Order task to queue exception, e={e}")
 
             plg_cfg.proxy.id = proxy["id"]
             plg_cfg.proxy.ip = proxy["ip"]
@@ -264,101 +392,156 @@ class GCO:
             plg_cfg.proxy.scheme = proxy["scheme"]
             plg_cfg.proxy.username = proxy.get("username", "")
             plg_cfg.proxy.password = proxy.get("password", "")
-            plg_cfg.proxy.lock_until = proxy.get("lock_until", "")
-            self._log(f"Using proxy ID {plg_cfg.proxy.id}, IP {plg_cfg.proxy.ip}:{plg_cfg.proxy.port}")
+            plg_cfg.proxy.lock_until = proxy.get("lock_until", 0)
 
-        plg_cfg.free_config = self.m_cfg.free_config
-        self._log("Plugin configuration prepared.")
-        return plg_cfg
-
-    def _create_instance(self, plg_cfg: VSPlgConfig) -> Optional[IVSPlg]:
-        # """
-        # @brief 创建并初始化单个插件实例。
-        # 这个方法在 creator_loop 的线程池中执行。
-        # """
-        self._log(f"Creating plugin instance (plugin={self.m_cfg.plugin_config.plugin_name})...")
+        return plg_cfg, task_ref
+
+    def _create_and_register_plg_worker(self, plg_cfg: VSPlgConfig, task_ref: Optional[Dict[str, Any]] = None):
+        """
+        @brief 异步创建工作线程
+        """
+        instance = None
+        creation_success = False 
+        
         try:
-            inst = self.m_factory.create(self.m_cfg.identifier, self.m_cfg.plugin_config.plugin_name)
-            inst.set_log(self.m_logger)
-            inst.set_config(plg_cfg)
-            inst.create_session()
-            if self.m_cfg.need_account and self.m_cfg.account_login_interval > 0:
-                AccountManager.Instance().lock_account(
-                    self.m_cfg.account_pool, plg_cfg.account.id, self.m_cfg.account_login_interval * 60)
-            self._log("Plugin instance created and session established.")
-            return inst
+            # 1. 耗时操作:实例化 & 登录
+            instance = self.m_factory.create(self.m_cfg.identifier, self.m_cfg.plugin_config.plugin_name)
+            instance.set_log(self.m_logger)
+            instance.set_config(plg_cfg)
+            
+            instance.create_session() # 可能耗时很久
+
+            # 2. 注册到任务列表
+            with self.m_lock:
+                # 既然允许短暂超发,这里直接添加,不做严格的拒绝并返回逻辑
+                
+                # 计算预定权限
+                book_allowed = False
+                if not self.m_cfg.account_bind_applicant:
+                    book_allowed = True
+                elif self.m_cfg.account_bind_applicant and task_ref:
+                    book_allowed = True
+                
+                new_task = Task(
+                    instance=instance,
+                    qw_cfg=self.m_cfg.query_wait,
+                    next_run=time.time(),
+                    task_ref=task_ref,
+                    book_allowed=book_allowed
+                )
+                self.m_tasks.append(new_task)
+                
+                creation_success = True
+                
+                p_type = "Order" if task_ref else "Built-in"
+                self._log(f"=== Instance Registered [{p_type}]. Total Active: {len(self.m_tasks)} ===")
+
         except Exception as e:
-            self._log(f"Error creating plugin instance: {e}")
-        return None
+            self._log(f"Creation failed for {plg_cfg.account.username}: {e}")
+            
+        finally:
+            # -------------------------------------------------------------
+            # 3. [绝对核心] 必须扣减 Pending
+            # -------------------------------------------------------------
+            # 无论上面是否抛出异常,或者是否注册成功,Pending 必须释放
+            with self.m_lock:
+                if task_ref:
+                    self.m_pending_orders -= 1
+                    if self.m_pending_orders < 0: self.m_pending_orders = 0
+                else:
+                    self.m_pending_builtin -= 1
+                    if self.m_pending_builtin < 0: self.m_pending_builtin = 0
+            
+            # 4. 失败回滚
+            if not creation_success:
+                # 如果是云端订单,必须归还,否则丢单
+                if task_ref:
+                    self._log(f"Rolling back task {task_ref['id']}")
+                    try:
+                        VSCloudApi.Instance().return_vas_task_to_queue(task_ref['id'])
+                    except:
+                        pass
 
-    def _on_query_result(self, sptr: IVSPlg, query_result: VSQueryResult):
+    def _on_query_result(self, t: Task, query_result: VSQueryResult):
         self._log(f"Query result received: {str(query_result)}. BLOCKING monitor loop for booking...")
         
-        # 定义内部预订任务
-        def book_task(inst: IVSPlg, result: VSQueryResult):
-            task_id = None
-            try:
-                # 1. 获取对应的用户任务 (Pop Task)
-                booking_routing_key = f'auto.{result.routing_key}' if result.routing_key else "default"
+        if not t.book_allowed:
+            return
+
+        # -------------------------------------------------------
+        # 1. 准备任务数据 (Data Preparation)
+        # -------------------------------------------------------
+        task_data = t.task_ref
+        is_cloud_task = False # 标记:是否为云端临时取出的任务(需要回滚)
+
+        # 如果没有绑定本地任务,尝试从云端 Pop
+        if not task_data:
+            booking_routing_key = f'auto.{query_result.routing_key}' if query_result.routing_key else "default"
+            task_data = VSCloudApi.Instance().get_vas_task_pop(booking_routing_key)
+            
+            if not task_data:
+                # 这种情况属于:内置账号查到了号,但云端没有待处理订单,直接放弃
+                self._log(f"No pending task found for key {booking_routing_key}. Abandoning slot.")
+                return
+            
+            is_cloud_task = True
+            self._log(f"Picked up Cloud Task ID {task_data['id']} for booking...")
+
+        # 统一提取核心参数
+        task_id = task_data['id']
+        order_id = task_data.get('order_id')
+        user_input = task_data.get('user_inputs', {})
+
+        # -------------------------------------------------------
+        # 2. 执行预订 (Execution)
+        # -------------------------------------------------------
+        booking_success = False
+        
+        try:
+            # 统一调用,无需区分来源
+            book_res = t.instance.book(query_result, user_input)
+            
+            # -------------------------------------------------------
+            # 3. 成功处理 (Success Handling)
+            # -------------------------------------------------------
+            if book_res.success:
+                booking_success = True
+                self._log(f"✅ Booking SUCCESS! Order: {order_id}")
                 
-                # 尝试获取任务
-                task = VSCloudApi.Instance().get_vas_task_pop(booking_routing_key)
+                current_grab_info = {
+                    "account": book_res.account,
+                    "session_id": book_res.session_id,
+                    "slot_date": book_res.book_date,
+                    "slot_time": book_res.book_time,
+                    "timestamp": int(time.time()),
+                    "payment_link": book_res.payment_link,
+                }
                 
-                if not task:
-                    self._log(f"No pending task found for key {booking_routing_key}. Abandoning slot.")
-                    return 
-
-                task_id = task['id']
-                order_id = task['order_id']
-                user_input = task.get('user_inputs', {})
+                update_data = {
+                    "status": "grabbed",
+                    "grabbed_history": current_grab_info
+                }
                 
-                self._log(f"Picked up Task ID {task_id} for booking...")
+                VSCloudApi.Instance().update_vas_task(task_id, update_data)
+                self._log(f"Task {task_id} marked as GRABBED.")
+            
+            else:
+                self._log(f"❌ Booking Failed for Order {order_id}: {book_res.message}")
 
-                # 2. 执行预订
-                # 注意:插件的 book 方法需要接收 user_input
-                book_res = inst.book(result, user_input)
+        except Exception as e:
+            self._log(f"Exception during booking for Order {order_id}: {e}")
 
-                # 3. 处理结果
-                if book_res.success:
-                    self._log(f" Booking SUCCESS! Order: {order_id}")
-                    
-                    # 4. 成功逻辑:更新任务状态为 grabbed
-                    # 包含后端需要的关键信息
-                    current_grab_info = {
-                        "account": book_res.account,
-                        "session_id": book_res.session_id,
-                        "slot_date": book_res.book_date,
-                        "slot_time": book_res.book_time,
-                        "timestamp": int(time.time()),
-                        "payment_link": book_res.payment_link,
-                    }
-                    
-                    update_data = {
-                        "status": "grabbed",
-                        # === 修改点:直接覆盖 ===
-                        # 直接发送字典对象,requests 会自动序列化为 JSON Object
-                        # 满足后端 "type":"dict_type" 的校验
-                        "grabbed_history": current_grab_info
-                    }
-                    
-                    VSCloudApi.Instance().update_vas_task(task_id, update_data)
-                    self._log(f"Task {task_id} marked as GRABBED.")
-                    # 成功后 task_id 置空,防止 finally 块再次将其重置为 pending
-                    task_id = None 
-            except Exception as e:
-                self._log(f"Exception during booking: {e}")
-            
-            finally:
-                # 5. Return to Queue (回滚机制)
-                if task_id is not None:
-                    self._log(f"Returning Task {task_id} to queue (status=pending).")
-                    try:
-                        VSCloudApi.Instance().return_vas_task_to_queue(task_id)
-                    except Exception as ex:
-                        self.log(f"Failed to return task to queue: {ex}")
+        finally:
+            # -------------------------------------------------------
+            # 4. 回滚机制 (Rollback / Return to Queue)
+            # -------------------------------------------------------
+            # 只有两个条件同时满足才回滚:
+            # 1. 任务来自云端队列 (is_cloud_task is True)
+            # 2. 预定没有成功 (booking_success is False) - 包括预定失败或发生异常
+            if is_cloud_task and not booking_success:
+                self._log(f"Returning Task {task_id} to queue (status=pending).")
+                try:
+                    VSCloudApi.Instance().return_vas_task_to_queue(task_id)
+                except Exception as ex:
+                    self._log(f"Failed to return task to queue: {ex}")
 
-        futures = []
-        f = ThreadPool.getInstance().enqueue(book_task, sptr, query_result)
-        futures.append(f)
-        
-        wait(futures)

+ 1 - 1
main_server.py

@@ -36,7 +36,7 @@ def main():
         # 退出时清理
         VSC_INFO("main", "Shutting down...")
         # 停止所有组
-        for gid in list(manager.coordinators.keys()):
+        for gid in list(manager.executors.keys()):
             manager.stop_group(gid)
 
 if __name__ == "__main__":

+ 32 - 15
plugins/bls_plugin.py

@@ -20,7 +20,7 @@ from cryptography.hazmat.backends import default_backend
 
 # 框架依赖
 from vs_plg import IVSPlg 
-from vs_types import VSPlgConfig, VSQueryResult, VSBookResult, AvailabilityStatus, NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError
+from vs_types import VSPlgConfig, VSQueryResult, VSBookResult, DateAvailability, TimeSlot, AvailabilityStatus, NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError
 from toolkit.vs_cloud_api import VSCloudApi 
 
 class BlsPlugin(IVSPlg):
@@ -38,10 +38,11 @@ class BlsPlugin(IVSPlg):
         
         # 运行时状态
         self.book_params: Dict = {} 
-        self.is_healthy = True
+        self.is_healthy: bool = True
         
         # OCR 服务地址默认值
-        self.local_service_url = ""
+        self.local_service_url: str = ""
+        self.session_create_time: float = 0
 
     def get_group_id(self) -> str:
         return self.group_id
@@ -57,7 +58,17 @@ class BlsPlugin(IVSPlg):
             self.local_service_url = self.free_config["local_service_url"]
 
     def health_check(self) -> bool:
-        return self.is_healthy
+        if not self.is_healthy:
+            return False
+        if self.session is None:
+            return False
+        if self.config.session_max_life > 0:
+            current_time = time.time()
+            elapsed_time = current_time - self.session_create_time
+            if elapsed_time > self.config.session_max_life * 60:
+                self._log(f"Session Life ({int(elapsed_time)}s) out of max life limit ({self.config.session_max_life * 60}s), mark as unhealth session")
+                return False
+        return True
 
     def create_session(self):
         self.session = requests.Session(
@@ -113,9 +124,10 @@ class BlsPlugin(IVSPlg):
         payload[real_user] = self.config.account.username
         payload[real_pass] = self.config.account.password
         login_resp = self._perform_request('POST', submit_url, data=payload, headers=headers)
-        if login_resp.json()['success']:
-            return
-        raise BizLogicError(message='Login failed')
+        if not login_resp.json()['success']:
+            raise BizLogicError(message='Login failed')
+        self.session_create_time = time.time()
+        self._log("Session created successfully.")
 
     # =========================================================================
     # 2. 查询流程 (Query)
@@ -192,16 +204,21 @@ class BlsPlugin(IVSPlg):
                 res.success = True
                 res.availability_status = AvailabilityStatus.Available
                 res.earliest_date = dates[0]
-                for d in dates:
-                    da = VSQueryResult.DateAvailability()
-                    da.date = d
-                    da.times = [] 
-                    time_slot = VSQueryResult.DateAvailability.TimeSlot(time="00:00", label="Available")
-                    da.times.append(time_slot)
-                    res.availability.append(da)
+
+                res.availability = [
+                    DateAvailability(
+                        date=d,
+                        times=[],
+                    )
+                    for d in dates
+                ]
+
             else:
-                res.success = False
+                # 查询成功,但没有可用日期
+                res.success = True
                 res.availability_status = AvailabilityStatus.NoneAvailable
+                res.availability = []
+
             return res
                 
         raise BizLogicError(message='Query page not found required field [var availDates]')

+ 29 - 12
plugins/de_plugin.py

@@ -13,7 +13,7 @@ from bs4 import BeautifulSoup
 
 
 from vs_plg import IVSPlg 
-from vs_types import VSPlgConfig, VSQueryResult, VSBookResult, AvailabilityStatus, NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError 
+from vs_types import VSPlgConfig, VSQueryResult, VSBookResult, TimeSlot, DateAvailability, AvailabilityStatus, NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError 
 from toolkit.vs_cloud_api import VSCloudApi 
 
 
@@ -57,6 +57,7 @@ class DePlugin(IVSPlg):
         
         # 默认 OCR 服务地址
         self.local_service_url = "http://127.0.0.1:8085"
+        self.session_create_time: float = 0
 
     def get_group_id(self) -> str:
         return self.group_id
@@ -75,7 +76,17 @@ class DePlugin(IVSPlg):
             self.local_service_url = self.free_config["local_service_url"]
 
     def health_check(self) -> bool:
-        return self.is_healthy
+        if not self.is_healthy:
+            return False
+        if self.session is None:
+            return False
+        if self.config.session_max_life > 0:
+            current_time = time.time()
+            elapsed_time = current_time - self.session_create_time
+            if elapsed_time > self.config.session_max_life * 60:
+                self._log(f"Session Life ({int(elapsed_time)}s) out of max life limit ({self.config.session_max_life * 60}s), mark as unhealth session")
+                return False
+        return True
 
     def create_session(self):
         """
@@ -138,6 +149,7 @@ class DePlugin(IVSPlg):
         # 4. 提交验证码 (/appointment-form)
         # 这一步是为了让服务器验证 Session,并返回包含 personalinfo 的页面
         self._submit_captcha(captcha_code)
+        self.session_create_time = time.time()
         self._log("Session created successfully.")
 
     def query(self) -> VSQueryResult:
@@ -190,18 +202,23 @@ class DePlugin(IVSPlg):
         if dates:
             res.success = True
             res.availability_status = AvailabilityStatus.Available
-            # Visametric 返回 DD-MM-YYYY, 标准化为 YYYY-MM-DD
-            res.earliest_date = to_yyyymmdd(dates[0], '%d-%m-%Y')
-            for d in dates:
-                da = VSQueryResult.DateAvailability()
-                da.date = to_yyyymmdd(d, '%d-%m-%Y')
-                da.times = [] 
-                time_slot = VSQueryResult.DateAvailability.TimeSlot(time="00:00", label="Available")
-                da.times.append(time_slot)
-                res.availability.append(da)
+
+            # Visametric 返回 DD-MM-YYYY → 标准化为 YYYY-MM-DD
+            res.earliest_date = to_yyyymmdd(dates[0], "%d-%m-%Y")
+
+            res.availability = [
+                DateAvailability(
+                    date=to_yyyymmdd(d, "%d-%m-%Y"),
+                    times=[],
+                )
+                for d in dates
+            ]
+
         else:
-            res.success = False # 获取成功,只是没号,所以 success 依然是 True
+            # 查询成功,但没有可用日期
+            res.success = False
             res.availability_status = AvailabilityStatus.NoneAvailable
+
         return res
 
     def book(self, slot_info: VSQueryResult, user_inputs: Dict) -> VSBookResult:

+ 100 - 66
plugins/tls_plugin.py

@@ -6,12 +6,13 @@ import os
 from datetime import datetime
 from typing import List, Dict, Optional, Any, Callable
 from urllib.parse import urljoin, urlparse
+from requests_toolbelt import MultipartEncoder
 
 from curl_cffi import requests, const
 from bs4 import BeautifulSoup
 
 from vs_plg import IVSPlg
-from vs_types import VSPlgConfig, VSQueryResult, VSBookResult, AvailabilityStatus, NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError 
+from vs_types import VSPlgConfig, VSQueryResult, VSBookResult, AvailabilityStatus, TimeSlot, DateAvailability, NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError 
 from toolkit.vs_cloud_api import VSCloudApi
 
 class TlsPlugin(IVSPlg):
@@ -29,7 +30,8 @@ class TlsPlugin(IVSPlg):
         # 会话相关
         self.session: Optional[requests.Session] = None
         self.travel_group: Optional[Dict] = None
-        self.user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
+        self.user_agent: str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
+        self.session_create_time: float = 0
 
     def get_group_id(self) -> str:
         return self.group_id
@@ -42,7 +44,17 @@ class TlsPlugin(IVSPlg):
         self.free_config = config.free_config or {}
 
     def health_check(self) -> bool:
-        return self.is_healthy
+        if not self.is_healthy:
+            return False
+        if self.session is None:
+            return False
+        if self.config.session_max_life > 0:
+            current_time = time.time()
+            elapsed_time = current_time - self.session_create_time
+            if elapsed_time > self.config.session_max_life * 60:
+                self._log(f"Session Life ({int(elapsed_time)}s) out of max life limit ({self.config.session_max_life * 60}s), mark as unhealth session")
+                return False
+        return True
 
     def create_session(self):
         """
@@ -134,7 +146,8 @@ class TlsPlugin(IVSPlg):
         
         if not self.travel_group:
             raise NotFoundError(message=f"No matched group found for city {target_city}")
-        self._log(f"Session created. Group: {self.travel_group['group_number']}")
+        self.session_create_time = time.time()
+        self._log(f"Session created successfully. Group: {self.travel_group['group_number']}")
 
     def query(self) -> VSQueryResult:
         res = VSQueryResult()
@@ -188,21 +201,25 @@ class TlsPlugin(IVSPlg):
         if available:
             res.success = True
             res.availability_status = AvailabilityStatus.Available
-            res.earliest_date = available[0]['date']
-            date_map = {}
+            res.earliest_date = available[0]["date"]
+
+            date_map: dict[str, list[TimeSlot]] = {}
+
             for s in available:
-                d = s['date']
-                date_map.setdefault(d, [])
-                ts = VSQueryResult.DateAvailability.TimeSlot()
-                ts.time = s['time']
-                ts.label = f"{s['type']}"
-                date_map[d].append(ts)
-
-            for d, slots in date_map.items():
-                da = VSQueryResult.DateAvailability()
-                da.date = d
-                da.times = slots
-                res.availability.append(da)
+                d = s["date"]
+
+                date_map.setdefault(d, []).append(
+                    TimeSlot(
+                        time=s["time"],
+                        label=str(s.get("label", "")),
+                    )
+                )
+
+            res.availability = [
+                DateAvailability(date=d, times=slots)
+                for d, slots in date_map.items()
+            ]
+
         else:
             res.success = False
             res.availability_status = AvailabilityStatus.NoneAvailable
@@ -212,82 +229,99 @@ class TlsPlugin(IVSPlg):
     def book(self, slot_info: VSQueryResult, user_input: Dict = None) -> VSBookResult:
         res = VSBookResult()
         res.success = False
-        target_date = slot_info.availability[0].date
-        target_time = slot_info.availability[0].times[0].time
-        target_label = "" 
         
+        # 1. 基础信息提取
         embassy = self.free_config.get('center', {})
         group_num = self.travel_group['group_number']
-        interest_month = self.free_config.get("interest_month", time.strftime("%m-%Y"))
         
-        # 1. 解决 ReCaptcha V3
-        page_url = f'https://visas-fr.tlscontact.com/en-us/{group_num}/workflow/appointment-booking?location={embassy["code"]}&month={interest_month}'
-        api_token = self.free_config.get("capsolver_key", "")
+        target_slot = slot_info.availability[0]
+        target_date = target_slot.date
+        target_time = target_slot.times[0].time
         
+        # [关键修正] Label 处理
+        # 根据你的 dump,如果是 Prime Time,这里是 "pta"。
+        # 如果是普通号,通常是 "regular" 或者空字符串。
+        # 我们优先取 slot_info 里的 label,如果没有则默认为空
+        raw_labels = getattr(target_slot.times[0], 'labels', [])
+        if isinstance(raw_labels, list) and len(raw_labels) > 0:
+            # 取第一个标签,例如 "pta" 或 "regular"
+            target_label = raw_labels[0] 
+        else:
+            target_label = "" # 或者试试 "regular"
+
+        # 2. 解决 ReCaptcha V3
+        # 动作必须是 "book"
+        page_url = f'https://visas-fr.tlscontact.com/en-us/{group_num}/workflow/appointment-booking?location={embassy["code"]}&month={target_date[:7]}'
+        
+        api_token = self.free_config.get("capsolver_key", "")
         rc_params = {
             "type": "ReCaptchaV3Task",
             "page": page_url,
-            "action": "book",
+            "action": "book", 
             "siteKey": "6LcTpXcfAAAAAM3VojNhyV-F1z92ADJIvcSZ39Y9",
             "apiToken": api_token,
             "proxy": self._get_proxy_url()
         }
         g_token = self._solve_recaptcha(rc_params)
 
-        # 2. 构造请求
-        url = f'https://visas-fr.tlscontact.com/en-us/{group_num}/workflow/appointment-booking'
+        # 3. 构造 Payload (严格对齐你的 Curl Dump)
+        # Next.js Server Action ID (从你的 header 确认)
+        ACTION_ID = "60d0616946df1fc4e7c094ca6a7a04f134d0be3d53"
         
-        next_action = '601f284bf7ee33b6578ad0fad426fae18c232707f2'
-        next_state = '%5B%22%22%2C%7B%22children%22%3A%5B%5B%22lang%22%2C%22en-us%22%2C%22d%22%5D%2C%7B%22children%22%3A%5B%5B%22groupId%22%2C%22$GROUPID$%22%2C%22d%22%5D%2C%7B%22children%22%3A%5B%22workflow%22%2C%7B%22children%22%3A%5B%22appointment-booking%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%2Ctrue%5D%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%2Ctrue%5D%7D%2Cnull%2Cnull%5D'
-        
-        headers = {
-            'Next-Action': next_action,
-            'Referer': page_url,
-            'Next-Router-State-Tree': next_state.replace("$GROUPID$", group_num),
-            'Accept': 'text/x-component',
-            'User-Agent': self.user_agent,
-        }
-        params = {
-            'location': embassy["code"],
-            'month': interest_month,
-        }
-        
-        boundary = "----WebKitFormBoundary" + "".join(
-            random.choices("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", k=16)
-        )
-        headers["Content-Type"] = f"multipart/form-data; boundary={boundary}"
-        form_fields = {
-            '1_formGroupId': str(group_num),
+        fields = {
+            '1_formGroupId': str(group_num),      # 修正:加了 form 前缀
             '1_lang': 'en-us',
             '1_process': 'APPOINTMENT',
-            '1_location': embassy["code"],
+            '1_location': embassy["code"],        # 例如 gbLON2fr
             '1_date': target_date,
             '1_time': target_time,
-            '1_appointmentLabel': target_label,
-            '1_captcha_token': g_token,
-            '0': '[{"status":"IDLE"},"$K1"]'
+            '1_appointmentLabel': target_label,   # 修正:单数 Label,值为字符串 "pta" 或 "regular"
+            '1_captcha_token': g_token,           # 修正:下划线格式
+            '0': '[{"status":"IDLE"},"$K1"]'      # 对应 Next.js Action 的状态位
         }
         
-        body_parts = []
-        for name, value in form_fields.items():
-            body_parts.append(f"--{boundary}\r\n")
-            body_parts.append(f'Content-Disposition: form-data; name="{name}"\r\n')
-            body_parts.append("\r\n")
-            body_parts.append(f"{value}\r\n")
-        body_parts.append(f"--{boundary}--\r\n")
-        body = "".join(body_parts).encode("utf-8")
+        m = MultipartEncoder(fields=fields)
+
+        # 4. 发送请求
+        url = f'https://visas-fr.tlscontact.com/en-us/{group_num}/workflow/appointment-booking'
         
-        resp = self.session.post(url, params=params, headers=headers, data=body, allow_redirects=False)
+        headers = {
+            'Next-Action': ACTION_ID,
+            'Referer': page_url,
+            'Origin': 'https://visas-fr.tlscontact.com',
+            'Accept': 'text/x-component',
+            'User-Agent': self.user_agent, # 确保和 curl_cffi 的 impersonate 一致
+            'Content-Type': m.content_type,
+            # 使用你 dump 里的 State Tree,虽然长,但最稳妥
+            'Next-Router-State-Tree': '%5B%22%22%2C%7B%22children%22%3A%5B%5B%22lang%22%2C%22en-us%22%2C%22d%22%5D%2C%7B%22children%22%3A%5B%5B%22groupId%22%2C%22'+str(group_num)+'%22%2C%22d%22%5D%2C%7B%22children%22%3A%5B%22workflow%22%2C%7B%22children%22%3A%5B%22appointment-booking%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%2Ctrue%5D%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%2Ctrue%5D%7D%2Cnull%2Cnull%5D'
+        }
+
+        # 必须使用 curl_cffi 模拟浏览器指纹
+        resp = self.session.post(url, data=m.to_string(), headers=headers, allow_redirects=False)
+
         if self.config.debug:
-            self._save_debug_html(resp.text, prefix='Tls_Book_Appointment_Page')
-        if resp.status_code == 303: 
+            self._save_debug_html(resp.text, prefix='Tls_Book_Result')
+
+        # 5. 结果判定
+        if resp.status_code == 303:
+            location = resp.headers.get('Location', '')
+            self._log(f"Booking Success! Redirecting to: {location}")
             res.success = True
             res.book_date = target_date
             res.book_time = target_time
             return res
+            
+        elif resp.status_code == 200:
+            # Next.js 有时会在 200 中返回业务错误
+            if "APPOINTMENT_LIMIT_REACHED" in resp.text:
+                self._log("Failed: 限制/无号")
+            elif "Invalid captcha" in resp.text:
+                self._log("Failed: 验证码错误")
+            else:
+                self._log(f"Booking Failed (200 OK but error content): {resp.text[:200]}")
         else:
-            self._log(f'Expected Status is 303, but got {resp.status_code}')
-            res.success = False            
+            self._log(f'Booking Failed. Status: {resp.status_code}')
+
         return res
 
     def _log(self, message):

+ 42 - 20
plugins/vfs_plugin.py

@@ -15,7 +15,7 @@ from cryptography.hazmat.primitives.asymmetric import padding
 from cryptography.hazmat.backends import default_backend
 
 from vs_plg import IVSPlg 
-from vs_types import VSPlgConfig, VSQueryResult, VSBookResult, AvailabilityStatus, NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError 
+from vs_types import VSPlgConfig, VSQueryResult, VSBookResult, DateAvailability, AvailabilityStatus, NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError 
 from toolkit.vs_cloud_api import VSCloudApi 
 
 # ----------------- 静态常量与辅助数据 -----------------
@@ -64,20 +64,21 @@ class VfsPlugin(IVSPlg):
         
         self.session: Optional[requests.Session] = None
         
-        self.jwt_token = ""
-        self.user_agent = ""
-        self.real_ip = ""
-        self.is_healthy = True
+        self.jwt_token: str = ""
+        self.user_agent: str = ""
+        self.real_ip: str = ""
+        self.is_healthy: bool = True
         # 缓存配置
         self.center_conf = None
-        self.category_conf = {}
-        self.subcategory_conf = {}
+        self.category_conf: Dict = {}
+        self.subcategory_conf: Dict = {}
         
         # 加载公钥
         self.public_key = serialization.load_pem_public_key(
             VFS_PUBLIC_KEY_PEM.encode(),
             backend=default_backend()
         )
+        self.session_create_time: float = 0
 
     def get_group_id(self) -> str:
         return self.group_id
@@ -90,7 +91,17 @@ class VfsPlugin(IVSPlg):
         self.logger = logger
 
     def health_check(self) -> bool:
-        return self.is_healthy
+        if not self.is_healthy:
+            return False
+        if self.session is None:
+            return False
+        if self.config.session_max_life > 0:
+            current_time = time.time()
+            elapsed_time = current_time - self.session_create_time
+            if elapsed_time > self.config.session_max_life * 60:
+                self._log(f"Session Life ({int(elapsed_time)}s) out of max life limit ({self.config.session_max_life * 60}s), mark as unhealth session")
+                return False
+        return True
 
     def create_session(self) -> None:
         # 初始化 Session
@@ -143,21 +154,28 @@ class VfsPlugin(IVSPlg):
             "captcha_api_key": cf_token
         }
         
-        # 3. 发送登录请求 (包含 OPTIONS)
+        # 3. 发送登录请求
         resp = self._perform_request("POST", url, headers=headers, data=data)
         resp_json = resp.json()
-        if resp_json.get('accessToken', ''):
+
+        # 分支 1: 登录直接成功,获取到 Token
+        if resp_json.get('accessToken'):
             self.jwt_token = resp_json["accessToken"]
             self._log("Login successful, JWT obtained.")
-            return
         
-        # OTP 处理
-        if resp_json.get("enableOTPAuthentication"):
+        # 分支 2: 需要 OTP 验证
+        elif resp_json.get("enableOTPAuthentication"):
             self._log("Login requires OTP.")
             otp = self._read_otp_email()
+            # 提交 OTP,如果失败该函数内部应抛出异常
             self._submit_login_otp(None, otp)
-            return
-        raise BizLogicError(message="Login failed: No access token or OTP flow.")
+        
+        # 分支 3: 异常情况(既无 Token 也无 OTP)
+        else:
+            # 在分支内部抛出异常,包含响应内容方便调试
+            raise BizLogicError(message=f"Login failed: No access token or OTP flow. Response: {resp_json}")
+        self.session_create_time = time.time()
+        self._log("Session created successfully.")
 
     def query(self) -> VSQueryResult:
         """查询可预约 Slot"""
@@ -175,16 +193,20 @@ class VfsPlugin(IVSPlg):
         result.country = apt_config.get("country", "")
         result.routing_key = apt_config.get("routing_key", "")
         if earliest_date:
+            result.success = True
+
             if "WaitList" in earliest_date:
-                result.success = True
                 result.availability_status = AvailabilityStatus.Waitlist
             else:
-                result.success = True
                 result.availability_status = AvailabilityStatus.Available
                 result.earliest_date = earliest_date
-                day_info = VSQueryResult.DateAvailability()
-                day_info.date = earliest_date
-                result.availability.append(day_info)
+
+                result.availability = [
+                    DateAvailability(
+                        date=earliest_date,
+                        times=[],
+                    )
+                ]
         return result
 
     def book(self, slot_info: VSQueryResult, user_inputs) -> VSBookResult:

+ 19 - 8
toolkit/account_manager.py

@@ -69,9 +69,13 @@ class AccountManager:
         except Exception as e:
             VSC_ERROR("acc_mgr", f"Failed to load config: {e}")
 
-    def get_next_account(self, pool_name: str) -> Optional[Dict[str, Any]]:
+    def next(self, pool_name: str, lock_duration: float = 60.0) -> Optional[Dict[str, Any]]:
         """
-        获取下一个可用账号 (随机策略)
+        获取下一个可用账号 (随机策略),并自动锁定指定时长。
+        
+        @param pool_name: 账号池名称
+        @param lock_duration: 锁定时间(秒),默认60秒
+        @return: 账号信息字典 或 None
         """
         with self._account_lock:
             accounts = self._accounts.get(pool_name, [])
@@ -80,7 +84,7 @@ class AccountManager:
                 return None
 
             now = time.time()
-            # 筛选未锁定的账号
+            # 筛选未锁定的账号 (lock_until 必须小于等于当前时间)
             available = [acc for acc in accounts if acc.get("lock_until", 0) <= now]
             
             if not available:
@@ -90,11 +94,18 @@ class AccountManager:
             # 随机选择
             selected = random.choice(available)
             
-            VSC_DEBUG("acc_mgr", "Selected account %s (id=%s) from pool %s", 
-                      selected.get("username"), selected.get("id"), pool_name)
-            return selected
+            # === 关键修改:立即更新锁定时间 ===
+            # 直接修改引用的对象,确保其他线程或其他逻辑能感知到该账号已被占用
+            selected["lock_until"] = now + lock_duration
+            
+            VSC_DEBUG("acc_mgr", "Selected account %s (id=%s) from pool %s, locked for %.1fs", 
+                      selected.get("username"), selected.get("id"), pool_name, lock_duration)
+            
+            # 返回账号数据的浅拷贝,防止调用者无意修改 Manager 内部的其他字段
+            # (虽然 Python dict 是引用传递,但 copy 一下是个好习惯,除非你需要引用修改)
+            return selected.copy()
 
-    def lock_account(self, pool_name: str, account_id: int, duration_seconds: int):
+    def lock(self, pool_name: str, account_id: int, duration_seconds: int):
         """
         锁定账号一段时间
         """
@@ -108,7 +119,7 @@ class AccountManager:
                     return
             VSC_WARN("acc_mgr", "Account %d not found in pool %s to lock", account_id, pool_name)
 
-    def remove_account(self, pool_name: str, account_id: int, reason: str = "success", extra_data: dict = None):
+    def remove(self, pool_name: str, account_id: int, reason: str = "success", extra_data: dict = None):
         """
         从内存中移除账号 (预订成功后不再使用)
         注意:这不会修改磁盘上的 accounts.json 文件,重启程序后账号会恢复

+ 0 - 119
toolkit/binding_manager.py

@@ -1,119 +0,0 @@
-import threading
-import json
-import os
-from typing import List, Optional, Tuple, Dict, Any
-from vs_log_macros import VSC_DEBUG, VSC_INFO, VSC_WARN, VSC_ERROR
-
-class BindingManager:
-    """
-    绑定管理器 (支持从配置文件加载静态绑定 + 运行时动态绑定)
-    读取 config/bindings.json
-    """
-    _instance = None
-    _lock = threading.RLock()
-
-    def __new__(cls):
-        with cls._lock:
-            if cls._instance is None:
-                cls._instance = super().__new__(cls)
-                cls._instance._init_data()
-            return cls._instance
-
-    @staticmethod
-    def Instance():
-        return BindingManager()
-
-    def _init_data(self):
-        # Key: (account_pool, account_id)
-        # Value: (proxy_pool, proxy_id, bind_type)
-        self._bindings: Dict[Tuple[str, int], Tuple[str, int, str]] = {}
-        self._binding_lock = threading.RLock()
-        self._config_path = "config/bindings.json"
-        
-        self.reload_config()
-
-    def reload_config(self):
-        """加载本地绑定配置 (通常用于静态绑定)"""
-        if not os.path.exists(self._config_path):
-            # 绑定文件不存在是正常的,可能全是动态绑定
-            VSC_DEBUG("binding_mgr", f"Config file not found: {self._config_path}. No static bindings loaded.")
-            return
-
-        try:
-            with open(self._config_path, 'r', encoding='utf-8') as f:
-                data = json.load(f)
-            
-            if not isinstance(data, list):
-                VSC_ERROR("binding_mgr", "Invalid JSON format: bindings must be a list")
-                return
-
-            count = 0
-            with self._binding_lock:
-                # 策略:不清除运行时生成的动态绑定,仅覆盖/添加配置文件中的静态绑定
-                # 如果需要重置,可以先 self._bindings.clear()
-                
-                for item in data:
-                    # 校验字段
-                    if not all(k in item for k in ("account_pool", "account_id", "proxy_pool", "proxy_id")):
-                        continue
-                    
-                    key = (item["account_pool"], item["account_id"])
-                    val = (item["proxy_pool"], item["proxy_id"], item.get("bind_type", "static"))
-                    
-                    self._bindings[key] = val
-                    count += 1
-                    
-            VSC_INFO("binding_mgr", f"Loaded {count} static bindings from {self._config_path}")
-            
-        except json.JSONDecodeError:
-            VSC_ERROR("binding_mgr", f"Invalid JSON format in {self._config_path}")
-        except Exception as e:
-            VSC_ERROR("binding_mgr", f"Failed to load bindings: {e}")
-
-    def get_bounded_proxy_id(self, account_pool: str, account_id: int) -> Optional[int]:
-        """获取给定账户绑定的代理ID"""
-        with self._binding_lock:
-            key = (account_pool, account_id)
-            binding = self._bindings.get(key)
-            if binding:
-                proxy_pool, proxy_id, b_type = binding
-                VSC_DEBUG("binding_mgr", "Found %s binding: Acc(%s:%d) -> Proxy(%s:%d)", 
-                          b_type, account_pool, account_id, proxy_pool, proxy_id)
-                return proxy_id
-            
-            VSC_DEBUG("binding_mgr", "No binding found for Acc(%s:%d)", account_pool, account_id)
-            return None
-    
-    def get_bounded_proxies_ids(self, account_pool: str, proxy_pool: str) -> List[int]:
-        """
-        获取所有在特定代理池中被账户绑定的代理ID列表。
-        用于 ProxyManager 过滤掉已被占用的代理。
-        """
-        with self._binding_lock:
-            bounded_ids = []
-            for (acc_p, _), (px_p, px_id, _) in self._bindings.items():
-                if acc_p == account_pool and px_p == proxy_pool:
-                    bounded_ids.append(px_id)
-            
-            # VSC_DEBUG("binding_mgr", "Bounded IDs in %s (for %s): %s", proxy_pool, account_pool, bounded_ids)
-            return bounded_ids
-
-    def create_binding(self, account_pool: str, account_id: int, 
-                       proxy_pool: str, proxy_id: int, bind_type: str):
-        """
-        创建绑定 (运行时调用,如动态绑定)
-        注意:运行时创建的绑定目前仅存在内存中,重启后丢失(除非写入文件,当前版本暂不实现回写)
-        """
-        with self._binding_lock:
-            key = (account_pool, account_id)
-            self._bindings[key] = (proxy_pool, proxy_id, bind_type)
-            VSC_INFO("binding_mgr", "Created binding: Acc(%s:%d) -> Proxy(%s:%d) [%s]",
-                     account_pool, account_id, proxy_pool, proxy_id, bind_type)
-
-    def remove_binding(self, account_pool: str, account_id: int):
-        """移除绑定"""
-        with self._binding_lock:
-            key = (account_pool, account_id)
-            if key in self._bindings:
-                del self._bindings[key]
-                VSC_INFO("binding_mgr", "Removed binding for Acc(%s:%d)", account_pool, account_id)

+ 3 - 3
toolkit/proxy_manager.py

@@ -70,7 +70,7 @@ class ProxyManager:
         except Exception as e:
             VSC_ERROR("proxy_mgr", f"Failed to load proxy config: {e}")
 
-    def get_next_proxy(self, pool_name: str) -> Optional[Dict[str, Any]]:
+    def next(self, pool_name: str) -> Optional[Dict[str, Any]]:
         """
         从指定池中获取下一个可用代理 (随机)
         """
@@ -92,7 +92,7 @@ class ProxyManager:
                       proxy["id"], proxy["ip"], pool_name)
             return proxy
 
-    def get_unbind_proxy(self, pool_name: str, bounded_ids: List[int]) -> Optional[Dict[str, Any]]:
+    def get_unbind(self, pool_name: str, bounded_ids: List[int]) -> Optional[Dict[str, Any]]:
         """
         获取一个未绑定(且未锁定)的代理。
         用于 GCO 的 IP 绑定逻辑。
@@ -119,7 +119,7 @@ class ProxyManager:
             VSC_DEBUG("proxy_mgr", "Selected unbound proxy ID %d from pool '%s'", proxy["id"], pool_name)
             return proxy
 
-    def lock_proxy(self, pool_name: str, proxy_id: int, duration_seconds: int):
+    def lock(self, pool_name: str, proxy_id: int, duration_seconds: int):
         """
         锁定指定代理一段时间 (例如请求过于频繁被 429)
         """

+ 160 - 140
vs_types.py

@@ -1,8 +1,9 @@
 # vs_types.py
 import json
-from dataclasses import dataclass, field
+from pydantic import BaseModel, Field
+from pydantic.generics import GenericModel
 from enum import Enum, auto
-from typing import List, Optional, Any, Dict
+from typing import Generic, TypeVar, Optional, List, Any, Dict
 
 
 class BizException(Exception):
@@ -51,201 +52,220 @@ class PermissionDeniedError(BizException):
 class BizLogicError(BizException):
     def __init__(self, message="Business logic error"):
         super().__init__(code=40001, message=message)
+        
+class AvailabilityStatus(str, Enum):
+    NoneAvailable = "NoAvailable"
+    Available = "Available"
+    Waitlist = "Waitlist"
 
-class QueryWaitMode(Enum):
-    Loop = 0
-    Fixed = 1
-    Random = 2
-
-class AvailabilityStatus(Enum):
-    NoneAvailable = 0
-    Available = 1
-    Waitlist = 2
+class QueryWaitMode(str, Enum):
+    Loop = "Loop"
+    Fixed = "Fixed"
+    Random = "Random"
 
 # --- Structs ---
 
-@dataclass
-class QueryWaitConfig:
+class QueryWaitConfig(BaseModel):
     mode: QueryWaitMode = QueryWaitMode.Loop
-    fixed_wait: int = 0  # 仅在 Fixed 模式下使用
-    random_min: int = 0  # 仅在 Random 模式下使用
-    random_max: int = 0  # 仅在 Random 模式下使用
-    
+    fixed_wait: int = 0
+    random_min: int = 0
+    random_max: int = 0
+
     @classmethod
-    def from_json(cls, data: Dict[str, Any]):
-        return cls(
-            mode=data.get("mode", 0),
-            fixed_wait=data.get("fixed_wait", 0),
-            random_min=data.get("random_min", 0),
-            random_max=data.get("random_max", 0)
-        )
+    def from_json(cls, data: Dict[str, Any]) -> "QueryWaitConfig":
+        return cls.model_validate(data)
 
     def to_json(self) -> Dict[str, Any]:
-        return {
-            "mode": self.mode,
-            "fixed_wait": self.fixed_wait,
-            "random_min": self.random_min,
-            "random_max": self.random_max
-        }
+        return self.model_dump()
+
+class PluginConfig(BaseModel):
+    lib_path: str = Field(default="plugins", description="插件目录")
+    plugin_name: str = Field(default="", description="插件注册名")
+    plugin_bin: str = Field(default="", description="插件文件名 / Python 模块")
+    plugin_proto: str = Field(default="IVSPlg", description="插件接口协议")
 
-@dataclass
-class PluginConfig:
-    lib_path: str = ""           # 插件目录
-    plugin_name: str = ""        # 插件注册名
-    plugin_bin: str = ""         # 动态库文件名/Python文件名
-    plugin_proto: str = ""       # 接口协议
-    
     @classmethod
-    def from_json(cls, data: Dict[str, Any]):
-        return cls(
-            lib_path=data.get("lib_path", "plugins"),
-            plugin_name=data.get("plugin_name", ""),
-            plugin_bin=data.get("plugin_bin", ""),
-            plugin_proto=data.get("plugin_proto", "IVSPlg")
-        )
+    def from_json(cls, data: Dict[str, Any]) -> "PluginConfig":
+        """
+        支持 dict / JSON decode 后的数据
+        """
+        return cls.model_validate(data)
 
     def to_json(self) -> Dict[str, Any]:
-        return {
-            "lib_path": self.lib_path,
-            "plugin_name": self.plugin_name,
-            "plugin_bin": self.plugin_bin,
-            "plugin_proto": self.plugin_proto
-        }
+        """
+        输出标准 JSON dict(Enum 自动转字符串)
+        """
+        return self.model_dump()
+
 
-@dataclass
-class GroupConfig:
+class GroupConfig(BaseModel):
     debug: bool = False
     enable: bool = False
     identifier: str = ""
+
     need_account: bool = False
-    account_built_in: bool = True
-    account_pool: str = ""
+    local_account_pool: str = ""
+
     need_proxy: bool = False
     proxy_pool: str = ""
     need_ip_bind: bool = False
+
     account_login_interval: int = 0
     target_instances: int = 1
     
-    query_wait: QueryWaitConfig = field(default_factory=QueryWaitConfig)
-    plugin_config: PluginConfig = field(default_factory=PluginConfig)
-    free_config: Dict[str, Any] = field(default_factory=dict)
+    order_account_routing: str = ""
+    order_account_online_limit: int = 0
     
+    input_map_username: str = "username"
+    input_map_password: str = "password"
+    
+    account_bind_applicant: bool = False
+    
+    session_max_life: int = 15
+    query_wait: QueryWaitConfig = Field(default_factory=QueryWaitConfig)
+    plugin_config: PluginConfig = Field(default_factory=PluginConfig)
+
+    free_config: Dict[str, Any] = Field(default_factory=dict)
+
     @classmethod
-    def from_json(cls, data: Dict[str, Any]):
-        return cls(
-            identifier=data.get("identifier", ""),
-            debug=data.get("debug", False),
-            enable=data.get("enable", False),
-            need_account=data.get("need_account", False),
-            account_built_in=data.get("account_built_in", True),
-            account_pool=data.get("account_pool", ""),
-            need_proxy=data.get("need_proxy", False),
-            proxy_pool=data.get("proxy_pool", ""),
-            need_ip_bind=data.get("need_ip_bind", False),
-            account_login_interval=data.get("account_login_interval", 30),
-            target_instances=data.get("target_instances", 1),
-            query_wait=QueryWaitConfig.from_json(data.get("query_wait", {})),
-            plugin_config=PluginConfig.from_json(data.get("plugin_config", {})),
-            free_config=data.get("free_config", {})
-        )
+    def from_json(cls, data: Dict[str, Any]) -> "GroupConfig":
+        return cls.model_validate(data)
 
     def to_json(self) -> Dict[str, Any]:
-        return {
-            "identifier": self.identifier,
-            "debug": self.debug,
-            "enable": self.enable,
-            "need_account": self.need_account,
-            "account_built_in": self.account_built_in,
-            "account_pool": self.account_pool,
-            "need_proxy": self.need_proxy,
-            "proxy_pool": self.proxy_pool,
-            "need_ip_bind": self.need_ip_bind,
-            "account_login_interval": self.account_login_interval,
-            "target_instances": self.target_instances,
-            "query_wait": self.query_wait.to_json(),
-            "plugin_config": self.plugin_config.to_json(),
-            "free_config": self.free_config
-        }
+        return self.model_dump()
 
     def to_json_str(self) -> str:
-        """直接输出 JSON 字符串"""
-        return json.dumps(self.to_json(), indent=2)
+        return json.dumps(self.model_dump(), indent=2, ensure_ascii=False)
+
+    # ---------------------------
+    # 🔥 强烈推荐:用于热更新 / diff
+    # ---------------------------
+
+    def normalized(self) -> Dict[str, Any]:
+        """
+        用于配置比较 / 热更新判断
+        - Enum -> value
+        - 去掉运行期无关字段(如果有)
+        """
+        return self.model_dump()
 
-@dataclass
-class VSAccount:
+
+class VSAccount(BaseModel):
     id: int = 0
     username: str = ""
     password: str = ""
-    lock_until: str = ""
+    lock_until: float = 0.0 
 
-@dataclass
-class VSProxy:
+class VSProxy(BaseModel):
     id: int = 0
     scheme: str = "http"
     ip: str = ""
     port: int = 0
     username: str = ""
     password: str = ""
-    lock_until: str = ""
+    lock_until: float = 0.0 
 
-@dataclass
-class VSPlgConfig:
+class VSPlgConfig(BaseModel):
     debug: bool = False
-    account: VSAccount = field(default_factory=VSAccount)
-    proxy: VSProxy = field(default_factory=VSProxy)
-    free_config: str = ""
-
-@dataclass
-class VSQueryResult:
-    class DateAvailability:
-        @dataclass
-        class TimeSlot:
-            # "hh:mm"
-            time: str = ""
-            label: str = ""
-        date: str = ""
-        times: List[TimeSlot] = field(default_factory=list)
-    
-    # 可用状态[可用, 等候列表, 不可用]
+    account: VSAccount = Field(default_factory=VSAccount)
+    proxy: VSProxy = Field(default_factory=VSProxy)
+    free_config: dict = Field(default_factory=dict)
+    session_max_life: int = 15
+
+class TimeSlot(BaseModel):
+    time: str = ""     # "hh:mm"
+    label: str = ""
+
+class DateAvailability(BaseModel):
+    date: str = ""
+    times: List[TimeSlot] = Field(default_factory=list)
+
+class VSQueryResult(BaseModel):
     availability_status: AvailabilityStatus = AvailabilityStatus.NoneAvailable
-    # 最早可用日期
     earliest_date: str = ""
-    # 业务routing_key
     routing_key: str = ""
-    # 签证类型
     visa_type: str = ""
-    # 递交城市
     city: str = ""
-    # 被申请国
     country: str = ""
-    # 所有有效的日期和时间
-    availability: List[DateAvailability] = field(default_factory=list)
-    # 查询到有Slot 为True, 无Slot 为False
-    success: bool = False 
-
-@dataclass
-class VSBookResult:
-    # 抢位成功 True, 抢位失败 False
+    availability: List[DateAvailability] = Field(default_factory=list)
+    success: bool = False
+
+class VSBookResult(BaseModel):
     success: bool = False
-    # 会话ID
     session_id: str = ""
-    # 预定成功的账号
     account: str = ""
-    # 预定的日期
     book_date: str = ""
-    # 预定的时间
     book_time: str = ""
-    # 费用 单位分
     fee_amount: int = 0
-    # 货币单位
     fee_currency: str = ""
-    # 支付链接
     payment_link: str = ""
 
 # --- 内部任务结构 ---
-@dataclass
-class Task:
-    instance: Any # IVSPlg 实例 (使用 Any 避免循环导入)
+class Task(BaseModel):
+    instance: Any              # IVSPlg 实例(运行时对象)
     qw_cfg: QueryWaitConfig
-    next_run: float = 0.0  # Unix timestamp
+    next_run: float = 0.0      # Unix timestamp
+    book_allowed: bool = True
+    task_ref: Optional[Dict[str, Any]] = None
+    class Config:
+        arbitrary_types_allowed = True
+        underscore_attrs_are_private = True
+
+# server 相关TYPES
+# === 数据模型 ===
+class GroupControl(BaseModel):
+    group_id: str
+
+class UpgradePluginRequest(BaseModel):
+    plugin_name: str
+    plugin_bin: str
+
+class UpdateConfigRequest(BaseModel):
+    group_id: str
+    new_config_str: str
+    
+class PluginStatusOut(BaseModel):
+    id: str
+    plugin: str
+    running: bool
+    instances: int
+    local_account_pool: str
+    proxies_pool: str
+    
+class PluginRestarted(BaseModel):
+    restarted: List[str]
+    
+T = TypeVar("T")
+
+class ApiResponse(BaseModel, Generic[T]):
+    code: int = 0
+    message: str = "success"
+    data: Optional[T] = None
+
+def _to_serializable(data: Any):
+    """自动将 ORM / Pydantic / list 转换为可序列化对象"""
+    if isinstance(data, BaseModel):
+        return data.dict()
+    if hasattr(data, "__table__"):  # SQLAlchemy ORM
+        return {
+            c.name: getattr(data, c.name)
+            for c in data.__table__.columns
+        }
+    if isinstance(data, list):
+        return [_to_serializable(i) for i in data]
+    return data
+
+def success(data=None, message: str = "success"):
+    return ApiResponse(
+        code=0,
+        message=message,
+        data=_to_serializable(data)
+    )
+
+def fail(message: str, code: int = 1):
+    return ApiResponse(
+        code=code,
+        message=message,
+        data=None
+    )
     

+ 98 - 91
web/server.py

@@ -1,127 +1,134 @@
 # web/server.py
-import os
 import asyncio
-from pathlib import Path
-from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
-from fastapi.staticfiles import StaticFiles
-from fastapi.responses import FileResponse
-from pydantic import BaseModel
+from typing import List, Any, Dict
+from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
+from fastapi.responses import JSONResponse
+
+from vs_types import (
+    success,
+    fail,
+    BizException,
+    ApiResponse,
+    GroupControl,
+    UpgradePluginRequest,
+    UpdateConfigRequest,
+    PluginStatusOut,
+    GroupConfig,
+    PluginRestarted
+)
 from core.app_manager import AppManager
 from vs_log_macros import VSC_INFO, VSC_ERROR
 
+
 app = FastAPI(title="Visa Plugin Manager")
 
-# === 1. 路径计算 (关键修改) ===
-# 获取 web/server.py 所在的绝对路径目录
-CURRENT_DIR = Path(__file__).resolve().parent
-# 拼接出 static 目录: .../web/static
-STATIC_DIR = CURRENT_DIR / "static"
-# 拼接出 index.html 路径
-INDEX_FILE = STATIC_DIR / "index.html"
-
-VSC_INFO("web", f"Static Directory configured at: {STATIC_DIR}")
-VSC_INFO("web", f"Index File expected at: {INDEX_FILE}")
-
-# 确保目录存在
-if not STATIC_DIR.exists():
-    VSC_INFO("web", "Static directory missing, creating...")
-    STATIC_DIR.mkdir(parents=True, exist_ok=True)
-
-# === 2. 挂载静态文件 ===
-# 挂载 /static 路径,用于访问 CSS/JS 等资源 (虽然本例是CDN,但保留以备用)
-app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
-
-# === 3. 根路径路由 ===
-@app.get("/")
-async def read_index():
-    # 显式检查文件是否存在,方便调试
-    if not INDEX_FILE.exists():
-        return {
-            "error": "Index file not found",
-            "expected_path": str(INDEX_FILE),
-            "tip": "Please ensure 'index.html' exists in the 'web/static' folder."
-        }
-    return FileResponse(str(INDEX_FILE))
-
-# === 数据模型 ===
-class GroupControl(BaseModel):
-    group_id: str
-
-class UpgradePluginRequest(BaseModel):
-    plugin_name: str
-    plugin_bin: str
-
-class UpdateConfigRequest(BaseModel):
-    group_id: str
-    new_config_str: str
-# === API 接口 ===
-
-@app.get("/status")
+
+# -----------------------
+# Exception Handlers
+# -----------------------
+@app.exception_handler(BizException)
+async def biz_exception_handler(request: Request, exc: BizException):
+    return JSONResponse(
+        status_code=exc.http_status,
+        content={
+            "code": exc.code,
+            "message": exc.message,
+            "data": exc.extra,
+        },
+    )
+
+
+@app.exception_handler(Exception)
+async def unhandled_exception_handler(request: Request, exc: Exception):
+    return JSONResponse(
+        status_code=500,
+        content={
+            "code": 50000,
+            "message": "Internal Server Error",
+            "data": None,
+        },
+    )
+
+@app.get("/status", response_model=ApiResponse[List[PluginStatusOut]])
 def get_status():
-    return {"data": AppManager.Instance().get_status()}
+    plugin_status = AppManager.Instance().get_status()
+    return success(data=plugin_status)
 
-@app.post("/start")
+@app.post("/start", response_model=ApiResponse)
 def start_group(payload: GroupControl):
-    if AppManager.Instance().start_group(payload.group_id):
-        return {"message": f"Group {payload.group_id} started"}
-    raise HTTPException(status_code=400, detail="Failed to start group")
+    AppManager.Instance().start_group(payload.group_id)
+    return success(message=f"Group {payload.group_id} started")
 
-@app.post("/stop")
+@app.post("/stop", response_model=ApiResponse)
 def stop_group(payload: GroupControl):
-    if AppManager.Instance().stop_group(payload.group_id):
-        return {"message": f"Group {payload.group_id} stopped"}
-    raise HTTPException(status_code=400, detail="Group not running or failed to stop")
+    AppManager.Instance().stop_group(payload.group_id)
+    return success(message=f"Group {payload.group_id} stopped")
 
-@app.post("/restart")
+@app.post("/restart", response_model=ApiResponse)
 def restart_group(payload: GroupControl):
-    if AppManager.Instance().restart_group(payload.group_id):
-        return {"message": f"Group {payload.group_id} restarted"}
-    raise HTTPException(status_code=400, detail="Failed to restart")
+    AppManager.Instance().restart_group(payload.group_id)
+    return success(message=f"Group {payload.group_id} restarted")
 
-@app.post("/group_config")
+@app.post("/group_config", response_model=ApiResponse[GroupConfig])
 def get_group_config(payload: GroupControl):
     config = AppManager.Instance().get_group_config(payload.group_id)
-    if config:
-        return {"message": f"Group {payload.group_id} restarted", "data": config}
-    raise HTTPException(status_code=400, detail="Failed to get group config")
+    return success(data=config)
 
-@app.post("/ota/upgrade_plugin")
-def ota_update(payload: UpgradePluginRequest):
-    try:
-        restarted = AppManager.Instance().ota_upgrade_plugin(payload.plugin_name)
-        return {
-            "message": f"Plugin {payload.plugin_name} reloaded",
-            "restarted_groups": restarted
-        }
-    except Exception as e:
-        raise HTTPException(status_code=500, detail=str(e))
+@app.post("/ota/upgrade_plugin", response_model=ApiResponse[PluginRestarted])
+def ota_upgrade(payload: UpgradePluginRequest):
+    restarted = AppManager.Instance().ota_upgrade_plugin(payload.plugin_name)
+    return success(message=f"Plugin {payload.plugin_name} reloaded", data=restarted)
     
 @app.post("/ota/update_config")
 def ota_update(payload: UpdateConfigRequest):
-    try:
-        AppManager.Instance().ota_update_plugin_config(payload.group_id, payload.new_config_str)
-        return {
-            "message": f"Plugin {payload.group_id} config updated"
-        }
-    except Exception as e:
-        raise HTTPException(status_code=500, detail=str(e))
+    AppManager.Instance().ota_update_plugin_config(payload.group_id, payload.new_config_str)
+    return success(message=f"Plugin {payload.group_id} config updated")
     
 @app.websocket("/ws/logs/{group_id}")
-async def websocket_logs(ws: WebSocket, group_id: str):
+async def websocket_logs_batch(ws: WebSocket, group_id: str):
     await ws.accept()
+    queue = asyncio.Queue(maxsize=5000)
+    loop = asyncio.get_running_loop()
 
-    queue = asyncio.Queue()
-    loop = asyncio.get_running_loop()   # ⭐ 只在这里拿
+    def _put_msg_in_loop(msg: str):
+        try:
+            queue.put_nowait(msg)
+        except asyncio.QueueFull:
+            pass
 
     def log_callback(msg: str):
-        loop.call_soon_threadsafe(queue.put_nowait, msg)
+        loop.call_soon_threadsafe(_put_msg_in_loop, msg)
 
     AppManager.Instance().subscribe_executor_logs(group_id, log_callback)
 
     try:
+        buffer = []
         while True:
+            # 阻塞等待第一条消息
             msg = await queue.get()
-            await ws.send_text(msg)
+            buffer.append(msg)
+
+            # 尝试非阻塞获取队列中剩余的消息(最多取 50 条,避免包太大)
+            # 这样可以将瞬间涌入的日志合并成一个包发送
+            for _ in range(50):
+                if queue.empty():
+                    break
+                try:
+                    buffer.append(queue.get_nowait())
+                except asyncio.QueueEmpty:
+                    break
+            
+            # 合并发送 (根据前端需求,可以用换行符拼接,或者发送 JSON 数组)
+            if buffer:
+                await ws.send_text("\n".join(buffer))
+                buffer.clear()
+    except WebSocketDisconnect:
+        # 这是 FastAPI/Starlette 封装的标准异常
+        # 当客户端断开连接时会抛出这个
+        print(f"Client {group_id} disconnected")
+    except Exception as e:
+        # 处理其他未知的系统错误
+        print(f"Error: {e}")
     finally:
         AppManager.Instance().unsubscribe_executor_logs(group_id, log_callback)
 

+ 0 - 205
web/static/index.html

@@ -1,205 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Visa Plugin Manager</title>
-    <!-- 引入 Tailwind CSS -->
-    <script src="https://cdn.tailwindcss.com"></script>
-    <!-- 引入 Vue 3 -->
-    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
-    <!-- 引入 Axios -->
-    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
-    <style>
-        [v-cloak] { display: none; }
-    </style>
-</head>
-<body class="bg-gray-100 min-h-screen font-sans">
-    <div id="app" v-cloak class="container mx-auto px-4 py-8">
-        
-        <!-- 头部 -->
-        <header class="flex justify-between items-center mb-8 bg-white p-6 rounded-lg shadow-md">
-            <div>
-                <h1 class="text-2xl font-bold text-gray-800">Visa Plugin Manager</h1>
-                <p class="text-gray-500 text-sm mt-1">Status Monitor & Control Panel</p>
-            </div>
-            <div class="flex gap-3">
-                <button @click="reloadConfig" :disabled="loading" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded shadow transition flex items-center">
-                    <span v-if="loading">...</span>
-                    <span v-else>Reload Config</span>
-                </button>
-                <button @click="fetchStatus" :disabled="loading" class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded shadow transition">
-                    Refresh Status
-                </button>
-            </div>
-        </header>
-
-        <!-- OTA 面板 -->
-        <div class="mb-8 bg-white p-6 rounded-lg shadow-md">
-            <h2 class="text-lg font-semibold mb-4 text-gray-700 border-b pb-2">OTA Plugin Update</h2>
-            <div class="flex gap-4 items-end">
-                <div class="flex-1">
-                    <label class="block text-sm font-medium text-gray-700 mb-1">Plugin Name</label>
-                    <input v-model="otaPluginName" type="text" placeholder="e.g. bls_plugin" class="w-full border-gray-300 border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500">
-                    <p class="text-xs text-gray-500 mt-1">Make sure you have replaced the .py file in plugins/ directory first.</p>
-                </div>
-                <button @click="triggerOTA" :disabled="!otaPluginName || loading" class="bg-purple-600 hover:bg-purple-700 text-white px-6 py-2 rounded shadow transition mb-[2px]">
-                    Hot Reload Plugin
-                </button>
-            </div>
-        </div>
-
-        <!-- 任务组列表 -->
-        <div class="bg-white rounded-lg shadow-md overflow-hidden">
-            <div class="p-6 border-b border-gray-200 flex justify-between items-center">
-                <h2 class="text-lg font-semibold text-gray-700">Task Groups</h2>
-                <span class="text-sm bg-gray-100 text-gray-600 px-3 py-1 rounded-full">{{ groups.length }} Groups</span>
-            </div>
-            
-            <div class="overflow-x-auto">
-                <table class="w-full text-left border-collapse">
-                    <thead>
-                        <tr class="bg-gray-50 text-gray-600 text-sm uppercase tracking-wider">
-                            <th class="px-6 py-4 font-medium">Group ID</th>
-                            <th class="px-6 py-4 font-medium">Plugin</th>
-                            <th class="px-6 py-4 font-medium">Pool</th>
-                            <th class="px-6 py-4 font-medium text-center">Instances</th>
-                            <th class="px-6 py-4 font-medium text-center">Status</th>
-                            <th class="px-6 py-4 font-medium text-right">Actions</th>
-                        </tr>
-                    </thead>
-                    <tbody class="divide-y divide-gray-200">
-                        <tr v-for="g in groups" :key="g.id" class="hover:bg-gray-50 transition">
-                            <td class="px-6 py-4 font-medium text-gray-900">{{ g.id }}</td>
-                            <td class="px-6 py-4 text-gray-600 font-mono text-sm">{{ g.plugin }}</td>
-                            <td class="px-6 py-4 text-gray-600">{{ g.account_pool }}</td>
-                            <td class="px-6 py-4 text-center">
-                                <span class="inline-block px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-xs font-bold">
-                                    {{ g.instances }}
-                                </span>
-                            </td>
-                            <td class="px-6 py-4 text-center">
-                                <span v-if="g.running" class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
-                                    <span class="w-2 h-2 mr-1 bg-green-500 rounded-full"></span>
-                                    Running
-                                </span>
-                                <span v-else class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
-                                    <span class="w-2 h-2 mr-1 bg-red-500 rounded-full"></span>
-                                    Stopped
-                                </span>
-                            </td>
-                            <td class="px-6 py-4 text-right space-x-2">
-                                <button v-if="!g.running" @click="controlGroup(g.id, 'start')" class="text-green-600 hover:text-green-900 font-medium text-sm hover:underline">Start</button>
-                                <button v-if="g.running" @click="controlGroup(g.id, 'restart')" class="text-orange-600 hover:text-orange-900 font-medium text-sm hover:underline">Restart</button>
-                                <button v-if="g.running" @click="controlGroup(g.id, 'stop')" class="text-red-600 hover:text-red-900 font-medium text-sm hover:underline">Stop</button>
-                            </td>
-                        </tr>
-                        <tr v-if="groups.length === 0">
-                            <td colspan="6" class="px-6 py-8 text-center text-gray-500">No groups loaded. Check config/groups.json</td>
-                        </tr>
-                    </tbody>
-                </table>
-            </div>
-        </div>
-    </div>
-
-    <script>
-        const { createApp, ref, onMounted } = Vue;
-
-        createApp({
-            setup() {
-                const groups = ref([]);
-                const loading = ref(false);
-                const otaPluginName = ref("");
-
-                // 获取状态
-                const fetchStatus = async () => {
-                    try {
-                        loading.value = true;
-                        const res = await axios.get('/status');
-                        groups.value = res.data.data;
-                    } catch (err) {
-                        alert("Failed to fetch status: " + err.message);
-                    } finally {
-                        loading.value = false;
-                    }
-                };
-
-                // 统一的控制函数
-                const controlGroup = async (groupId, action) => {
-                    try {
-                        loading.value = true;
-                        // action: 'start', 'stop', 'restart'
-                        const res = await axios.post(`/${action}`, { group_id: groupId });
-                        // 操作后稍微等待一下再刷新,让后台状态变化
-                        setTimeout(() => {
-                            fetchStatus();
-                            alert(`${action.toUpperCase()} command sent.`);
-                        }, 500);
-                    } catch (err) {
-                        const msg = err.response?.data?.detail || err.message;
-                        alert(`Failed to ${action}: ${msg}`);
-                        loading.value = false;
-                    }
-                };
-
-                // 重载配置
-                const reloadConfig = async () => {
-                    if (!confirm("Are you sure to reload config? This won't stop running groups.")) return;
-                    try {
-                        loading.value = true;
-                        await axios.post('/reload_config');
-                        await fetchStatus();
-                        alert("Configuration reloaded.");
-                    } catch (err) {
-                        alert("Error: " + err.message);
-                    } finally {
-                        loading.value = false;
-                    }
-                };
-
-                // OTA 更新
-                const triggerOTA = async () => {
-                    if (!otaPluginName.value) return;
-                    if (!confirm(`Confirm hot reload for plugin '${otaPluginName.value}'? This will restart related groups.`)) return;
-                    
-                    try {
-                        loading.value = true;
-                        const res = await axios.post('/ota', { plugin_name: otaPluginName.value });
-                        
-                        let msg = res.data.message;
-                        if (res.data.restarted_groups.length > 0) {
-                            msg += `\nRestarted groups: ${res.data.restarted_groups.join(', ')}`;
-                        } else {
-                            msg += "\nNo active groups were using this plugin.";
-                        }
-                        
-                        await fetchStatus();
-                        alert(msg);
-                    } catch (err) {
-                        alert("OTA Error: " + err.message);
-                    } finally {
-                        loading.value = false;
-                    }
-                };
-
-                onMounted(() => {
-                    fetchStatus();
-                    // 自动刷新 (可选,每5秒)
-                    setInterval(fetchStatus, 5000);
-                });
-
-                return {
-                    groups,
-                    loading,
-                    otaPluginName,
-                    fetchStatus,
-                    controlGroup,
-                    reloadConfig,
-                    triggerOTA
-                };
-            }
-        }).mount('#app');
-    </script>
-</body>
-</html>