"feat: 新增 kevisual 同步配置,升级相关依赖"

This commit is contained in:
熊潇 2025-05-27 01:39:15 +08:00
parent 1869164d3d
commit 552653c8f7
17 changed files with 1214 additions and 770 deletions

32
assistant/kevisual.json Normal file
View File

@ -0,0 +1,32 @@
{
"$schema": "https://kevisual.xiongxiao.me/root/ai/kevisual/tools/kevisual-sync/schema.json?v=2",
"metadata": {
"share": "public"
},
"checkDir": {
"src/query/query-login": {
"url": "https://kevisual.xiongxiao.me/root/ai/code/registry/query/query-login",
"enabled": true
},
"src/query/query-ai": {
"url": "https://kevisual.xiongxiao.me/root/ai/code/registry/query/query-ai",
"enabled": true
}
},
"syncDirectory": [
{
"files": [
"src/query/query-login/**/*",
"src/query/query-ai/**/*"
],
"ignore": [],
"registry": "https://kevisual.xiongxiao.me/root/ai/code/registry",
"replace": {
"src/": ""
}
}
],
"sync": {
"src/query/index.ts": "https://kevisual.xiongxiao.me/root/ai/code/registry/query/index.ts"
}
}

View File

@ -41,11 +41,11 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@kevisual/ai-center": "^0.0.4", "@kevisual/ai": "^0.0.5",
"@kevisual/load": "^0.0.6", "@kevisual/load": "^0.0.6",
"@kevisual/local-app-manager": "^0.1.20", "@kevisual/local-app-manager": "^0.1.20",
"@kevisual/logger": "^0.0.4", "@kevisual/logger": "^0.0.4",
"@kevisual/query": "0.0.18", "@kevisual/query": "0.0.20",
"@kevisual/query-login": "0.0.6", "@kevisual/query-login": "0.0.6",
"@kevisual/router": "^0.0.21", "@kevisual/router": "^0.0.21",
"@kevisual/task-command": "^0.0.7", "@kevisual/task-command": "^0.0.7",
@ -62,7 +62,7 @@
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"get-port": "^7.1.0", "get-port": "^7.1.0",
"inquirer": "^12.6.1", "inquirer": "^12.6.3",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"nanoid": "^5.1.5", "nanoid": "^5.1.5",
"send": "^1.2.0", "send": "^1.2.0",
@ -77,6 +77,7 @@
}, },
"dependencies": { "dependencies": {
"eventemitter3": "^5.0.1", "eventemitter3": "^5.0.1",
"lowdb": "^7.0.1",
"pm2": "^6.0.6" "pm2": "^6.0.6"
} }
} }

225
assistant/pnpm-lock.yaml generated
View File

