feat: soc-client timeout via toolConfig, 24 unit tests, fix abort early-check and placeholder regex
This commit is contained in:
@@ -13,7 +13,9 @@
|
|||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format:check": "prettier --check ."
|
"format:check": "prettier --check .",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@assistant-ui/react": "^0.8.0",
|
"@assistant-ui/react": "^0.8.0",
|
||||||
@@ -85,7 +87,8 @@
|
|||||||
"tailwindcss": "^4.0.6",
|
"tailwindcss": "^4.0.6",
|
||||||
"typescript": "~5.7.2",
|
"typescript": "~5.7.2",
|
||||||
"typescript-eslint": "^8.22.0",
|
"typescript-eslint": "^8.22.0",
|
||||||
"vite": "^6.1.0"
|
"vite": "^6.1.0",
|
||||||
|
"vitest": "^3.1.1"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"react-is": "^19.0.0-rc-69d4b800-20241021"
|
"react-is": "^19.0.0-rc-69d4b800-20241021"
|
||||||
|
|||||||
Generated
+371
@@ -213,6 +213,9 @@ importers:
|
|||||||
vite:
|
vite:
|
||||||
specifier: ^6.1.0
|
specifier: ^6.1.0
|
||||||
version: 6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0)
|
version: 6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0)
|
||||||
|
vitest:
|
||||||
|
specifier: ^3.1.1
|
||||||
|
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -685,6 +688,9 @@ packages:
|
|||||||
'@jridgewell/sourcemap-codec@1.5.0':
|
'@jridgewell/sourcemap-codec@1.5.0':
|
||||||
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.5':
|
||||||
|
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||||
|
|
||||||
'@jridgewell/trace-mapping@0.3.25':
|
'@jridgewell/trace-mapping@0.3.25':
|
||||||
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
||||||
|
|
||||||
@@ -1320,6 +1326,9 @@ packages:
|
|||||||
'@types/babel__traverse@7.20.6':
|
'@types/babel__traverse@7.20.6':
|
||||||
resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
|
resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
|
||||||
|
|
||||||
|
'@types/chai@5.2.3':
|
||||||
|
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
|
||||||
|
|
||||||
'@types/d3-array@3.2.1':
|
'@types/d3-array@3.2.1':
|
||||||
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
|
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
|
||||||
|
|
||||||
@@ -1350,6 +1359,9 @@ packages:
|
|||||||
'@types/debug@4.1.12':
|
'@types/debug@4.1.12':
|
||||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||||
|
|
||||||
|
'@types/deep-eql@4.0.2':
|
||||||
|
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||||
|
|
||||||
'@types/estree-jsx@1.0.5':
|
'@types/estree-jsx@1.0.5':
|
||||||
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
|
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
|
||||||
|
|
||||||
@@ -1483,6 +1495,35 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^4.2.0 || ^5.0.0 || ^6.0.0
|
vite: ^4.2.0 || ^5.0.0 || ^6.0.0
|
||||||
|
|
||||||
|
'@vitest/expect@3.2.4':
|
||||||
|
resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
|
||||||
|
|
||||||
|
'@vitest/mocker@3.2.4':
|
||||||
|
resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==}
|
||||||
|
peerDependencies:
|
||||||
|
msw: ^2.4.9
|
||||||
|
vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
msw:
|
||||||
|
optional: true
|
||||||
|
vite:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@vitest/pretty-format@3.2.4':
|
||||||
|
resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==}
|
||||||
|
|
||||||
|
'@vitest/runner@3.2.4':
|
||||||
|
resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==}
|
||||||
|
|
||||||
|
'@vitest/snapshot@3.2.4':
|
||||||
|
resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==}
|
||||||
|
|
||||||
|
'@vitest/spy@3.2.4':
|
||||||
|
resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==}
|
||||||
|
|
||||||
|
'@vitest/utils@3.2.4':
|
||||||
|
resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
|
||||||
|
|
||||||
abort-controller@3.0.0:
|
abort-controller@3.0.0:
|
||||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||||
engines: {node: '>=6.5'}
|
engines: {node: '>=6.5'}
|
||||||
@@ -1539,6 +1580,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
|
resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
assertion-error@2.0.1:
|
||||||
|
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
assistant-stream@0.0.21:
|
assistant-stream@0.0.21:
|
||||||
resolution: {integrity: sha512-tQuGIuTGtmNnHSc6hxANVjO23LPmwFBjZnOt0xOhoBQBqW3vBv6DK2QOObgT9wnfLY8i2DRDwDbw8fSvYVzdMA==}
|
resolution: {integrity: sha512-tQuGIuTGtmNnHSc6hxANVjO23LPmwFBjZnOt0xOhoBQBqW3vBv6DK2QOObgT9wnfLY8i2DRDwDbw8fSvYVzdMA==}
|
||||||
|
|
||||||
@@ -1586,6 +1631,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
|
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
cac@6.7.14:
|
||||||
|
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
call-bind-apply-helpers@1.0.2:
|
call-bind-apply-helpers@1.0.2:
|
||||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1608,6 +1657,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
||||||
engines: {node: '>=0.8'}
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
|
chai@5.3.3:
|
||||||
|
resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
chalk@4.1.2:
|
chalk@4.1.2:
|
||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -1633,6 +1686,10 @@ packages:
|
|||||||
character-reference-invalid@2.0.1:
|
character-reference-invalid@2.0.1:
|
||||||
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
|
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
|
||||||
|
|
||||||
|
check-error@2.1.3:
|
||||||
|
resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==}
|
||||||
|
engines: {node: '>= 16'}
|
||||||
|
|
||||||
chokidar@4.0.3:
|
chokidar@4.0.3:
|
||||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||||
engines: {node: '>= 14.16.0'}
|
engines: {node: '>= 14.16.0'}
|
||||||
@@ -1789,6 +1846,15 @@ packages:
|
|||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
debug@4.4.3:
|
||||||
|
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||||
|
engines: {node: '>=6.0'}
|
||||||
|
peerDependencies:
|
||||||
|
supports-color: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
supports-color:
|
||||||
|
optional: true
|
||||||
|
|
||||||
decamelize@1.2.0:
|
decamelize@1.2.0:
|
||||||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1807,6 +1873,10 @@ packages:
|
|||||||
babel-plugin-macros:
|
babel-plugin-macros:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
deep-eql@5.0.2:
|
||||||
|
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
deep-is@0.1.4:
|
deep-is@0.1.4:
|
||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
|
|
||||||
@@ -1899,6 +1969,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-module-lexer@1.7.0:
|
||||||
|
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
||||||
|
|
||||||
es-object-atoms@1.1.1:
|
es-object-atoms@1.1.1:
|
||||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1979,6 +2052,9 @@ packages:
|
|||||||
estree-util-is-identifier-name@3.0.0:
|
estree-util-is-identifier-name@3.0.0:
|
||||||
resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
|
resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
|
||||||
|
|
||||||
|
estree-walker@3.0.3:
|
||||||
|
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||||
|
|
||||||
esutils@2.0.3:
|
esutils@2.0.3:
|
||||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -2002,6 +2078,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==}
|
resolution: {integrity: sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
expect-type@1.3.0:
|
||||||
|
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
extend@3.0.2:
|
extend@3.0.2:
|
||||||
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
||||||
|
|
||||||
@@ -2047,6 +2127,15 @@ packages:
|
|||||||
fd-slicer@1.1.0:
|
fd-slicer@1.1.0:
|
||||||
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
|
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
|
||||||
|
|
||||||
|
fdir@6.5.0:
|
||||||
|
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
picomatch: ^3 || ^4
|
||||||
|
peerDependenciesMeta:
|
||||||
|
picomatch:
|
||||||
|
optional: true
|
||||||
|
|
||||||
fecha@4.2.3:
|
fecha@4.2.3:
|
||||||
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
|
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
|
||||||
|
|
||||||
@@ -2389,6 +2478,9 @@ packages:
|
|||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
|
js-tokens@9.0.1:
|
||||||
|
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
|
||||||
|
|
||||||
js-yaml@4.1.0:
|
js-yaml@4.1.0:
|
||||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -2537,6 +2629,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
loupe@3.2.1:
|
||||||
|
resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==}
|
||||||
|
|
||||||
lowlight@1.20.0:
|
lowlight@1.20.0:
|
||||||
resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==}
|
resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==}
|
||||||
|
|
||||||
@@ -2551,6 +2646,9 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
|
magic-string@0.30.21:
|
||||||
|
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||||
|
|
||||||
markdown-table@3.0.4:
|
markdown-table@3.0.4:
|
||||||
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
||||||
|
|
||||||
@@ -2874,6 +2972,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
||||||
engines: {node: '>=16 || 14 >=14.18'}
|
engines: {node: '>=16 || 14 >=14.18'}
|
||||||
|
|
||||||
|
pathe@2.0.3:
|
||||||
|
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||||
|
|
||||||
|
pathval@2.0.1:
|
||||||
|
resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
|
||||||
|
engines: {node: '>= 14.16'}
|
||||||
|
|
||||||
pdf-parse@2.4.5:
|
pdf-parse@2.4.5:
|
||||||
resolution: {integrity: sha512-mHU89HGh7v+4u2ubfnevJ03lmPgQ5WU4CxAVmTSh/sxVTEDYd1er/dKS/A6vg77NX47KTEoihq8jZBLr8Cxuwg==}
|
resolution: {integrity: sha512-mHU89HGh7v+4u2ubfnevJ03lmPgQ5WU4CxAVmTSh/sxVTEDYd1er/dKS/A6vg77NX47KTEoihq8jZBLr8Cxuwg==}
|
||||||
engines: {node: '>=20.16.0 <21 || >=22.3.0'}
|
engines: {node: '>=20.16.0 <21 || >=22.3.0'}
|
||||||
@@ -2927,6 +3032,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||||
engines: {node: '>=8.6'}
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
|
picomatch@4.0.4:
|
||||||
|
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
postcss-modules-extract-imports@3.1.0:
|
postcss-modules-extract-imports@3.1.0:
|
||||||
resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==}
|
resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==}
|
||||||
engines: {node: ^10 || ^12 || >= 14}
|
engines: {node: ^10 || ^12 || >= 14}
|
||||||
@@ -3231,6 +3340,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
siginfo@2.0.0:
|
||||||
|
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||||
|
|
||||||
signal-exit@4.1.0:
|
signal-exit@4.1.0:
|
||||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@@ -3268,10 +3380,16 @@ packages:
|
|||||||
stack-trace@0.0.10:
|
stack-trace@0.0.10:
|
||||||
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
|
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
|
||||||
|
|
||||||
|
stackback@0.0.2:
|
||||||
|
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||||
|
|
||||||
stacktrace-parser@0.1.11:
|
stacktrace-parser@0.1.11:
|
||||||
resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==}
|
resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
std-env@3.10.0:
|
||||||
|
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
||||||
|
|
||||||
string-hash@1.1.3:
|
string-hash@1.1.3:
|
||||||
resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==}
|
resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==}
|
||||||
|
|
||||||
@@ -3305,6 +3423,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
strip-literal@3.1.0:
|
||||||
|
resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==}
|
||||||
|
|
||||||
strnum@1.1.2:
|
strnum@1.1.2:
|
||||||
resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==}
|
resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==}
|
||||||
|
|
||||||
@@ -3353,6 +3474,28 @@ packages:
|
|||||||
tiny-invariant@1.3.3:
|
tiny-invariant@1.3.3:
|
||||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||||
|
|
||||||
|
tinybench@2.9.0:
|
||||||
|
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||||
|
|
||||||
|
tinyexec@0.3.2:
|
||||||
|
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
|
||||||
|
|
||||||
|
tinyglobby@0.2.16:
|
||||||
|
resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
|
tinypool@1.1.1:
|
||||||
|
resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
|
||||||
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
|
|
||||||
|
tinyrainbow@2.0.0:
|
||||||
|
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
tinyspy@4.0.4:
|
||||||
|
resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
@@ -3539,6 +3682,11 @@ packages:
|
|||||||
victory-vendor@36.9.2:
|
victory-vendor@36.9.2:
|
||||||
resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
|
resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
|
||||||
|
|
||||||
|
vite-node@3.2.4:
|
||||||
|
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
|
||||||
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
vite@6.2.0:
|
vite@6.2.0:
|
||||||
resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==}
|
resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==}
|
||||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
@@ -3579,6 +3727,34 @@ packages:
|
|||||||
yaml:
|
yaml:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
vitest@3.2.4:
|
||||||
|
resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
|
||||||
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@edge-runtime/vm': '*'
|
||||||
|
'@types/debug': ^4.1.12
|
||||||
|
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||||
|
'@vitest/browser': 3.2.4
|
||||||
|
'@vitest/ui': 3.2.4
|
||||||
|
happy-dom: '*'
|
||||||
|
jsdom: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@edge-runtime/vm':
|
||||||
|
optional: true
|
||||||
|
'@types/debug':
|
||||||
|
optional: true
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
'@vitest/browser':
|
||||||
|
optional: true
|
||||||
|
'@vitest/ui':
|
||||||
|
optional: true
|
||||||
|
happy-dom:
|
||||||
|
optional: true
|
||||||
|
jsdom:
|
||||||
|
optional: true
|
||||||
|
|
||||||
web-namespaces@2.0.1:
|
web-namespaces@2.0.1:
|
||||||
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
|
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
|
||||||
|
|
||||||
@@ -3597,6 +3773,11 @@ packages:
|
|||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
why-is-node-running@2.3.0:
|
||||||
|
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
winston-console-format@1.0.8:
|
winston-console-format@1.0.8:
|
||||||
resolution: {integrity: sha512-dq7t/E0D0QRi4XIOwu6HM1+5e//WPqylH88GVjKEhQVrzGFg34MCz+G7pMJcXFBen9C0kBsu5GYgbYsE2LDwKw==}
|
resolution: {integrity: sha512-dq7t/E0D0QRi4XIOwu6HM1+5e//WPqylH88GVjKEhQVrzGFg34MCz+G7pMJcXFBen9C0kBsu5GYgbYsE2LDwKw==}
|
||||||
|
|
||||||
@@ -4197,6 +4378,8 @@ snapshots:
|
|||||||
|
|
||||||
'@jridgewell/sourcemap-codec@1.5.0': {}
|
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||||
|
|
||||||
'@jridgewell/trace-mapping@0.3.25':
|
'@jridgewell/trace-mapping@0.3.25':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
@@ -4817,6 +5000,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.26.9
|
'@babel/types': 7.26.9
|
||||||
|
|
||||||
|
'@types/chai@5.2.3':
|
||||||
|
dependencies:
|
||||||
|
'@types/deep-eql': 4.0.2
|
||||||
|
assertion-error: 2.0.1
|
||||||
|
|
||||||
'@types/d3-array@3.2.1': {}
|
'@types/d3-array@3.2.1': {}
|
||||||
|
|
||||||
'@types/d3-color@3.1.3': {}
|
'@types/d3-color@3.1.3': {}
|
||||||
@@ -4845,6 +5033,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/ms': 2.1.0
|
'@types/ms': 2.1.0
|
||||||
|
|
||||||
|
'@types/deep-eql@4.0.2': {}
|
||||||
|
|
||||||
'@types/estree-jsx@1.0.5':
|
'@types/estree-jsx@1.0.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.6
|
'@types/estree': 1.0.6
|
||||||
@@ -5020,6 +5210,48 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
'@vitest/expect@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@types/chai': 5.2.3
|
||||||
|
'@vitest/spy': 3.2.4
|
||||||
|
'@vitest/utils': 3.2.4
|
||||||
|
chai: 5.3.3
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
|
'@vitest/mocker@3.2.4(vite@6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0))':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/spy': 3.2.4
|
||||||
|
estree-walker: 3.0.3
|
||||||
|
magic-string: 0.30.21
|
||||||
|
optionalDependencies:
|
||||||
|
vite: 6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0)
|
||||||
|
|
||||||
|
'@vitest/pretty-format@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
|
'@vitest/runner@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/utils': 3.2.4
|
||||||
|
pathe: 2.0.3
|
||||||
|
strip-literal: 3.1.0
|
||||||
|
|
||||||
|
'@vitest/snapshot@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/pretty-format': 3.2.4
|
||||||
|
magic-string: 0.30.21
|
||||||
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
'@vitest/spy@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
tinyspy: 4.0.4
|
||||||
|
|
||||||
|
'@vitest/utils@3.2.4':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/pretty-format': 3.2.4
|
||||||
|
loupe: 3.2.1
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
abort-controller@3.0.0:
|
abort-controller@3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
event-target-shim: 5.0.1
|
event-target-shim: 5.0.1
|
||||||
@@ -5063,6 +5295,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
assertion-error@2.0.1: {}
|
||||||
|
|
||||||
assistant-stream@0.0.21:
|
assistant-stream@0.0.21:
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid: 3.3.8
|
nanoid: 3.3.8
|
||||||
@@ -5114,6 +5348,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
run-applescript: 7.0.0
|
run-applescript: 7.0.0
|
||||||
|
|
||||||
|
cac@6.7.14: {}
|
||||||
|
|
||||||
call-bind-apply-helpers@1.0.2:
|
call-bind-apply-helpers@1.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
@@ -5132,6 +5368,14 @@ snapshots:
|
|||||||
adler-32: 1.3.1
|
adler-32: 1.3.1
|
||||||
crc-32: 1.2.2
|
crc-32: 1.2.2
|
||||||
|
|
||||||
|
chai@5.3.3:
|
||||||
|
dependencies:
|
||||||
|
assertion-error: 2.0.1
|
||||||
|
check-error: 2.1.3
|
||||||
|
deep-eql: 5.0.2
|
||||||
|
loupe: 3.2.1
|
||||||
|
pathval: 2.0.1
|
||||||
|
|
||||||
chalk@4.1.2:
|
chalk@4.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
@@ -5151,6 +5395,8 @@ snapshots:
|
|||||||
|
|
||||||
character-reference-invalid@2.0.1: {}
|
character-reference-invalid@2.0.1: {}
|
||||||
|
|
||||||
|
check-error@2.1.3: {}
|
||||||
|
|
||||||
chokidar@4.0.3:
|
chokidar@4.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
readdirp: 4.1.2
|
readdirp: 4.1.2
|
||||||
@@ -5278,6 +5524,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
|
debug@4.4.3:
|
||||||
|
dependencies:
|
||||||
|
ms: 2.1.3
|
||||||
|
|
||||||
decamelize@1.2.0: {}
|
decamelize@1.2.0: {}
|
||||||
|
|
||||||
decimal.js-light@2.5.1: {}
|
decimal.js-light@2.5.1: {}
|
||||||
@@ -5288,6 +5538,8 @@ snapshots:
|
|||||||
|
|
||||||
dedent@1.5.3: {}
|
dedent@1.5.3: {}
|
||||||
|
|
||||||
|
deep-eql@5.0.2: {}
|
||||||
|
|
||||||
deep-is@0.1.4: {}
|
deep-is@0.1.4: {}
|
||||||
|
|
||||||
default-browser-id@5.0.0: {}
|
default-browser-id@5.0.0: {}
|
||||||
@@ -5361,6 +5613,8 @@ snapshots:
|
|||||||
|
|
||||||
es-errors@1.3.0: {}
|
es-errors@1.3.0: {}
|
||||||
|
|
||||||
|
es-module-lexer@1.7.0: {}
|
||||||
|
|
||||||
es-object-atoms@1.1.1:
|
es-object-atoms@1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
@@ -5489,6 +5743,10 @@ snapshots:
|
|||||||
|
|
||||||
estree-util-is-identifier-name@3.0.0: {}
|
estree-util-is-identifier-name@3.0.0: {}
|
||||||
|
|
||||||
|
estree-walker@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.6
|
||||||
|
|
||||||
esutils@2.0.3: {}
|
esutils@2.0.3: {}
|
||||||
|
|
||||||
event-target-shim@5.0.1: {}
|
event-target-shim@5.0.1: {}
|
||||||
@@ -5514,6 +5772,8 @@ snapshots:
|
|||||||
|
|
||||||
exit-hook@4.0.0: {}
|
exit-hook@4.0.0: {}
|
||||||
|
|
||||||
|
expect-type@1.3.0: {}
|
||||||
|
|
||||||
extend@3.0.2: {}
|
extend@3.0.2: {}
|
||||||
|
|
||||||
extract-zip@2.0.1:
|
extract-zip@2.0.1:
|
||||||
@@ -5568,6 +5828,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
pend: 1.2.0
|
pend: 1.2.0
|
||||||
|
|
||||||
|
fdir@6.5.0(picomatch@4.0.4):
|
||||||
|
optionalDependencies:
|
||||||
|
picomatch: 4.0.4
|
||||||
|
|
||||||
fecha@4.2.3: {}
|
fecha@4.2.3: {}
|
||||||
|
|
||||||
figures@6.1.0:
|
figures@6.1.0:
|
||||||
@@ -5922,6 +6186,8 @@ snapshots:
|
|||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
|
js-tokens@9.0.1: {}
|
||||||
|
|
||||||
js-yaml@4.1.0:
|
js-yaml@4.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
argparse: 2.0.1
|
argparse: 2.0.1
|
||||||
@@ -6048,6 +6314,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
|
|
||||||
|
loupe@3.2.1: {}
|
||||||
|
|
||||||
lowlight@1.20.0:
|
lowlight@1.20.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
fault: 1.0.4
|
fault: 1.0.4
|
||||||
@@ -6063,6 +6331,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
|
|
||||||
|
magic-string@0.30.21:
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
markdown-table@3.0.4: {}
|
markdown-table@3.0.4: {}
|
||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
@@ -6605,6 +6877,10 @@ snapshots:
|
|||||||
lru-cache: 10.4.3
|
lru-cache: 10.4.3
|
||||||
minipass: 7.1.2
|
minipass: 7.1.2
|
||||||
|
|
||||||
|
pathe@2.0.3: {}
|
||||||
|
|
||||||
|
pathval@2.0.1: {}
|
||||||
|
|
||||||
pdf-parse@2.4.5:
|
pdf-parse@2.4.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@napi-rs/canvas': 0.1.80
|
'@napi-rs/canvas': 0.1.80
|
||||||
@@ -6655,6 +6931,8 @@ snapshots:
|
|||||||
|
|
||||||
picomatch@2.3.1: {}
|
picomatch@2.3.1: {}
|
||||||
|
|
||||||
|
picomatch@4.0.4: {}
|
||||||
|
|
||||||
postcss-modules-extract-imports@3.1.0(postcss@8.5.3):
|
postcss-modules-extract-imports@3.1.0(postcss@8.5.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
postcss: 8.5.3
|
postcss: 8.5.3
|
||||||
@@ -7023,6 +7301,8 @@ snapshots:
|
|||||||
|
|
||||||
shebang-regex@3.0.0: {}
|
shebang-regex@3.0.0: {}
|
||||||
|
|
||||||
|
siginfo@2.0.0: {}
|
||||||
|
|
||||||
signal-exit@4.1.0: {}
|
signal-exit@4.1.0: {}
|
||||||
|
|
||||||
simple-swizzle@0.2.2:
|
simple-swizzle@0.2.2:
|
||||||
@@ -7050,10 +7330,14 @@ snapshots:
|
|||||||
|
|
||||||
stack-trace@0.0.10: {}
|
stack-trace@0.0.10: {}
|
||||||
|
|
||||||
|
stackback@0.0.2: {}
|
||||||
|
|
||||||
stacktrace-parser@0.1.11:
|
stacktrace-parser@0.1.11:
|
||||||
dependencies:
|
dependencies:
|
||||||
type-fest: 0.7.1
|
type-fest: 0.7.1
|
||||||
|
|
||||||
|
std-env@3.10.0: {}
|
||||||
|
|
||||||
string-hash@1.1.3: {}
|
string-hash@1.1.3: {}
|
||||||
|
|
||||||
string-width@4.2.3:
|
string-width@4.2.3:
|
||||||
@@ -7089,6 +7373,10 @@ snapshots:
|
|||||||
|
|
||||||
strip-json-comments@3.1.1: {}
|
strip-json-comments@3.1.1: {}
|
||||||
|
|
||||||
|
strip-literal@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
js-tokens: 9.0.1
|
||||||
|
|
||||||
strnum@1.1.2: {}
|
strnum@1.1.2: {}
|
||||||
|
|
||||||
strnum@2.2.3: {}
|
strnum@2.2.3: {}
|
||||||
@@ -7135,6 +7423,21 @@ snapshots:
|
|||||||
|
|
||||||
tiny-invariant@1.3.3: {}
|
tiny-invariant@1.3.3: {}
|
||||||
|
|
||||||
|
tinybench@2.9.0: {}
|
||||||
|
|
||||||
|
tinyexec@0.3.2: {}
|
||||||
|
|
||||||
|
tinyglobby@0.2.16:
|
||||||
|
dependencies:
|
||||||
|
fdir: 6.5.0(picomatch@4.0.4)
|
||||||
|
picomatch: 4.0.4
|
||||||
|
|
||||||
|
tinypool@1.1.1: {}
|
||||||
|
|
||||||
|
tinyrainbow@2.0.0: {}
|
||||||
|
|
||||||
|
tinyspy@4.0.4: {}
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
@@ -7323,6 +7626,27 @@ snapshots:
|
|||||||
d3-time: 3.1.0
|
d3-time: 3.1.0
|
||||||
d3-timer: 3.0.1
|
d3-timer: 3.0.1
|
||||||
|
|
||||||
|
vite-node@3.2.4(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0):
|
||||||
|
dependencies:
|
||||||
|
cac: 6.7.14
|
||||||
|
debug: 4.4.3
|
||||||
|
es-module-lexer: 1.7.0
|
||||||
|
pathe: 2.0.3
|
||||||
|
vite: 6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- jiti
|
||||||
|
- less
|
||||||
|
- lightningcss
|
||||||
|
- sass
|
||||||
|
- sass-embedded
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
- tsx
|
||||||
|
- yaml
|
||||||
|
|
||||||
vite@6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0):
|
vite@6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.25.0
|
esbuild: 0.25.0
|
||||||
@@ -7336,6 +7660,48 @@ snapshots:
|
|||||||
tsx: 4.19.3
|
tsx: 4.19.3
|
||||||
yaml: 2.7.0
|
yaml: 2.7.0
|
||||||
|
|
||||||
|
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0):
|
||||||
|
dependencies:
|
||||||
|
'@types/chai': 5.2.3
|
||||||
|
'@vitest/expect': 3.2.4
|
||||||
|
'@vitest/mocker': 3.2.4(vite@6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0))
|
||||||
|
'@vitest/pretty-format': 3.2.4
|
||||||
|
'@vitest/runner': 3.2.4
|
||||||
|
'@vitest/snapshot': 3.2.4
|
||||||
|
'@vitest/spy': 3.2.4
|
||||||
|
'@vitest/utils': 3.2.4
|
||||||
|
chai: 5.3.3
|
||||||
|
debug: 4.4.3
|
||||||
|
expect-type: 1.3.0
|
||||||
|
magic-string: 0.30.21
|
||||||
|
pathe: 2.0.3
|
||||||
|
picomatch: 4.0.4
|
||||||
|
std-env: 3.10.0
|
||||||
|
tinybench: 2.9.0
|
||||||
|
tinyexec: 0.3.2
|
||||||
|
tinyglobby: 0.2.16
|
||||||
|
tinypool: 1.1.1
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
vite: 6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0)
|
||||||
|
vite-node: 3.2.4(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0)
|
||||||
|
why-is-node-running: 2.3.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/debug': 4.1.12
|
||||||
|
'@types/node': 22.13.5
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- jiti
|
||||||
|
- less
|
||||||
|
- lightningcss
|
||||||
|
- msw
|
||||||
|
- sass
|
||||||
|
- sass-embedded
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
- tsx
|
||||||
|
- yaml
|
||||||
|
|
||||||
web-namespaces@2.0.1: {}
|
web-namespaces@2.0.1: {}
|
||||||
|
|
||||||
web-streams-polyfill@4.0.0-beta.3: {}
|
web-streams-polyfill@4.0.0-beta.3: {}
|
||||||
@@ -7351,6 +7717,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
|
||||||
|
why-is-node-running@2.3.0:
|
||||||
|
dependencies:
|
||||||
|
siginfo: 2.0.0
|
||||||
|
stackback: 0.0.2
|
||||||
|
|
||||||
winston-console-format@1.0.8:
|
winston-console-format@1.0.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
colors: 1.4.0
|
colors: 1.4.0
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
import { describe, it, expect, vi } from "vitest";
|
||||||
|
|
||||||
|
// Mock config to avoid missing env var errors at import time
|
||||||
|
vi.mock("@/agent/utils/config", () => ({
|
||||||
|
config: {
|
||||||
|
kb: { url: "http://mock", searchPath: "/search", apiKey: "mock" },
|
||||||
|
gongdan: { apiBase: "http://mock", apiKey: "mock" },
|
||||||
|
jina: { apiKey: "mock" },
|
||||||
|
serper: { apiKey: "mock" },
|
||||||
|
daytona: { apiKey: "mock", apiUrl: "http://mock" },
|
||||||
|
azureOpenAI: { apiKey: "mock", endpoint: "http://mock", apiVersion: "2024", deployment: "mock" },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { preValidateToolCall } from "../tool-executor.js";
|
||||||
|
import type { EnterpriseState } from "../../types.js";
|
||||||
|
import { HumanMessage } from "@langchain/core/messages";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a minimal EnterpriseState stub for testing preValidateToolCall.
|
||||||
|
* Only `messages` and `execution_log` are inspected by the validator.
|
||||||
|
*/
|
||||||
|
function makeState(
|
||||||
|
overrides: Partial<Pick<EnterpriseState, "messages" | "execution_log">> = {},
|
||||||
|
): EnterpriseState {
|
||||||
|
return {
|
||||||
|
messages: overrides.messages ?? [new HumanMessage("hello world test")],
|
||||||
|
ui: [],
|
||||||
|
timestamp: Date.now(),
|
||||||
|
toolStatus: [],
|
||||||
|
execution_log: overrides.execution_log ?? [],
|
||||||
|
} as unknown as EnterpriseState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ticket_detail
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
describe("preValidateToolCall - ticket_detail", () => {
|
||||||
|
it("passes with a valid ticket_id", () => {
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"ticket_detail",
|
||||||
|
{ ticket_id: "TK-2026-123456" },
|
||||||
|
makeState(),
|
||||||
|
);
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(["xxx", "null", "{ticket_id}", "undefined", "test", "id", "unknown"])(
|
||||||
|
"blocks placeholder id: %s",
|
||||||
|
(badId) => {
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"ticket_detail",
|
||||||
|
{ ticket_id: badId },
|
||||||
|
makeState(),
|
||||||
|
);
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
expect(result).toContain("ticket_detail");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// kb_search
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
describe("preValidateToolCall - kb_search", () => {
|
||||||
|
it("blocks query shorter than 5 characters", () => {
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"kb_search",
|
||||||
|
{ query: "abc" },
|
||||||
|
makeState(),
|
||||||
|
);
|
||||||
|
expect(result).toContain("搜索词过短");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("blocks duplicate same-query in the same turn", () => {
|
||||||
|
const state = makeState({
|
||||||
|
execution_log: [
|
||||||
|
{
|
||||||
|
tool: "kb_search",
|
||||||
|
status: "success",
|
||||||
|
inputSummary: "query: VPN connection issue",
|
||||||
|
summary: "found 3 results",
|
||||||
|
timestamp: Date.now(),
|
||||||
|
durationMs: 100,
|
||||||
|
resultCount: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"kb_search",
|
||||||
|
{ query: "VPN connection issue" },
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
expect(result).toContain("已查询过相同内容");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes with a valid long-enough query and no duplicates", () => {
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"kb_search",
|
||||||
|
{ query: "how to reset password" },
|
||||||
|
makeState(),
|
||||||
|
);
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ticket_list - page validation
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
describe("preValidateToolCall - ticket_list page range", () => {
|
||||||
|
it("blocks page=0 (below range)", () => {
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"ticket_list",
|
||||||
|
{ page: 0 },
|
||||||
|
makeState(),
|
||||||
|
);
|
||||||
|
expect(result).toContain("page 参数无效");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("blocks page=101 (above range)", () => {
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"ticket_list",
|
||||||
|
{ page: 101 },
|
||||||
|
makeState(),
|
||||||
|
);
|
||||||
|
expect(result).toContain("page 参数无效");
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([1, 50, 100])("passes with valid page=%i", (page) => {
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"ticket_list",
|
||||||
|
{ page },
|
||||||
|
makeState(),
|
||||||
|
);
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// execution_log length >= 3
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
describe("preValidateToolCall - tool call limit", () => {
|
||||||
|
it("blocks when execution_log.length >= 3", () => {
|
||||||
|
const state = makeState({
|
||||||
|
execution_log: [
|
||||||
|
{ tool: "kb_search", status: "success", summary: "", timestamp: 1, durationMs: 0, inputSummary: "" },
|
||||||
|
{ tool: "ticket_list", status: "success", summary: "", timestamp: 2, durationMs: 0, inputSummary: "" },
|
||||||
|
{ tool: "ticket_detail", status: "success", summary: "", timestamp: 3, durationMs: 0, inputSummary: "" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"kb_search",
|
||||||
|
{ query: "something long enough" },
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
expect(result).toContain("达到单轮上限");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ticket_list when message contains TK-xxxx
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
describe("preValidateToolCall - ticket_list with TK-xxxx in message", () => {
|
||||||
|
it("blocks ticket_list when user message contains a ticket number", () => {
|
||||||
|
const state = makeState({
|
||||||
|
messages: [new HumanMessage("请帮我查一下 TK-2026-296691 的状态")],
|
||||||
|
});
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"ticket_list",
|
||||||
|
{ page: 1 },
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
expect(result).toContain("ticket_detail");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes ticket_list when message has no ticket number", () => {
|
||||||
|
const state = makeState({
|
||||||
|
messages: [new HumanMessage("列出最近的工单")],
|
||||||
|
});
|
||||||
|
const result = preValidateToolCall(
|
||||||
|
"ticket_list",
|
||||||
|
{ page: 1 },
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -185,7 +185,7 @@ async function withTimeout<T>(
|
|||||||
* Pre-validate tool call arguments before execution.
|
* Pre-validate tool call arguments before execution.
|
||||||
* Returns a human-readable block reason, or null if validation passes.
|
* Returns a human-readable block reason, or null if validation passes.
|
||||||
*/
|
*/
|
||||||
function preValidateToolCall(
|
export function preValidateToolCall(
|
||||||
name: string,
|
name: string,
|
||||||
args: Record<string, unknown>,
|
args: Record<string, unknown>,
|
||||||
state: EnterpriseState,
|
state: EnterpriseState,
|
||||||
@@ -204,7 +204,7 @@ function preValidateToolCall(
|
|||||||
return "ticket_detail 调用被拦截:未提供有效工单编号,请改用 ticket_list 查询工单列表";
|
return "ticket_detail 调用被拦截:未提供有效工单编号,请改用 ticket_list 查询工单列表";
|
||||||
}
|
}
|
||||||
// Placeholder ID detection
|
// Placeholder ID detection
|
||||||
const PLACEHOLDER = /^(xxx|unknown|test|id|null|undefined|\d{1,2})$/i;
|
const PLACEHOLDER = /^(xxx|unknown|test|id|null|undefined|\d{1,2}|\{.*\})$/i;
|
||||||
if (PLACEHOLDER.test(id)) {
|
if (PLACEHOLDER.test(id)) {
|
||||||
return `ticket_detail 调用被拦截:工单 ID "${id}" 看起来是占位符,请先用 ticket_list 查询真实工单号`;
|
return `ticket_detail 调用被拦截:工单 ID "${id}" 看起来是占位符,请先用 ticket_list 查询真实工单号`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { config } from "@/agent/utils/config";
|
import { config } from "@/agent/utils/config";
|
||||||
|
import { getToolConfig } from "@/agent/utils/toolConfig";
|
||||||
|
|
||||||
// --- Knowledge Base Search ---
|
// --- Knowledge Base Search ---
|
||||||
export async function kbSearch(query: string): Promise<{
|
export async function kbSearch(query: string): Promise<{
|
||||||
@@ -24,7 +25,7 @@ export async function kbSearch(query: string): Promise<{
|
|||||||
"api-key": config.kb.apiKey,
|
"api-key": config.kb.apiKey,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ query, top: 5, search_mode: "hybrid" }),
|
body: JSON.stringify({ query, top: 5, search_mode: "hybrid" }),
|
||||||
signal: AbortSignal.timeout(45000),
|
signal: AbortSignal.timeout(getToolConfig("kb_search").timeoutMs),
|
||||||
});
|
});
|
||||||
if (!resp.ok) throw new Error(`KB search failed: ${resp.status}`);
|
if (!resp.ok) throw new Error(`KB search failed: ${resp.status}`);
|
||||||
return resp.json();
|
return resp.json();
|
||||||
@@ -47,7 +48,7 @@ export async function ticketList(
|
|||||||
const url = `${config.gongdan.apiBase}/api/tickets?page=${page}&pageSize=${pageSize}`;
|
const url = `${config.gongdan.apiBase}/api/tickets?page=${page}&pageSize=${pageSize}`;
|
||||||
const resp = await fetch(url, {
|
const resp = await fetch(url, {
|
||||||
headers: { "X-Api-Key": config.gongdan.apiKey },
|
headers: { "X-Api-Key": config.gongdan.apiKey },
|
||||||
signal: AbortSignal.timeout(15000),
|
signal: AbortSignal.timeout(getToolConfig("ticket_list").timeoutMs),
|
||||||
});
|
});
|
||||||
if (!resp.ok) throw new Error(`Ticket list failed: ${resp.status}`);
|
if (!resp.ok) throw new Error(`Ticket list failed: ${resp.status}`);
|
||||||
return resp.json();
|
return resp.json();
|
||||||
@@ -59,6 +60,7 @@ export async function ticketDetail(
|
|||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
const base = config.gongdan.apiBase;
|
const base = config.gongdan.apiBase;
|
||||||
const headers = { "X-Api-Key": config.gongdan.apiKey };
|
const headers = { "X-Api-Key": config.gongdan.apiKey };
|
||||||
|
const tdTimeout = getToolConfig("ticket_detail").timeoutMs;
|
||||||
|
|
||||||
// If ticketId looks like a ticketNumber (e.g. "TK-2026-296691"), resolve UUID first.
|
// If ticketId looks like a ticketNumber (e.g. "TK-2026-296691"), resolve UUID first.
|
||||||
// Strategy: try query-param filter first; if no match, fall back to full list + local find.
|
// Strategy: try query-param filter first; if no match, fall back to full list + local find.
|
||||||
@@ -69,7 +71,7 @@ export async function ticketDetail(
|
|||||||
const searchUrl = `${base}/api/tickets?${paramName}=${encodeURIComponent(ticketId)}&pageSize=50`;
|
const searchUrl = `${base}/api/tickets?${paramName}=${encodeURIComponent(ticketId)}&pageSize=50`;
|
||||||
const searchResp = await fetch(searchUrl, {
|
const searchResp = await fetch(searchUrl, {
|
||||||
headers,
|
headers,
|
||||||
signal: AbortSignal.timeout(15000),
|
signal: AbortSignal.timeout(tdTimeout),
|
||||||
});
|
});
|
||||||
if (searchResp.ok) {
|
if (searchResp.ok) {
|
||||||
const searchData = await searchResp.json();
|
const searchData = await searchResp.json();
|
||||||
@@ -91,7 +93,7 @@ export async function ticketDetail(
|
|||||||
const listUrl = `${base}/api/tickets?pageSize=100`;
|
const listUrl = `${base}/api/tickets?pageSize=100`;
|
||||||
const listResp = await fetch(listUrl, {
|
const listResp = await fetch(listUrl, {
|
||||||
headers,
|
headers,
|
||||||
signal: AbortSignal.timeout(15000),
|
signal: AbortSignal.timeout(tdTimeout),
|
||||||
});
|
});
|
||||||
if (listResp.ok) {
|
if (listResp.ok) {
|
||||||
const listData = await listResp.json();
|
const listData = await listResp.json();
|
||||||
@@ -111,7 +113,7 @@ export async function ticketDetail(
|
|||||||
let url = `${base}/api/tickets/detail/${resolvedId}`;
|
let url = `${base}/api/tickets/detail/${resolvedId}`;
|
||||||
let resp = await fetch(url, {
|
let resp = await fetch(url, {
|
||||||
headers,
|
headers,
|
||||||
signal: AbortSignal.timeout(15000),
|
signal: AbortSignal.timeout(tdTimeout),
|
||||||
});
|
});
|
||||||
|
|
||||||
// If /detail/ returns 404, fall back to /api/tickets/{id}
|
// If /detail/ returns 404, fall back to /api/tickets/{id}
|
||||||
@@ -119,7 +121,7 @@ export async function ticketDetail(
|
|||||||
url = `${base}/api/tickets/${resolvedId}`;
|
url = `${base}/api/tickets/${resolvedId}`;
|
||||||
resp = await fetch(url, {
|
resp = await fetch(url, {
|
||||||
headers,
|
headers,
|
||||||
signal: AbortSignal.timeout(15000),
|
signal: AbortSignal.timeout(tdTimeout),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +149,7 @@ export async function webSearch(query: string): Promise<{
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify({ q: query, num: 5 }),
|
body: JSON.stringify({ q: query, num: 5 }),
|
||||||
signal: AbortSignal.timeout(12000),
|
signal: AbortSignal.timeout(getToolConfig("web_search").timeoutMs),
|
||||||
});
|
});
|
||||||
if (!searchResp.ok) throw new Error(`Jina search failed: ${searchResp.status}`);
|
if (!searchResp.ok) throw new Error(`Jina search failed: ${searchResp.status}`);
|
||||||
const searchData = await searchResp.json();
|
const searchData = await searchResp.json();
|
||||||
@@ -159,7 +161,7 @@ export async function webSearch(query: string): Promise<{
|
|||||||
if (!r.url) return "";
|
if (!r.url) return "";
|
||||||
const readResp = await fetch(`https://r.jina.ai/${r.url}`, {
|
const readResp = await fetch(`https://r.jina.ai/${r.url}`, {
|
||||||
headers,
|
headers,
|
||||||
signal: AbortSignal.timeout(8000),
|
signal: AbortSignal.timeout(getToolConfig("web_search").timeoutMs),
|
||||||
});
|
});
|
||||||
if (!readResp.ok) return "";
|
if (!readResp.ok) return "";
|
||||||
const data = await readResp.json();
|
const data = await readResp.json();
|
||||||
@@ -200,7 +202,7 @@ export async function googleSearch(query: string): Promise<{
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ q: query, num: 10, gl: "cn", hl: "zh-cn" }),
|
body: JSON.stringify({ q: query, num: 10, gl: "cn", hl: "zh-cn" }),
|
||||||
signal: AbortSignal.timeout(10000),
|
signal: AbortSignal.timeout(getToolConfig("google_search").timeoutMs),
|
||||||
});
|
});
|
||||||
if (!resp.ok) throw new Error(`Serper search failed: ${resp.status}`);
|
if (!resp.ok) throw new Error(`Serper search failed: ${resp.status}`);
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
@@ -229,7 +231,7 @@ export async function webRead(url: string): Promise<{ content: string; title: st
|
|||||||
Authorization: `Bearer ${config.jina.apiKey}`,
|
Authorization: `Bearer ${config.jina.apiKey}`,
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
},
|
},
|
||||||
signal: AbortSignal.timeout(10000),
|
signal: AbortSignal.timeout(getToolConfig("web_search").timeoutMs),
|
||||||
});
|
});
|
||||||
if (!resp.ok) throw new Error(`Jina read failed: ${resp.status}`);
|
if (!resp.ok) throw new Error(`Jina read failed: ${resp.status}`);
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
@@ -252,7 +254,7 @@ export async function jinaRerank(query: string, documents: string[], topN = 5):
|
|||||||
model: "jina-reranker-v2-base-multilingual",
|
model: "jina-reranker-v2-base-multilingual",
|
||||||
top_n: topN,
|
top_n: topN,
|
||||||
}),
|
}),
|
||||||
signal: AbortSignal.timeout(10000),
|
signal: AbortSignal.timeout(getToolConfig("web_search").timeoutMs),
|
||||||
});
|
});
|
||||||
if (!resp.ok) throw new Error(`Jina rerank failed: ${resp.status}`);
|
if (!resp.ok) throw new Error(`Jina rerank failed: ${resp.status}`);
|
||||||
return resp.json();
|
return resp.json();
|
||||||
@@ -264,13 +266,10 @@ export async function jinaRerank(query: string, documents: string[], topN = 5):
|
|||||||
const SANDBOX_MAX_CODE_LENGTH = 10_000;
|
const SANDBOX_MAX_CODE_LENGTH = 10_000;
|
||||||
/** Maximum output length (characters) before truncation */
|
/** Maximum output length (characters) before truncation */
|
||||||
const SANDBOX_MAX_OUTPUT_LENGTH = 3_000;
|
const SANDBOX_MAX_OUTPUT_LENGTH = 3_000;
|
||||||
/** Default code execution timeout (ms) */
|
|
||||||
const SANDBOX_DEFAULT_EXEC_TIMEOUT_MS = 15_000;
|
|
||||||
|
|
||||||
export async function sandboxRun(
|
export async function sandboxRun(
|
||||||
code: string,
|
code: string,
|
||||||
language = "python",
|
language = "python",
|
||||||
timeoutMs = SANDBOX_DEFAULT_EXEC_TIMEOUT_MS,
|
timeoutMs = getToolConfig("code_execute").timeoutMs,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
exit_code: number;
|
exit_code: number;
|
||||||
stdout: string;
|
stdout: string;
|
||||||
@@ -295,7 +294,7 @@ export async function sandboxRun(
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify({ autoStopInterval: 5, autoDeleteInterval: 0 }),
|
body: JSON.stringify({ autoStopInterval: 5, autoDeleteInterval: 0 }),
|
||||||
signal: AbortSignal.timeout(30000),
|
signal: AbortSignal.timeout(getToolConfig("code_execute").timeoutMs * 2),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const isTimeout = e instanceof DOMException && e.name === "AbortError";
|
const isTimeout = e instanceof DOMException && e.name === "AbortError";
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import { describe, it, expect, vi } from "vitest";
|
||||||
|
import { executeWithRetry } from "../retry.js";
|
||||||
|
|
||||||
|
describe("executeWithRetry", () => {
|
||||||
|
it("returns result on first success with retryCount=0 (no retries triggered)", async () => {
|
||||||
|
const onAttempt = vi.fn();
|
||||||
|
const result = await executeWithRetry(
|
||||||
|
() => Promise.resolve("ok"),
|
||||||
|
2,
|
||||||
|
{ backoffMs: 10, onAttempt },
|
||||||
|
);
|
||||||
|
expect(result).toBe("ok");
|
||||||
|
expect(onAttempt).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("retries once then succeeds (retryCount=1)", async () => {
|
||||||
|
let call = 0;
|
||||||
|
const onAttempt = vi.fn();
|
||||||
|
const result = await executeWithRetry(
|
||||||
|
() => {
|
||||||
|
call++;
|
||||||
|
if (call === 1) return Promise.reject(new Error("fail1"));
|
||||||
|
return Promise.resolve("ok2");
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
{ backoffMs: 10, onAttempt },
|
||||||
|
);
|
||||||
|
expect(result).toBe("ok2");
|
||||||
|
expect(onAttempt).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onAttempt).toHaveBeenCalledWith(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws the last error when all attempts fail", async () => {
|
||||||
|
let call = 0;
|
||||||
|
await expect(
|
||||||
|
executeWithRetry(
|
||||||
|
() => {
|
||||||
|
call++;
|
||||||
|
return Promise.reject(new Error(`fail${call}`));
|
||||||
|
},
|
||||||
|
2, // 2 retries = 3 total attempts
|
||||||
|
{ backoffMs: 10 },
|
||||||
|
),
|
||||||
|
).rejects.toThrow("fail3");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects immediately when abortSignal is already aborted", async () => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
controller.abort();
|
||||||
|
|
||||||
|
const fn = vi.fn(() => Promise.reject(new Error("should-retry")));
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
executeWithRetry(fn, 3, {
|
||||||
|
backoffMs: 10,
|
||||||
|
abortSignal: controller.signal,
|
||||||
|
}),
|
||||||
|
).rejects.toThrow();
|
||||||
|
|
||||||
|
// fn is called once (the initial attempt), then during the backoff wait
|
||||||
|
// the abort fires and rejects with RunCancelled before any retry
|
||||||
|
expect(fn).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects with RunCancelled when abortSignal fires during backoff", async () => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
let call = 0;
|
||||||
|
|
||||||
|
const fn = () => {
|
||||||
|
call++;
|
||||||
|
if (call === 1) {
|
||||||
|
// Abort during the backoff delay after first failure
|
||||||
|
setTimeout(() => controller.abort(), 5);
|
||||||
|
return Promise.reject(new Error("fail1"));
|
||||||
|
}
|
||||||
|
return Promise.resolve("should-not-reach");
|
||||||
|
};
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
executeWithRetry(fn, 3, {
|
||||||
|
backoffMs: 5000, // long backoff so abort fires first
|
||||||
|
abortSignal: controller.signal,
|
||||||
|
}),
|
||||||
|
).rejects.toThrow("RunCancelled");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -26,6 +26,11 @@ export async function executeWithRetry<T>(
|
|||||||
`[retry] attempt ${attempt}/${maxAttempts} failed, retrying in ${delay}ms`,
|
`[retry] attempt ${attempt}/${maxAttempts} failed, retrying in ${delay}ms`,
|
||||||
);
|
);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
// Check if signal already aborted before adding listener
|
||||||
|
if (options?.abortSignal?.aborted) {
|
||||||
|
reject(new Error("RunCancelled"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
const t = setTimeout(resolve, delay);
|
const t = setTimeout(resolve, delay);
|
||||||
options?.abortSignal?.addEventListener("abort", () => {
|
options?.abortSignal?.addEventListener("abort", () => {
|
||||||
clearTimeout(t);
|
clearTimeout(t);
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: "node",
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user