@ -11,13 +11,16 @@ importers:
eventemitter3: eventemitter3:
specifier: ^5.0.1 specifier: ^5.0.1
version: 5.0.1 version: 5.0.1
lowdb:
specifier: ^7.0.1
version: 7.0.1
pm2: pm2:
specifier: ^6.0.6 specifier: ^6.0.6
version: 6.0.6(supports-color@10.0.0) version: 6.0.6(supports-color@10.0.0)
devDependencies: devDependencies:
'@kevisual/ai-center': '@kevisual/ai':
specifier: ^0.0.4 specifier: ^0.0.5
version: 0.0.4 version: 0.0.5
'@kevisual/load': '@kevisual/load':
specifier: ^0.0.6 specifier: ^0.0.6
version: 0.0.6 version: 0.0.6
@ -28,11 +31,11 @@ importers:
specifier: ^0.0.4 specifier: ^0.0.4
version: 0.0.4 version: 0.0.4
'@kevisual/query': '@kevisual/query':
specifier: 0.0.18 specifier: 0.0.20
version: 0.0.18(@kevisual/ws@8.0.0) version: 0.0.20(@kevisual/ws@8.0.0)
'@kevisual/query-login': '@kevisual/query-login':
specifier: 0.0.6 specifier: 0.0.6
version: 0.0.6(@kevisual/query@0.0.18(@kevisual/ws@8.0.0))(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3) version: 0.0.6(@kevisual/query@0.0.20(@kevisual/ws@8.0.0))(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)
'@kevisual/router': '@kevisual/router':
specifier: ^0.0.21 specifier: ^0.0.21
version: 0.0.21 version: 0.0.21
@ -79,8 +82,8 @@ importers:
specifier: ^7.1.0 specifier: ^7.1.0
version: 7.1.0 version: 7.1.0
inquirer: inquirer:
specifier: ^12.6.1 specifier: ^12.6.3
version: 12.6.1(@types/node@22.15.21) version: 12.6.3(@types/node@22.15.21)
lodash-es: lodash-es:
specifier: ^4.17.21 specifier: ^4.17.21
version: 4.17.21 version: 4.17.21
@ -126,7 +129,7 @@ importers:
version: 16.5.0 version: 16.5.0
inquirer: inquirer:
specifier: ^12.6.0 specifier: ^12.6.0
version: 12.6.1(@types/node@22.15.21) version: 12.6.3(@types/node@22.15.21)
lodash-es: lodash-es:
specifier: ^4.17.21 specifier: ^4.17.21
version: 4.17.21 version: 4.17.21
@ -144,8 +147,8 @@ packages:
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@inquirer/checkbox@4.1.6': '@inquirer/checkbox@4.1.8':
resolution: {integrity: sha512-62u896rWCtKKE43soodq5e/QcRsA22I+7/4Ov7LESWnKRO6BVo2A1DFLDmXL9e28TB0CfHc3YtkbPm7iwajqkg==} resolution: {integrity: sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -153,8 +156,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/confirm@5.1.10': '@inquirer/confirm@5.1.12':
resolution: {integrity: sha512-FxbQ9giWxUWKUk2O5XZ6PduVnH2CZ/fmMKMBkH71MHJvWr7WL5AHKevhzF1L5uYWB2P548o1RzVxrNd3dpmk6g==} resolution: {integrity: sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -162,8 +165,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/core@10.1.11': '@inquirer/core@10.1.13':
resolution: {integrity: sha512-BXwI/MCqdtAhzNQlBEFE7CEflhPkl/BqvAuV/aK6lW3DClIfYVDWPP/kXuXHtBWC7/EEbNqd/1BGq2BGBBnuxw==} resolution: {integrity: sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -171,8 +174,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/editor@4.2.11': '@inquirer/editor@4.2.13':
resolution: {integrity: sha512-YoZr0lBnnLFPpfPSNsQ8IZyKxU47zPyVi9NLjCWtna52//M/xuL0PGPAxHxxYhdOhnvY2oBafoM+BI5w/JK7jw==} resolution: {integrity: sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -180,8 +183,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/expand@4.0.13': '@inquirer/expand@4.0.15':
resolution: {integrity: sha512-HgYNWuZLHX6q5y4hqKhwyytqAghmx35xikOGY3TcgNiElqXGPas24+UzNPOwGUZa5Dn32y25xJqVeUcGlTv+QQ==} resolution: {integrity: sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -189,12 +192,12 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/figures@1.0.11': '@inquirer/figures@1.0.12':
resolution: {integrity: sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==} resolution: {integrity: sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
'@inquirer/input@4.1.10': '@inquirer/input@4.1.12':
resolution: {integrity: sha512-kV3BVne3wJ+j6reYQUZi/UN9NZGZLxgc/tfyjeK3mrx1QI7RXPxGp21IUTv+iVHcbP4ytZALF8vCHoxyNSC6qg==} resolution: {integrity: sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -202,8 +205,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/number@3.0.13': '@inquirer/number@3.0.15':
resolution: {integrity: sha512-IrLezcg/GWKS8zpKDvnJ/YTflNJdG0qSFlUM/zNFsdi4UKW/CO+gaJpbMgQ20Q58vNKDJbEzC6IebdkprwL6ew==} resolution: {integrity: sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -211,8 +214,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/password@4.0.13': '@inquirer/password@4.0.15':
resolution: {integrity: sha512-NN0S/SmdhakqOTJhDwOpeBEEr8VdcYsjmZHDb0rblSh2FcbXQOr+2IApP7JG4WE3sxIdKytDn4ed3XYwtHxmJQ==} resolution: {integrity: sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -220,8 +223,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/prompts@7.5.1': '@inquirer/prompts@7.5.3':
resolution: {integrity: sha512-5AOrZPf2/GxZ+SDRZ5WFplCA2TAQgK3OYrXCYmJL5NaTu4ECcoWFlfUZuw7Es++6Njv7iu/8vpYJhuzxUH76Vg==} resolution: {integrity: sha512-8YL0WiV7J86hVAxrh3fE5mDCzcTDe1670unmJRz6ArDgN+DBK1a0+rbnNWp4DUB5rPMwqD5ZP6YHl9KK1mbZRg==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -229,8 +232,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/rawlist@4.1.1': '@inquirer/rawlist@4.1.3':
resolution: {integrity: sha512-VBUC0jPN2oaOq8+krwpo/mf3n/UryDUkKog3zi+oIi8/e5hykvdntgHUB9nhDM78RubiyR1ldIOfm5ue+2DeaQ==} resolution: {integrity: sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -238,8 +241,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/search@3.0.13': '@inquirer/search@3.0.15':
resolution: {integrity: sha512-9g89d2c5Izok/Gw/U7KPC3f9kfe5rA1AJ24xxNZG0st+vWekSk7tB9oE+dJv5JXd0ZSijomvW0KPMoBd8qbN4g==} resolution: {integrity: sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -247,8 +250,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/select@4.2.1': '@inquirer/select@4.2.3':
resolution: {integrity: sha512-gt1Kd5XZm+/ddemcT3m23IP8aD8rC9drRckWoP/1f7OL46Yy2FGi8DSmNjEjQKtPl6SV96Kmjbl6p713KXJ/Jg==} resolution: {integrity: sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -256,8 +259,8 @@ packages:
'@types/node': '@types/node':
optional: true optional: true
'@inquirer/type@3.0.6': '@inquirer/type@3.0.7':
resolution: {integrity: sha512-/mKVCtVpyBu3IDarv0G+59KC4stsD5mDsGpYh+GKs1NZT88Jh52+cuoA1AtLk2Q0r/quNl+1cSUyLRHBFeD0XA==} resolution: {integrity: sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -268,8 +271,8 @@ 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==}
'@kevisual/ai-center@0.0.4': '@kevisual/ai@0.0.5':
resolution: {integrity: sha512-pZw8sH7G0JjknHBHERV71INvmgyDS2JWyI2kb1a9FFHfRKg4TCv+RJIE505vKoAPjUd6+h6FHVHXvPXBdeTdxg==} resolution: {integrity: sha512-yEwwFIWJz2Enj1Uo3wDH9Aa0lf42ACAZdMFCXkqI44NyA/5C59tm1Ro4Bm46Z2bgT5IMtkIDfTxJHMvVtosJ7w==}
'@kevisual/cache@0.0.2': '@kevisual/cache@0.0.2':
resolution: {integrity: sha512-2Cl5KF2Gi27uLfhO6CdTMFnRzx9vYnqevAo7d9ab3rOaqTgF8tLeAXglXyRbaWW3WUbHU2XaOb4r98uUsqIQQw==} resolution: {integrity: sha512-2Cl5KF2Gi27uLfhO6CdTMFnRzx9vYnqevAo7d9ab3rOaqTgF8tLeAXglXyRbaWW3WUbHU2XaOb4r98uUsqIQQw==}
@ -280,9 +283,6 @@ packages:
'@kevisual/local-app-manager@0.1.20': '@kevisual/local-app-manager@0.1.20':
resolution: {integrity: sha512-PQf1wL9vQazTYvxhTqnhno3ZYccFGmgjkPEqKUY9IUpc2vP7srX73TGn/3e7S5dysO1urzgif4dciGe0yPbxAg==} resolution: {integrity: sha512-PQf1wL9vQazTYvxhTqnhno3ZYccFGmgjkPEqKUY9IUpc2vP7srX73TGn/3e7S5dysO1urzgif4dciGe0yPbxAg==}
'@kevisual/logger@0.0.3':
resolution: {integrity: sha512-8emqxg+ab62WAK6VY4FQqetXPSSVKFAjGctD1NDbdnxt7YWuI/PyuDltCpsVz+uvWpV1dO5OKZOoHU7ow59Omw==}
'@kevisual/logger@0.0.4': '@kevisual/logger@0.0.4':
resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==} resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==}
@ -291,8 +291,8 @@ packages:
peerDependencies: peerDependencies:
'@kevisual/query': ^0.0.17 '@kevisual/query': ^0.0.17
'@kevisual/query@0.0.18': '@kevisual/query@0.0.20':
resolution: {integrity: sha512-I2vHTu0I6AyD9PJyr+vxyp9jIJ6rd2EZqLVHTv/+zrVKVc2SS76Tg7aGNkmAFqqLSCB8kLLsmMGtSJU1Qb8VVg==} resolution: {integrity: sha512-IpkQd1Pz50namxDNytLJpyR2CRLJWKTkmk68Fiw6CEkTXQDkdyBh2kIOIf7+Geu8quai/J9io3KjWzt17AeYdg==}
'@kevisual/router@0.0.21': '@kevisual/router@0.0.21':
resolution: {integrity: sha512-XKTxbNO924cT18UOAGplWErZ+hMze8Y53F2jYCk18v4jsdsvjRho5uXXjJb6HSVsuITMtQR4R3rG0IcM3jkDKQ==} resolution: {integrity: sha512-XKTxbNO924cT18UOAGplWErZ+hMze8Y53F2jYCk18v4jsdsvjRho5uXXjJb6HSVsuITMtQR4R3rG0IcM3jkDKQ==}
@ -930,8 +930,8 @@ packages:
ini@1.3.8: ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
inquirer@12.6.1: inquirer@12.6.3:
resolution: {integrity: sha512-MGFnzHVS3l3oM3cy+LWkyR7UUtVEn3D5U41CZbEY34szToWoJAvaVtCTz1mxsEzZFk/HXWyCArn0HDgloTXMDw==} resolution: {integrity: sha512-eX9beYAjr1MqYsIjx1vAheXsRk1jbZRvHLcBu5nA9wX0rXR1IfCZLnVLp4Ym4mrhqmh7AuANwcdtgQ291fZDfQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@types/node': '>=18' '@types/node': '>=18'
@ -998,6 +998,10 @@ packages:
lodash@4.17.21: lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
lowdb@7.0.1:
resolution: {integrity: sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==}
engines: {node: '>=18'}
lru-cache@6.0.0: lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -1064,6 +1068,7 @@ packages:
node-domexception@1.0.0: node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'} engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
node-fetch@2.7.0: node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
@ -1296,6 +1301,10 @@ packages:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
steno@4.0.2:
resolution: {integrity: sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==}
engines: {node: '>=18'}
string-width@4.2.3: string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -1419,27 +1428,27 @@ snapshots:
'@babel/helper-validator-identifier@7.25.9': '@babel/helper-validator-identifier@7.25.9':
optional: true optional: true
'@inquirer/checkbox@4.1.6(@types/node@22.15.21)': '@inquirer/checkbox@4.1.8(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/figures': 1.0.11 '@inquirer/figures': 1.0.12
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/confirm@5.1.10(@types/node@22.15.21)': '@inquirer/confirm@5.1.12(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/core@10.1.11(@types/node@22.15.21)': '@inquirer/core@10.1.13(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/figures': 1.0.11 '@inquirer/figures': 1.0.12
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
cli-width: 4.1.0 cli-width: 4.1.0
mute-stream: 2.0.0 mute-stream: 2.0.0
@ -1449,97 +1458,97 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/editor@4.2.11(@types/node@22.15.21)': '@inquirer/editor@4.2.13(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
external-editor: 3.1.0 external-editor: 3.1.0
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/expand@4.0.13(@types/node@22.15.21)': '@inquirer/expand@4.0.15(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/figures@1.0.11': {} '@inquirer/figures@1.0.12': {}
'@inquirer/input@4.1.10(@types/node@22.15.21)': '@inquirer/input@4.1.12(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/number@3.0.13(@types/node@22.15.21)': '@inquirer/number@3.0.15(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/password@4.0.13(@types/node@22.15.21)': '@inquirer/password@4.0.15(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/prompts@7.5.1(@types/node@22.15.21)': '@inquirer/prompts@7.5.3(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/checkbox': 4.1.6(@types/node@22.15.21) '@inquirer/checkbox': 4.1.8(@types/node@22.15.21)
'@inquirer/confirm': 5.1.10(@types/node@22.15.21) '@inquirer/confirm': 5.1.12(@types/node@22.15.21)
'@inquirer/editor': 4.2.11(@types/node@22.15.21) '@inquirer/editor': 4.2.13(@types/node@22.15.21)
'@inquirer/expand': 4.0.13(@types/node@22.15.21) '@inquirer/expand': 4.0.15(@types/node@22.15.21)
'@inquirer/input': 4.1.10(@types/node@22.15.21) '@inquirer/input': 4.1.12(@types/node@22.15.21)
'@inquirer/number': 3.0.13(@types/node@22.15.21) '@inquirer/number': 3.0.15(@types/node@22.15.21)
'@inquirer/password': 4.0.13(@types/node@22.15.21) '@inquirer/password': 4.0.15(@types/node@22.15.21)
'@inquirer/rawlist': 4.1.1(@types/node@22.15.21) '@inquirer/rawlist': 4.1.3(@types/node@22.15.21)
'@inquirer/search': 3.0.13(@types/node@22.15.21) '@inquirer/search': 3.0.15(@types/node@22.15.21)
'@inquirer/select': 4.2.1(@types/node@22.15.21) '@inquirer/select': 4.2.3(@types/node@22.15.21)
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/rawlist@4.1.1(@types/node@22.15.21)': '@inquirer/rawlist@4.1.3(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/search@3.0.13(@types/node@22.15.21)': '@inquirer/search@3.0.15(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/figures': 1.0.11 '@inquirer/figures': 1.0.12
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/select@4.2.1(@types/node@22.15.21)': '@inquirer/select@4.2.3(@types/node@22.15.21)':
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/figures': 1.0.11 '@inquirer/figures': 1.0.12
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
yoctocolors-cjs: 2.1.2 yoctocolors-cjs: 2.1.2
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@inquirer/type@3.0.6(@types/node@22.15.21)': '@inquirer/type@3.0.7(@types/node@22.15.21)':
optionalDependencies: optionalDependencies:
'@types/node': 22.15.21 '@types/node': 22.15.21
'@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/sourcemap-codec@1.5.0': {}
'@kevisual/ai-center@0.0.4': '@kevisual/ai@0.0.5':
dependencies: dependencies:
'@kevisual/logger': 0.0.3 '@kevisual/logger': 0.0.4
'@kevisual/cache@0.0.2(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)': '@kevisual/cache@0.0.2(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)':
dependencies: dependencies:
@ -1565,21 +1574,19 @@ snapshots:
- supports-color - supports-color
- utf-8-validate - utf-8-validate
'@kevisual/logger@0.0.3': {}
'@kevisual/logger@0.0.4': {} '@kevisual/logger@0.0.4': {}
'@kevisual/query-login@0.0.6(@kevisual/query@0.0.18(@kevisual/ws@8.0.0))(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)': '@kevisual/query-login@0.0.6(@kevisual/query@0.0.20(@kevisual/ws@8.0.0))(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)':
dependencies: dependencies:
'@kevisual/cache': 0.0.2(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3) '@kevisual/cache': 0.0.2(rollup@4.40.0)(tslib@2.8.1)(typescript@5.8.3)
'@kevisual/query': 0.0.18(@kevisual/ws@8.0.0) '@kevisual/query': 0.0.20(@kevisual/ws@8.0.0)
dotenv: 16.5.0 dotenv: 16.5.0
transitivePeerDependencies: transitivePeerDependencies:
- rollup - rollup
- tslib - tslib
- typescript - typescript
'@kevisual/query@0.0.18(@kevisual/ws@8.0.0)': '@kevisual/query@0.0.20(@kevisual/ws@8.0.0)':
dependencies: dependencies:
openai: 4.99.0(@kevisual/ws@8.0.0) openai: 4.99.0(@kevisual/ws@8.0.0)
transitivePeerDependencies: transitivePeerDependencies:
@ -2165,11 +2172,11 @@ snapshots:
ini@1.3.8: {} ini@1.3.8: {}
inquirer@12.6.1(@types/node@22.15.21): inquirer@12.6.3(@types/node@22.15.21):
dependencies: dependencies:
'@inquirer/core': 10.1.11(@types/node@22.15.21) '@inquirer/core': 10.1.13(@types/node@22.15.21)
'@inquirer/prompts': 7.5.1(@types/node@22.15.21) '@inquirer/prompts': 7.5.3(@types/node@22.15.21)
'@inquirer/type': 3.0.6(@types/node@22.15.21) '@inquirer/type': 3.0.7(@types/node@22.15.21)
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
mute-stream: 2.0.0 mute-stream: 2.0.0
run-async: 3.0.0 run-async: 3.0.0
@ -2231,6 +2238,10 @@ snapshots:
lodash@4.17.21: {} lodash@4.17.21: {}
lowdb@7.0.1:
dependencies:
steno: 4.0.2
lru-cache@6.0.0: lru-cache@6.0.0:
dependencies: dependencies:
yallist: 4.0.0 yallist: 4.0.0
@ -2582,6 +2593,8 @@ snapshots:
statuses@2.0.1: {} statuses@2.0.1: {}
steno@4.0.2: {}
string-width@4.2.3: string-width@4.2.3:
dependencies: dependencies:
emoji-regex: 8.0.0 emoji-regex: 8.0.0

View File

@ -3,6 +3,7 @@ import { HttpsPem } from '@/module/assistant/https/sign.ts';
// import { AssistantConfig } from '@/module/assistant/index.ts'; // import { AssistantConfig } from '@/module/assistant/index.ts';
import { AssistantInit, parseHomeArg } from '@/services/init/index.ts'; import { AssistantInit, parseHomeArg } from '@/services/init/index.ts';
import { configDir as HomeConfigDir } from '@/module/assistant/config/index.ts'; import { configDir as HomeConfigDir } from '@/module/assistant/config/index.ts';
import { useContextKey } from '@kevisual/use-config/context';
const manualParse = parseHomeArg(HomeConfigDir); const manualParse = parseHomeArg(HomeConfigDir);
const _configDir = manualParse.configDir; const _configDir = manualParse.configDir;
@ -13,12 +14,13 @@ export const assistantConfig = new AssistantInit({
}); });
const httpsPem = new HttpsPem(assistantConfig); const httpsPem = new HttpsPem(assistantConfig);
export const app = new App({ export const app = useContextKey('app', () => {
serverOptions: { return new App({
path: '/client/router', serverOptions: {
httpType: 'https', path: '/client/router',
httpsCert: httpsPem.cert, httpType: 'https',
httpsKey: httpsPem.key, httpsCert: httpsPem.cert,
}, httpsKey: httpsPem.key,
},
});
}); });

View File

@ -1,4 +1,5 @@
import fs from 'node:fs'; import fs from 'node:fs';
import path from 'node:path';
/** /**
* *
@ -18,6 +19,22 @@ export const checkFileExists = (filePath: string, checkIsFile = false) => {
} }
}; };
/**
*
* @param filePath
* @param create
* @returns
*/
export const checkFileDir = (filePath: string, create = true) => {
const dirPath = path.dirname(filePath);
const exist = checkFileExists(dirPath);
if (create && !exist) {
fs.mkdirSync(dirPath, { recursive: true });
}
return exist;
};
export const createDir = (dirPath: string) => { export const createDir = (dirPath: string) => {
if (!checkFileExists(dirPath)) { if (!checkFileExists(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true }); fs.mkdirSync(dirPath, { recursive: true });

View File

@ -0,0 +1,47 @@
import path from 'node:path';
import fs from 'node:fs';
import { checkFileDir, checkFileExists } from '../file/index.js';
type SimpleObject = {
[key: string]: any;
};
type StorageOptions = {
dir?: string;
};
export class StorageCore {
storagePath: string;
constructor(opts?: StorageOptions) {
this.storagePath = opts?.dir || process.cwd();
}
async getData<T = SimpleObject>(key: string = 'data.json', fileDirectory?: string): Promise<T | null> {
const storageFilePath = path.join(this.storagePath, fileDirectory, key);
if (!checkFileExists(storageFilePath)) {
return null;
}
try {
const data = fs.readFileSync(storageFilePath, 'utf-8');
return JSON.parse(data) as T;
} catch (error) {
console.error(`Error reading data from ${storageFilePath}:`, error);
return null;
}
}
async setData<T = SimpleObject>(data: T, key: string = 'data.json', fileDirectory?: string): Promise<void> {
const storageFilePath = path.join(this.storagePath, fileDirectory, key);
try {
checkFileDir(storageFilePath, true);
fs.writeFileSync(storageFilePath, JSON.stringify(data, null, 2), 'utf-8');
} catch (error) {
console.error(`Error writing data to ${storageFilePath}:`, error);
}
}
async deleteData(key: string = 'data.json', fileDirectory?: string): Promise<void> {
const storageFilePath = path.join(this.storagePath, fileDirectory, key);
try {
if (checkFileExists(storageFilePath)) {
fs.unlinkSync(storageFilePath);
}
} catch (error) {
console.error(`Error deleting data from ${storageFilePath}:`, error);
}
}
}

View File

@ -0,0 +1,7 @@
import { Query } from '@kevisual/query';
export const query = new Query();
export const clientQuery = new Query({ url: '/client/router' });
export { QueryUtil } from '@kevisual/router/define';

View File

@ -0,0 +1,42 @@
import { QueryUtil } from '@/query/index.ts';
type Message = {
role?: 'user' | 'assistant' | 'system' | 'tool';
content?: string;
name?: string;
};
export type PostChat = {
messages?: Message[];
model?: string;
group?: string;
user?: string;
};
export type ChatDataOpts = {
id?: string;
title?: string;
messages?: any[];
data?: any;
type?: 'temp' | 'keep' | string;
};
export type ChatOpts = {
username: string;
model: string;
/**
*
*/
getFull?: boolean;
group: string;
/**
* openai的参数
*/
options?: any;
};
export const appDefine = QueryUtil.create({
chat: {
path: 'ai',
key: 'chat',
description: '与 AI 进行对话, 调用 GPT 的AI 服务,生成结果,并返回。',
},
});

View File

@ -0,0 +1,101 @@
import { appDefine } from './defines/ai.ts';
import { PostChat, ChatOpts, ChatDataOpts } from './defines/ai.ts';
import { BaseQuery, DataOpts, Query } from '@kevisual/query/query';
export { appDefine };
export class QueryApp<T extends Query = Query> extends BaseQuery<T, typeof appDefine> {
constructor(opts?: { query: T }) {
super({
...opts,
query: opts?.query!,
queryDefine: appDefine,
});
}
/**
* AI , GPT AI
* @param data
* @param opts
* @returns
*/
postChat(data: PostChat, opts?: DataOpts) {
return this.chain('chat').post(data, opts);
}
/**
*
* @param opts
* @returns
*/
getModelList(data?: { usernames?: string[] }, opts?: DataOpts) {
return this.query.post(
{
path: 'ai',
key: 'get-model-list',
data,
},
opts,
);
}
/**
*
* @param data
* @param chatOpts
* @param opts
* @returns
*/
chat(data: ChatDataOpts, chatOpts: ChatOpts, opts?: DataOpts) {
const { username, model, group, getFull = true } = chatOpts;
if (!username || !model || !group) {
throw new Error('username, model, group is required');
}
return this.query.post(
{
path: 'ai',
key: 'chat',
...chatOpts,
getFull,
data,
},
opts,
);
}
clearConfigCache(opts?: DataOpts) {
return this.query.post(
{
path: 'ai',
key: 'clear-cache',
},
opts,
);
}
/**
* 使
* @param opts
* @returns
*/
getChatUsage(opts?: DataOpts) {
return this.query.post(
{
path: 'ai',
key: 'get-chat-usage',
},
opts,
);
}
/**
*
* @param opts
* @returns
*/
clearSelfUsage(opts?: DataOpts) {
return this.query.post(
{
path: 'ai',
key: 'clear-chat-limit',
},
opts,
);
}
}

View File

@ -0,0 +1,204 @@
export interface Cache {
/**
* @update
*/
get(key: string): Promise<any>;
/**
* @update
*/
set(key: string, value: any): Promise<any>;
/**
* @update
*/
del(): Promise<void>;
/**
*
*/
init?: () => Promise<any>;
}
type User = {
avatar?: string;
description?: string;
id?: string;
needChangePassword?: boolean;
orgs?: string[];
type?: string;
username?: string;
};
export type CacheLoginUser = {
user?: User;
id?: string;
accessToken?: string;
refreshToken?: string;
};
type CacheLogin = {
loginUsers: CacheLoginUser[];
} & CacheLoginUser;
export type CacheStore<T = Cache> = {
name: string;
/**
*
* @important init
*/
cacheData: CacheLogin;
/**
* cache, init
*/
cache: T;
/**
*
*/
setLoginUser(user: CacheLoginUser): Promise<void>;
/**
*
*/
getCurrentUser(): Promise<User>;
/**
*
*/
getCurrentUserList(): Promise<CacheLoginUser[]>;
/**
* refreshToken
*/
getRefreshToken(): Promise<string>;
/**
* accessToken
*/
getAccessToken(): Promise<string>;
/**
*
*/
clearCurrentUser(): Promise<void>;
/**
*
*/
clearAll(): Promise<void>;
getValue(): Promise<CacheLogin>;
setValue(value: CacheLogin): Promise<CacheLogin>;
delValue(): Promise<void>;
init(): Promise<any>;
};
export type LoginCacheStoreOpts = {
name: string;
cache: Cache;
};
export class LoginCacheStore implements CacheStore<any> {
cache: Cache;
name: string;
cacheData: CacheLogin;
constructor(opts: LoginCacheStoreOpts) {
if (!opts.cache) {
throw new Error('cache is required');
}
// @ts-ignore
this.cache = opts.cache;
this.cacheData = {
loginUsers: [],
user: undefined,
id: undefined,
accessToken: undefined,
refreshToken: undefined,
};
this.name = opts.name;
}
/**
*
* @param key
* @param value
* @returns
*/
async setValue(value: CacheLogin) {
await this.cache.set(this.name, value);
this.cacheData = value;
return value;
}
/**
*
*/
async delValue() {
await this.cache.del();
}
getValue(): Promise<CacheLogin> {
return this.cache.get(this.name);
}
/**
* ,
*/
async init() {
const defaultData = {
loginUsers: [],
user: null,
id: null,
accessToken: null,
refreshToken: null,
};
if (this.cache.init) {
try {
const cacheData = await this.cache.init();
this.cacheData = cacheData || defaultData;
} catch (error) {
console.log('cacheInit error', error);
}
} else {
this.cacheData = (await this.getValue()) || defaultData;
}
}
/**
*
* @param user
*/
async setLoginUser(user: CacheLoginUser) {
const has = this.cacheData.loginUsers.find((u) => u.id === user.id);
if (has) {
this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== user.id);
}
this.cacheData.loginUsers.push(user);
this.cacheData.user = user.user;
this.cacheData.id = user.id;
this.cacheData.accessToken = user.accessToken;
this.cacheData.refreshToken = user.refreshToken;
await this.setValue(this.cacheData);
}
getCurrentUser(): Promise<CacheLoginUser> {
const cacheData = this.cacheData;
return Promise.resolve(cacheData.user!);
}
getCurrentUserList(): Promise<CacheLoginUser[]> {
return Promise.resolve(this.cacheData.loginUsers.filter((u) => u?.id));
}
getRefreshToken(): Promise<string> {
const cacheData = this.cacheData;
return Promise.resolve(cacheData.refreshToken || '');
}
getAccessToken(): Promise<string> {
const cacheData = this.cacheData;
return Promise.resolve(cacheData.accessToken || '');
}
async clearCurrentUser() {
const user = await this.getCurrentUser();
const has = this.cacheData.loginUsers.find((u) => u.id === user.id);
if (has) {
this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== user.id);
}
this.cacheData.user = undefined;
this.cacheData.id = undefined;
this.cacheData.accessToken = undefined;
this.cacheData.refreshToken = undefined;
await this.setValue(this.cacheData);
}
async clearAll() {
this.cacheData.loginUsers = [];
this.cacheData.user = undefined;
this.cacheData.id = undefined;
this.cacheData.accessToken = undefined;
this.cacheData.refreshToken = undefined;
await this.setValue(this.cacheData);
}
}

View File

@ -0,0 +1,132 @@
import { Cache } from './login-cache.ts';
import { homedir } from 'node:os';
import { join, dirname } from 'node:path';
import fs from 'node:fs';
import { readFileSync, writeFileSync, accessSync } from 'node:fs';
import { readFile, writeFile, unlink, mkdir } from 'node:fs/promises';
export const fileExists = async (
filePath: string,
{ createIfNotExists = true, isFile = true, isDir = false }: { createIfNotExists?: boolean; isFile?: boolean; isDir?: boolean } = {},
) => {
try {
accessSync(filePath, fs.constants.F_OK);
return true;
} catch (error) {
if (createIfNotExists && isDir) {
await mkdir(filePath, { recursive: true });
return true;
} else if (createIfNotExists && isFile) {
await mkdir(dirname(filePath), { recursive: true });
return false;
}
return false;
}
};
export const readConfigFile = (filePath: string) => {
try {
const data = readFileSync(filePath, 'utf-8');
const jsonData = JSON.parse(data);
return jsonData;
} catch (error) {
return {};
}
};
export const writeConfigFile = (filePath: string, data: any) => {
writeFileSync(filePath, JSON.stringify(data, null, 2));
};
export const getHostName = () => {
const configDir = join(homedir(), '.config', 'envision');
const configFile = join(configDir, 'config.json');
const config = readConfigFile(configFile);
const baseURL = config.baseURL || 'https://kevisual.cn';
const hostname = new URL(baseURL).hostname;
return hostname;
};
export class StorageNode implements Storage {
cacheData: any;
filePath: string;
constructor() {
this.cacheData = {};
const configDir = join(homedir(), '.config', 'envision');
const hostname = getHostName();
this.filePath = join(configDir, 'config', `${hostname}-storage.json`);
fileExists(this.filePath, { isFile: true });
}
async loadCache() {
const filePath = this.filePath;
try {
const data = await readConfigFile(filePath);
this.cacheData = data;
} catch (error) {
this.cacheData = {};
await writeFile(filePath, JSON.stringify(this.cacheData, null, 2));
}
}
get length() {
return Object.keys(this.cacheData).length;
}
getItem(key: string) {
return this.cacheData[key];
}
setItem(key: string, value: any) {
this.cacheData[key] = value;
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
}
removeItem(key: string) {
delete this.cacheData[key];
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
}
clear() {
this.cacheData = {};
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
}
key(index: number) {
return Object.keys(this.cacheData)[index];
}
}
export class LoginNodeCache implements Cache {
filepath: string;
constructor(filepath?: string) {
this.filepath = filepath || join(homedir(), '.config', 'envision', 'config', `${getHostName()}-login.json`);
fileExists(this.filepath, { isFile: true });
}
async get(_key: string) {
try {
const filePath = this.filepath;
const data = readConfigFile(filePath);
return data;
} catch (error) {
console.log('get error', error);
return {};
}
}
async set(_key: string, value: any) {
try {
const data = readConfigFile(this.filepath);
const newData = { ...data, ...value };
writeConfigFile(this.filepath, newData);
} catch (error) {
console.log('set error', error);
}
}
async del() {
await unlink(this.filepath);
}
async loadCache(filePath: string) {
try {
const data = await readFile(filePath, 'utf-8');
const jsonData = JSON.parse(data);
return jsonData;
} catch (error) {
// console.log('loadCache error', error);
console.log('create new cache file:', filePath);
const defaultData = { loginUsers: [] };
writeConfigFile(filePath, defaultData);
return defaultData;
}
}
async init() {
return await this.loadCache(this.filepath);
}
}

View File

@ -0,0 +1,12 @@
import { QueryLogin, QueryLoginOpts } from './query-login.ts';
import { MyCache } from '@kevisual/cache';
type QueryLoginNodeOptsWithoutCache = Omit<QueryLoginOpts, 'cache'>;
export class QueryLoginBrowser extends QueryLogin {
constructor(opts: QueryLoginNodeOptsWithoutCache) {
super({
...opts,
cache: new MyCache('login'),
});
}
}

View File

@ -0,0 +1,14 @@
import { QueryLogin, QueryLoginOpts } from './query-login.ts';
import { LoginNodeCache, StorageNode } from './login-node-cache.ts';
type QueryLoginNodeOptsWithoutCache = Omit<QueryLoginOpts, 'cache'>;
export const storage = new StorageNode();
await storage.loadCache();
export class QueryLoginNode extends QueryLogin {
constructor(opts: QueryLoginNodeOptsWithoutCache) {
super({
...opts,
storage,
cache: new LoginNodeCache(),
});
}
}

View File

@ -0,0 +1,434 @@
import { Query, BaseQuery } from '@kevisual/query';
import type { Result, DataOpts } from '@kevisual/query/query';
import { setBaseResponse } from '@kevisual/query/query';
import { LoginCacheStore, CacheStore } from './login-cache.ts';
import { Cache } from './login-cache.ts';
export type QueryLoginOpts = {
query?: Query;
isBrowser?: boolean;
onLoad?: () => void;
storage?: Storage;
cache: Cache;
};
export type QueryLoginData = {
username?: string;
password: string;
email?: string;
};
export type QueryLoginResult = {
accessToken: string;
refreshToken: string;
};
export class QueryLogin extends BaseQuery {
/**
* query login cache cache的包裹模块
*/
cacheStore: CacheStore;
isBrowser: boolean;
load?: boolean;
storage: Storage;
onLoad?: () => void;
constructor(opts?: QueryLoginOpts) {
super({
query: opts?.query || new Query(),
});
this.cacheStore = new LoginCacheStore({ name: 'login', cache: opts?.cache! });
this.isBrowser = opts?.isBrowser ?? true;
this.init();
this.onLoad = opts?.onLoad;
this.storage = opts?.storage || localStorage;
}
setQuery(query: Query) {
this.query = query;
}
private async init() {
await this.cacheStore.init();
this.load = true;
this.onLoad?.();
}
async post<T = any>(data: any, opts?: DataOpts) {
try {
return this.query.post<T>({ path: 'user', ...data }, opts);
} catch (error) {
console.log('error', error);
return {
code: 400,
} as any;
}
}
/**
* ,
* @param data
* @returns
*/
async login(data: QueryLoginData) {
const res = await this.post<QueryLoginResult>({ key: 'login', ...data });
if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {};
this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken });
}
return res;
}
/**
*
* @param data
* @returns
*/
async loginByCode(data: { phone: string; code: string }) {
const res = await this.post<QueryLoginResult>({ path: 'sms', key: 'login', data });
if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {};
this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken });
}
return res;
}
/**
* token
* @param token
*/
async setLoginToken(token: { accessToken: string; refreshToken: string }) {
const { accessToken, refreshToken } = token;
this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken });
}
async loginByWechat(data: { code: string }) {
const res = await this.post<QueryLoginResult>({ path: 'wx', key: 'open-login', code: data.code });
if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {};
this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken });
}
return res;
}
/**
* onSuccessonError
* @param param0
*/
async checkWechat({ onSuccess, onError }: { onSuccess?: (res: QueryLoginResult) => void; onError?: (res: any) => void }) {
const url = new URL(window.location.href);
const code = url.searchParams.get('code');
const state = url.searchParams.get('state');
if (code && state) {
const res = await this.loginByWechat({ code });
if (res.code === 200) {
onSuccess?.(res.data);
} else {
onError?.(res);
}
}
}
/**
*
* @param param0
*/
async beforeSetLoginUser({ accessToken, refreshToken, check401 }: { accessToken?: string; refreshToken?: string; check401?: boolean }) {
if (accessToken && refreshToken) {
const resUser = await this.getMe(accessToken, check401);
if (resUser.code === 200) {
const user = resUser.data;
if (user) {
this.cacheStore.setLoginUser({
user,
id: user.id,
accessToken,
refreshToken,
});
} else {
console.error('登录失败');
}
}
}
}
/**
* token
* @param refreshToken
* @returns
*/
async queryRefreshToken(refreshToken?: string) {
const _refreshToken = refreshToken || this.cacheStore.getRefreshToken();
let data = { refreshToken: _refreshToken };
if (!_refreshToken) {
await this.cacheStore.clearCurrentUser();
return {
code: 401,
message: '请先登录',
data: {} as any,
};
}
return this.post(
{ key: 'refreshToken', data },
{
afterResponse: async (response, ctx) => {
setBaseResponse(response);
return response as any;
},
},
);
}
/**
* 401token, refreshToken存在token, 401
* 使run401Action 使 afterCheck401ToRefreshToken
* @param response
* @param ctx
* @param refetch
* @returns
*/
async afterCheck401ToRefreshToken(response: Result, ctx?: { req?: any; res?: any; fetch?: any }, refetch?: boolean) {
const that = this;
if (response?.code === 401) {
const hasRefreshToken = await that.cacheStore.getRefreshToken();
if (hasRefreshToken) {
const res = await that.queryRefreshToken(hasRefreshToken);
if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {};
that.storage.setItem('token', accessToken || '');
await that.beforeSetLoginUser({ accessToken, refreshToken, check401: false });
if (refetch && ctx && ctx.req && ctx.req.url && ctx.fetch) {
await new Promise((resolve) => setTimeout(resolve, 1500));
const url = ctx.req?.url;
const body = ctx.req?.body;
const headers = ctx.req?.headers;
const res = await ctx.fetch(url, {
method: 'POST',
body: body,
headers: { ...headers, Authorization: `Bearer ${accessToken}` },
});
setBaseResponse(res);
return res;
}
} else {
that.storage.removeItem('token');
await that.cacheStore.clearCurrentUser();
}
return res;
}
}
return response as any;
}
/**
* 401 401token, refreshToken不存在401
* refetch , bug使
* TODO:
* @param response
* @param ctx
* @param opts
* @returns
*/
async run401Action(
response: Result,
ctx?: { req?: any; res?: any; fetch?: any },
opts?: {
/**
* , bug使
*/
refetch?: boolean;
/**
* check之后的回调
*/
afterCheck?: (res: Result) => any;
/**
* 401 401
*/
afterAlso401?: (res: Result) => any;
},
) {
const that = this;
const refetch = opts?.refetch ?? false;
if (response?.code === 401) {
if (that.query.stop === true) {
return { code: 500, success: false, message: 'refresh token loading...' };
}
that.query.stop = true;
const res = await that.afterCheck401ToRefreshToken(response, ctx, refetch);
that.query.stop = false;
opts?.afterCheck?.(res);
if (res.code === 401) {
opts?.afterAlso401?.(res);
}
return res;
} else {
return response as any;
}
}
/**
*
* @param token
* @returns
*/
async getMe(token?: string, check401: boolean = true) {
const _token = token || this.storage.getItem('token');
const that = this;
return that.post(
{ key: 'me' },
{
beforeRequest: async (config) => {
if (config.headers) {
config.headers['Authorization'] = `Bearer ${_token}`;
}
if (!_token) {
return false;
}
return config;
},
afterResponse: async (response, ctx) => {
if (response?.code === 401 && check401 && !token) {
return await that.afterCheck401ToRefreshToken(response, ctx);
}
return response as any;
},
},
);
}
/**
* null
* @returns
*/
async checkLocalUser() {
const user = await this.cacheStore.getCurrentUser();
if (user) {
return user;
}
return null;
}
/**
* token是否存在
* @returns
*/
async checkLocalToken() {
const token = this.storage.getItem('token');
return !!token;
}
/**
*
* @returns
*/
async getToken() {
const token = this.storage.getItem('token');
return token || '';
}
async beforeRequest(opts: any = {}) {
const token = this.storage.getItem('token');
if (token) {
opts.headers = { ...opts.headers, Authorization: `Bearer ${token}` };
}
return opts;
}
/**
* 使switchUser
* @param username
* @returns
*/
private async postSwitchUser(username: string) {
return this.post({ key: 'switchCheck', data: { username } });
}
/**
*
* @param username
* @returns
*/
async switchUser(username: string) {
const localUserList = await this.cacheStore.getCurrentUserList();
const user = localUserList.find((userItem) => userItem.user!.username === username);
if (user) {
this.storage.setItem('token', user.accessToken || '');
await this.beforeSetLoginUser({ accessToken: user.accessToken, refreshToken: user.refreshToken });
return {
code: 200,
data: {
accessToken: user.accessToken,
refreshToken: user.refreshToken,
},
success: true,
message: '切换用户成功',
};
}
const res = await this.postSwitchUser(username);
if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {};
this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken });
}
return res;
}
/**
* 退token
* @returns
*/
async logout() {
this.storage.removeItem('token');
const users = await this.cacheStore.getCurrentUserList();
const tokens = users
.map((user) => {
return user?.accessToken;
})
.filter(Boolean);
this.cacheStore.delValue();
return this.post<Result>({ key: 'logout', data: { tokens } });
}
/**
*
* @param username
* @returns
*/
async hasUser(username: string) {
const that = this;
return this.post<Result>(
{
path: 'org',
key: 'hasUser',
data: {
username,
},
},
{
afterResponse: async (response, ctx) => {
if (response?.code === 401) {
const res = await that.afterCheck401ToRefreshToken(response, ctx, true);
return res;
}
return response as any;
},
},
);
}
/**
*
* @param token
* @returns
*/
async checkLoginStatus(token: string) {
const res = await this.post({
path: 'user',
key: 'checkLoginStatus',
loginToken: token,
});
if (res.code === 200) {
const accessToken = res.data?.accessToken;
this.storage.setItem('token', accessToken || '');
await this.beforeSetLoginUser({ accessToken, refreshToken: res.data?.refreshToken });
return res;
}
return false;
}
/**
* 使web登录,url地址, MD5和jsonwebtoken
*/
loginWithWeb(baseURL: string, { MD5, jsonwebtoken }: { MD5: any; jsonwebtoken: any }) {
const randomId = Math.random().toString(36).substring(2, 15);
const timestamp = Date.now();
const tokenSecret = 'xiao' + randomId;
const sign = MD5(`${tokenSecret}${timestamp}`).toString();
const token = jsonwebtoken.sign({ randomId, timestamp, sign }, tokenSecret, {
// 10分钟过期
expiresIn: 60 * 10, // 10分钟
});
const url = `${baseURL}/api/router?path=user&key=webLogin&p&loginToken=${token}&sign=${sign}&randomId=${randomId}`;
return { url, token, tokenSecret };
}
}

View File

@ -0,0 +1,35 @@
import { useContextKey } from '@kevisual/use-config/context';
// import { appDefine } from '@/query/query-ai/defines/ai.ts';
import { ProviderManager } from '@kevisual/ai';
import { App } from '@kevisual/router';
import { assistantConfig } from '@/app.ts';
const app = useContextKey<App>('app');
app
.route({
path: 'ai',
key: 'chat',
middleware: ['auth'],
})
.define(async (ctx) => {
const { messages = [], username, group, question, chatOpts = {} } = ctx.query;
// if (!username || !model || !group) {
// return ctx.throw(4001, 'username, model, group is required');
// }
if (messages.length === 0 && question) {
messages.push({
role: 'user',
content: question,
});
}
const as = assistantConfig.getCacheAssistantConfig();
const { provider, apiKey, model } = as.ai || {};
const pm = new ProviderManager({
provider: provider,
apiKey: apiKey,
model: model,
});
const result = await pm.provider.chat(messages, chatOpts);
ctx.body = result;
})
.addTo(app);

View File

@ -2,6 +2,8 @@ import { Query } from '@kevisual/query';
import { app, assistantConfig } from '../app.ts'; import { app, assistantConfig } from '../app.ts';
import './config/index.ts'; import './config/index.ts';
import './shop-install/index.ts'; import './shop-install/index.ts';
import './ai/index.ts';
import os from 'node:os'; import os from 'node:os';
app app

655
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff