mirror of
https://github.com/musistudio/claude-code-router.git
synced 2026-02-02 23:00:50 +08:00
Merge pull request #877 from TonyGeez/feature/add-model-selector-command
Feature/add model selector command
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,4 +4,5 @@ log.txt
|
|||||||
.idea
|
.idea
|
||||||
dist
|
dist
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode
|
.vscode
|
||||||
|
tsconfig.tsbuildinfo
|
||||||
|
|||||||
@@ -21,9 +21,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/static": "^8.2.0",
|
"@fastify/static": "^8.2.0",
|
||||||
"@musistudio/llms": "^1.0.36",
|
"@musistudio/llms": "^1.0.36",
|
||||||
|
"@inquirer/prompts": "^5.0.0",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"find-process": "^2.0.0",
|
"find-process": "^2.0.0",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
|
"lru-cache": "^11.2.2",
|
||||||
"minimist": "^1.2.8",
|
"minimist": "^1.2.8",
|
||||||
"openurl": "^1.1.1",
|
"openurl": "^1.1.1",
|
||||||
"rotating-file-stream": "^3.2.7",
|
"rotating-file-stream": "^3.2.7",
|
||||||
|
|||||||
244
pnpm-lock.yaml
generated
244
pnpm-lock.yaml
generated
@@ -11,6 +11,9 @@ importers:
|
|||||||
'@fastify/static':
|
'@fastify/static':
|
||||||
specifier: ^8.2.0
|
specifier: ^8.2.0
|
||||||
version: 8.2.0
|
version: 8.2.0
|
||||||
|
'@inquirer/prompts':
|
||||||
|
specifier: ^5.0.0
|
||||||
|
version: 5.5.0
|
||||||
'@musistudio/llms':
|
'@musistudio/llms':
|
||||||
specifier: ^1.0.36
|
specifier: ^1.0.36
|
||||||
version: 1.0.36(ws@8.18.3)
|
version: 1.0.36(ws@8.18.3)
|
||||||
@@ -23,6 +26,9 @@ importers:
|
|||||||
json5:
|
json5:
|
||||||
specifier: ^2.2.3
|
specifier: ^2.2.3
|
||||||
version: 2.2.3
|
version: 2.2.3
|
||||||
|
lru-cache:
|
||||||
|
specifier: ^11.2.2
|
||||||
|
version: 11.2.2
|
||||||
minimist:
|
minimist:
|
||||||
specifier: ^1.2.8
|
specifier: ^1.2.8
|
||||||
version: 1.2.8
|
version: 1.2.8
|
||||||
@@ -259,6 +265,66 @@ packages:
|
|||||||
'@modelcontextprotocol/sdk':
|
'@modelcontextprotocol/sdk':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@inquirer/checkbox@2.5.0':
|
||||||
|
resolution: {integrity: sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/confirm@3.2.0':
|
||||||
|
resolution: {integrity: sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/core@9.2.1':
|
||||||
|
resolution: {integrity: sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/editor@2.2.0':
|
||||||
|
resolution: {integrity: sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/expand@2.3.0':
|
||||||
|
resolution: {integrity: sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/figures@1.0.13':
|
||||||
|
resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/input@2.3.0':
|
||||||
|
resolution: {integrity: sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/number@1.1.0':
|
||||||
|
resolution: {integrity: sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/password@2.2.0':
|
||||||
|
resolution: {integrity: sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/prompts@5.5.0':
|
||||||
|
resolution: {integrity: sha512-BHDeL0catgHdcHbSFFUddNzvx/imzJMft+tWDPwTm3hfu8/tApk1HrooNngB2Mb4qY+KaRWF+iZqoVUPeslEog==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/rawlist@2.3.0':
|
||||||
|
resolution: {integrity: sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/search@1.1.0':
|
||||||
|
resolution: {integrity: sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/select@2.5.0':
|
||||||
|
resolution: {integrity: sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/type@1.5.5':
|
||||||
|
resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@inquirer/type@2.0.0':
|
||||||
|
resolution: {integrity: sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@isaacs/balanced-match@4.0.1':
|
'@isaacs/balanced-match@4.0.1':
|
||||||
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
|
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
|
||||||
engines: {node: 20 || >=22}
|
engines: {node: 20 || >=22}
|
||||||
@@ -293,6 +359,9 @@ packages:
|
|||||||
'@types/node@24.7.0':
|
'@types/node@24.7.0':
|
||||||
resolution: {integrity: sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==}
|
resolution: {integrity: sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==}
|
||||||
|
|
||||||
|
'@types/wrap-ansi@3.0.0':
|
||||||
|
resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==}
|
||||||
|
|
||||||
abstract-logging@2.0.1:
|
abstract-logging@2.0.1:
|
||||||
resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
|
resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
|
||||||
|
|
||||||
@@ -311,6 +380,10 @@ packages:
|
|||||||
ajv@8.17.1:
|
ajv@8.17.1:
|
||||||
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||||
|
|
||||||
|
ansi-escapes@4.3.2:
|
||||||
|
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
ansi-regex@5.0.1:
|
ansi-regex@5.0.1:
|
||||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -351,6 +424,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
chardet@0.7.0:
|
||||||
|
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
||||||
|
|
||||||
|
cli-width@4.1.0:
|
||||||
|
resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
engines: {node: '>=7.0.0'}
|
engines: {node: '>=7.0.0'}
|
||||||
@@ -433,6 +513,10 @@ packages:
|
|||||||
extend@3.0.2:
|
extend@3.0.2:
|
||||||
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
||||||
|
|
||||||
|
external-editor@3.1.0:
|
||||||
|
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
fast-decode-uri-component@1.0.1:
|
fast-decode-uri-component@1.0.1:
|
||||||
resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
|
resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
|
||||||
|
|
||||||
@@ -557,6 +641,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
|
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
|
iconv-lite@0.4.24:
|
||||||
|
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
inherits@2.0.4:
|
inherits@2.0.4:
|
||||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||||
|
|
||||||
@@ -665,6 +753,10 @@ packages:
|
|||||||
ms@2.1.3:
|
ms@2.1.3:
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
|
mute-stream@1.0.0:
|
||||||
|
resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==}
|
||||||
|
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||||
|
|
||||||
nice-try@1.0.5:
|
nice-try@1.0.5:
|
||||||
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
||||||
|
|
||||||
@@ -712,6 +804,10 @@ packages:
|
|||||||
openurl@1.1.1:
|
openurl@1.1.1:
|
||||||
resolution: {integrity: sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==}
|
resolution: {integrity: sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==}
|
||||||
|
|
||||||
|
os-tmpdir@1.0.2:
|
||||||
|
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
p-finally@1.0.0:
|
p-finally@1.0.0:
|
||||||
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
|
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -911,6 +1007,10 @@ packages:
|
|||||||
tiktoken@1.0.22:
|
tiktoken@1.0.22:
|
||||||
resolution: {integrity: sha512-PKvy1rVF1RibfF3JlXBSP0Jrcw2uq3yXdgcEXtKTYn3QJ/cBRBHDnrJ5jHky+MENZ6DIPwNUGWpkVx+7joCpNA==}
|
resolution: {integrity: sha512-PKvy1rVF1RibfF3JlXBSP0Jrcw2uq3yXdgcEXtKTYn3QJ/cBRBHDnrJ5jHky+MENZ6DIPwNUGWpkVx+7joCpNA==}
|
||||||
|
|
||||||
|
tmp@0.0.33:
|
||||||
|
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
|
||||||
|
engines: {node: '>=0.6.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'}
|
||||||
@@ -965,6 +1065,10 @@ packages:
|
|||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
wrap-ansi@6.2.0:
|
||||||
|
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
wrap-ansi@7.0.0:
|
wrap-ansi@7.0.0:
|
||||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -988,6 +1092,10 @@ packages:
|
|||||||
utf-8-validate:
|
utf-8-validate:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
yoctocolors-cjs@2.1.3:
|
||||||
|
resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
'@anthropic-ai/sdk@0.54.0': {}
|
'@anthropic-ai/sdk@0.54.0': {}
|
||||||
@@ -1127,6 +1235,106 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
|
'@inquirer/checkbox@2.5.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 9.2.1
|
||||||
|
'@inquirer/figures': 1.0.13
|
||||||
|
'@inquirer/type': 1.5.5
|
||||||
|
ansi-escapes: 4.3.2
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
|
||||||
|
'@inquirer/confirm@3.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 9.2.1
|
||||||
|
'@inquirer/type': 1.5.5
|
||||||
|
|
||||||
|
'@inquirer/core@9.2.1':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/figures': 1.0.13
|
||||||
|
'@inquirer/type': 2.0.0
|
||||||
|
'@types/mute-stream': 0.0.4
|
||||||
|
'@types/node': 22.18.8
|
||||||
|
'@types/wrap-ansi': 3.0.0
|
||||||
|
ansi-escapes: 4.3.2
|
||||||
|
cli-width: 4.1.0
|
||||||
|
mute-stream: 1.0.0
|
||||||
|
signal-exit: 4.1.0
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
wrap-ansi: 6.2.0
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
|
||||||
|
'@inquirer/editor@2.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 9.2.1
|
||||||
|
'@inquirer/type': 1.5.5
|
||||||
|
external-editor: 3.1.0
|
||||||
|
|
||||||
|
'@inquirer/expand@2.3.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 9.2.1
|
||||||
|
'@inquirer/type': 1.5.5
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
|
||||||
|
'@inquirer/figures@1.0.13': {}
|
||||||
|
|
||||||
|
'@inquirer/input@2.3.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 9.2.1
|
||||||
|
'@inquirer/type': 1.5.5
|
||||||
|
|
||||||
|
'@inquirer/number@1.1.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 9.2.1
|
||||||
|
'@inquirer/type': 1.5.5
|
||||||
|
|
||||||
|
'@inquirer/password@2.2.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 9.2.1
|
||||||
|
'@inquirer/type': 1.5.5
|
||||||
|
ansi-escapes: 4.3.2
|
||||||
|
|
||||||
|
'@inquirer/prompts@5.5.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/checkbox': 2.5.0
|
||||||
|
'@inquirer/confirm': 3.2.0
|
||||||
|
'@inquirer/editor': 2.2.0
|
||||||
|
'@inquirer/expand': 2.3.0
|
||||||
|
'@inquirer/input': 2.3.0
|
||||||
|
'@inquirer/number': 1.1.0
|
||||||
|
'@inquirer/password': 2.2.0
|
||||||
|
'@inquirer/rawlist': 2.3.0
|
||||||
|
'@inquirer/search': 1.1.0
|
||||||
|
'@inquirer/select': 2.5.0
|
||||||
|
|
||||||
|
'@inquirer/rawlist@2.3.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 9.2.1
|
||||||
|
'@inquirer/type': 1.5.5
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
|
||||||
|
'@inquirer/search@1.1.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 9.2.1
|
||||||
|
'@inquirer/figures': 1.0.13
|
||||||
|
'@inquirer/type': 1.5.5
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
|
||||||
|
'@inquirer/select@2.5.0':
|
||||||
|
dependencies:
|
||||||
|
'@inquirer/core': 9.2.1
|
||||||
|
'@inquirer/figures': 1.0.13
|
||||||
|
'@inquirer/type': 1.5.5
|
||||||
|
ansi-escapes: 4.3.2
|
||||||
|
yoctocolors-cjs: 2.1.3
|
||||||
|
|
||||||
|
'@inquirer/type@1.5.5':
|
||||||
|
dependencies:
|
||||||
|
mute-stream: 1.0.0
|
||||||
|
|
||||||
|
'@inquirer/type@2.0.0':
|
||||||
|
dependencies:
|
||||||
|
mute-stream: 1.0.0
|
||||||
|
|
||||||
'@isaacs/balanced-match@4.0.1': {}
|
'@isaacs/balanced-match@4.0.1': {}
|
||||||
|
|
||||||
'@isaacs/brace-expansion@5.0.0':
|
'@isaacs/brace-expansion@5.0.0':
|
||||||
@@ -1182,6 +1390,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 7.14.0
|
undici-types: 7.14.0
|
||||||
|
|
||||||
|
'@types/wrap-ansi@3.0.0': {}
|
||||||
|
|
||||||
abstract-logging@2.0.1: {}
|
abstract-logging@2.0.1: {}
|
||||||
|
|
||||||
agent-base@7.1.4: {}
|
agent-base@7.1.4: {}
|
||||||
@@ -1197,6 +1407,10 @@ snapshots:
|
|||||||
json-schema-traverse: 1.0.0
|
json-schema-traverse: 1.0.0
|
||||||
require-from-string: 2.0.2
|
require-from-string: 2.0.2
|
||||||
|
|
||||||
|
ansi-escapes@4.3.2:
|
||||||
|
dependencies:
|
||||||
|
type-fest: 0.21.3
|
||||||
|
|
||||||
ansi-regex@5.0.1: {}
|
ansi-regex@5.0.1: {}
|
||||||
|
|
||||||
ansi-regex@6.2.2: {}
|
ansi-regex@6.2.2: {}
|
||||||
@@ -1229,6 +1443,10 @@ snapshots:
|
|||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
|
|
||||||
|
chardet@0.7.0: {}
|
||||||
|
|
||||||
|
cli-width@4.1.0: {}
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name: 1.1.4
|
color-name: 1.1.4
|
||||||
@@ -1326,6 +1544,12 @@ snapshots:
|
|||||||
|
|
||||||
extend@3.0.2: {}
|
extend@3.0.2: {}
|
||||||
|
|
||||||
|
external-editor@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
chardet: 0.7.0
|
||||||
|
iconv-lite: 0.4.24
|
||||||
|
tmp: 0.0.33
|
||||||
|
|
||||||
fast-decode-uri-component@1.0.1: {}
|
fast-decode-uri-component@1.0.1: {}
|
||||||
|
|
||||||
fast-deep-equal@3.1.3: {}
|
fast-deep-equal@3.1.3: {}
|
||||||
@@ -1526,6 +1750,10 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
iconv-lite@0.4.24:
|
||||||
|
dependencies:
|
||||||
|
safer-buffer: 2.1.2
|
||||||
|
|
||||||
inherits@2.0.4: {}
|
inherits@2.0.4: {}
|
||||||
|
|
||||||
interpret@1.4.0: {}
|
interpret@1.4.0: {}
|
||||||
@@ -1610,6 +1838,8 @@ snapshots:
|
|||||||
|
|
||||||
ms@2.1.3: {}
|
ms@2.1.3: {}
|
||||||
|
|
||||||
|
mute-stream@1.0.0: {}
|
||||||
|
|
||||||
nice-try@1.0.5: {}
|
nice-try@1.0.5: {}
|
||||||
|
|
||||||
node-domexception@1.0.0: {}
|
node-domexception@1.0.0: {}
|
||||||
@@ -1640,6 +1870,8 @@ snapshots:
|
|||||||
|
|
||||||
openurl@1.1.1: {}
|
openurl@1.1.1: {}
|
||||||
|
|
||||||
|
os-tmpdir@1.0.2: {}
|
||||||
|
|
||||||
p-finally@1.0.0: {}
|
p-finally@1.0.0: {}
|
||||||
|
|
||||||
package-json-from-dist@1.0.1: {}
|
package-json-from-dist@1.0.1: {}
|
||||||
@@ -1808,6 +2040,10 @@ snapshots:
|
|||||||
|
|
||||||
tiktoken@1.0.22: {}
|
tiktoken@1.0.22: {}
|
||||||
|
|
||||||
|
tmp@0.0.33:
|
||||||
|
dependencies:
|
||||||
|
os-tmpdir: 1.0.2
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
@@ -1845,6 +2081,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
|
||||||
|
wrap-ansi@6.2.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
string-width: 4.2.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
wrap-ansi@7.0.0:
|
wrap-ansi@7.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
@@ -1860,3 +2102,5 @@ snapshots:
|
|||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
ws@8.18.3: {}
|
ws@8.18.3: {}
|
||||||
|
|
||||||
|
yoctocolors-cjs@2.1.3: {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {IAgent, ITool} from "./type";
|
import {IAgent, ITool} from "./type";
|
||||||
import { createHash } from 'crypto';
|
import { createHash } from 'crypto';
|
||||||
import { LRUCache } from 'lru-cache';
|
import * as LRU from 'lru-cache';
|
||||||
|
|
||||||
interface ImageCacheEntry {
|
interface ImageCacheEntry {
|
||||||
source: any;
|
source: any;
|
||||||
@@ -8,12 +8,13 @@ interface ImageCacheEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ImageCache {
|
class ImageCache {
|
||||||
private cache: LRUCache<string, ImageCacheEntry>;
|
private cache: any;
|
||||||
|
|
||||||
constructor(maxSize = 100) {
|
constructor(maxSize = 100) {
|
||||||
this.cache = new LRUCache({
|
const CacheClass: any = (LRU as any).LRUCache || (LRU as any);
|
||||||
|
this.cache = new CacheClass({
|
||||||
max: maxSize,
|
max: maxSize,
|
||||||
ttl: 5 * 60 * 1000,
|
ttl: 5 * 60 * 1000, // 5 minutes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
isServiceRunning,
|
isServiceRunning,
|
||||||
getServiceInfo,
|
getServiceInfo,
|
||||||
} from "./utils/processCheck";
|
} from "./utils/processCheck";
|
||||||
|
import { runModelSelector } from "./utils/modelSelector"; // ADD THIS LINE
|
||||||
import { version } from "../package.json";
|
import { version } from "../package.json";
|
||||||
import { spawn, exec } from "child_process";
|
import { spawn, exec } from "child_process";
|
||||||
import { PID_FILE, REFERENCE_COUNT_FILE } from "./constants";
|
import { PID_FILE, REFERENCE_COUNT_FILE } from "./constants";
|
||||||
@@ -26,6 +27,7 @@ Commands:
|
|||||||
status Show server status
|
status Show server status
|
||||||
statusline Integrated statusline
|
statusline Integrated statusline
|
||||||
code Execute claude command
|
code Execute claude command
|
||||||
|
model Interactive model selection and configuration
|
||||||
ui Open the web UI in browser
|
ui Open the web UI in browser
|
||||||
-v, version Show version information
|
-v, version Show version information
|
||||||
-h, help Show help information
|
-h, help Show help information
|
||||||
@@ -33,6 +35,7 @@ Commands:
|
|||||||
Example:
|
Example:
|
||||||
ccr start
|
ccr start
|
||||||
ccr code "Write a Hello World"
|
ccr code "Write a Hello World"
|
||||||
|
ccr model
|
||||||
ccr ui
|
ccr ui
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -109,6 +112,10 @@ async function main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
// ADD THIS CASE
|
||||||
|
case "model":
|
||||||
|
await runModelSelector();
|
||||||
|
break;
|
||||||
case "code":
|
case "code":
|
||||||
if (!isRunning) {
|
if (!isRunning) {
|
||||||
console.log("Service not running, starting service...");
|
console.log("Service not running, starting service...");
|
||||||
@@ -321,4 +328,4 @@ async function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch(console.error);
|
main().catch(console.error);
|
||||||
462
src/utils/modelSelector.ts
Normal file
462
src/utils/modelSelector.ts
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { select, input, confirm } from '@inquirer/prompts';
|
||||||
|
|
||||||
|
// ANSI color codes
|
||||||
|
const RESET = "\x1B[0m";
|
||||||
|
const DIM = "\x1B[2m";
|
||||||
|
const BOLDGREEN = "\x1B[1m\x1B[32m";
|
||||||
|
const CYAN = "\x1B[36m";
|
||||||
|
const BOLDCYAN = "\x1B[1m\x1B[36m";
|
||||||
|
const GREEN = "\x1B[32m";
|
||||||
|
const YELLOW = "\x1B[33m";
|
||||||
|
const BOLDYELLOW = "\x1B[1m\x1B[33m";
|
||||||
|
|
||||||
|
interface TransformerConfig {
|
||||||
|
use: Array<string | [string, any]>;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Provider {
|
||||||
|
name: string;
|
||||||
|
api_base_url: string;
|
||||||
|
api_key: string;
|
||||||
|
models: string[];
|
||||||
|
transformer?: TransformerConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RouterConfig {
|
||||||
|
default: string;
|
||||||
|
background?: string;
|
||||||
|
think?: string;
|
||||||
|
longContext?: string;
|
||||||
|
longContextThreshold?: number;
|
||||||
|
webSearch?: string;
|
||||||
|
image?: string;
|
||||||
|
[key: string]: string | number | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Config {
|
||||||
|
Providers: Provider[];
|
||||||
|
Router: RouterConfig;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ModelResult {
|
||||||
|
providerName: string;
|
||||||
|
modelName: string;
|
||||||
|
modelType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AVAILABLE_TRANSFORMERS = [
|
||||||
|
'anthropic',
|
||||||
|
'deepseek',
|
||||||
|
'gemini',
|
||||||
|
'openrouter',
|
||||||
|
'groq',
|
||||||
|
'maxtoken',
|
||||||
|
'tooluse',
|
||||||
|
'gemini-cli',
|
||||||
|
'reasoning',
|
||||||
|
'sampling',
|
||||||
|
'enhancetool',
|
||||||
|
'cleancache',
|
||||||
|
'vertex-gemini',
|
||||||
|
'chutes-glm',
|
||||||
|
'qwen-cli',
|
||||||
|
'rovo-cli'
|
||||||
|
];
|
||||||
|
|
||||||
|
function getConfigPath(): string {
|
||||||
|
const configDir = path.join(process.env.HOME || process.env.USERPROFILE || '', '.claude-code-router');
|
||||||
|
const configPath = path.join(configDir, 'config.json');
|
||||||
|
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
throw new Error(`config.json not found at ${configPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return configPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadConfig(): Config {
|
||||||
|
const configPath = getConfigPath();
|
||||||
|
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveConfig(config: Config): void {
|
||||||
|
const configPath = getConfigPath();
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
||||||
|
console.log(`${GREEN}✓ config.json updated successfully${RESET}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAllModels(config: Config) {
|
||||||
|
const models: any[] = [];
|
||||||
|
for (const provider of config.Providers) {
|
||||||
|
for (const model of provider.models) {
|
||||||
|
models.push({
|
||||||
|
name: `${BOLDCYAN}${provider.name}${RESET} → ${CYAN} ${model}`,
|
||||||
|
value: `${provider.name},${model}`,
|
||||||
|
description: `\n${BOLDCYAN}Provider:${RESET} ${provider.name}`,
|
||||||
|
provider: provider.name,
|
||||||
|
model: model
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayCurrentConfig(config: Config): void {
|
||||||
|
console.log(`\n${BOLDCYAN}═══════════════════════════════════════════════${RESET}`);
|
||||||
|
console.log(`${BOLDCYAN} Current Configuration${RESET}`);
|
||||||
|
console.log(`${BOLDCYAN}═══════════════════════════════════════════════${RESET}\n`);
|
||||||
|
|
||||||
|
const formatModel = (routerValue?: string | number) => {
|
||||||
|
if (!routerValue || typeof routerValue === 'number') {
|
||||||
|
return `${DIM}Not configured${RESET}`;
|
||||||
|
}
|
||||||
|
const [provider, model] = routerValue.split(',');
|
||||||
|
return `${YELLOW}${provider}${RESET} | ${model}\n ${DIM}- ${routerValue}${RESET}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`${BOLDCYAN}Default Model:${RESET}`);
|
||||||
|
console.log(` ${formatModel(config.Router.default)}\n`);
|
||||||
|
|
||||||
|
if (config.Router.background) {
|
||||||
|
console.log(`${BOLDCYAN}Background Model:${RESET}`);
|
||||||
|
console.log(` ${formatModel(config.Router.background)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Router.think) {
|
||||||
|
console.log(`${BOLDCYAN}Think Model:${RESET}`);
|
||||||
|
console.log(` ${formatModel(config.Router.think)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Router.longContext) {
|
||||||
|
console.log(`${BOLDCYAN}Long Context Model:${RESET}`);
|
||||||
|
console.log(` ${formatModel(config.Router.longContext)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Router.webSearch) {
|
||||||
|
console.log(`${BOLDCYAN}Web Search Model:${RESET}`);
|
||||||
|
console.log(` ${formatModel(config.Router.webSearch)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Router.image) {
|
||||||
|
console.log(`${BOLDCYAN}Image Model:${RESET}`);
|
||||||
|
console.log(` ${formatModel(config.Router.image)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n${BOLDCYAN}═══════════════════════════════════════════════${RESET}`);
|
||||||
|
console.log(`${BOLDCYAN} Add/Update Model${RESET}`);
|
||||||
|
console.log(`${BOLDCYAN}═══════════════════════════════════════════════${RESET}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectModelType() {
|
||||||
|
return await select({
|
||||||
|
message: `${BOLDYELLOW}Which model configuration do you want to update?${RESET}`,
|
||||||
|
choices: [
|
||||||
|
{ name: 'Default Model', value: 'default' },
|
||||||
|
{ name: 'Background Model', value: 'background' },
|
||||||
|
{ name: 'Think Model', value: 'think' },
|
||||||
|
{ name: 'Long Context Model', value: 'longContext' },
|
||||||
|
{ name: 'Web Search Model', value: 'webSearch' },
|
||||||
|
{ name: 'Image Model', value: 'image' },
|
||||||
|
{ name: `${BOLDGREEN}+ Add New Model${RESET}`, value: 'addModel' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectModel(config: Config, modelType: string) {
|
||||||
|
const models = getAllModels(config);
|
||||||
|
|
||||||
|
return await select({
|
||||||
|
message: `\n${BOLDYELLOW}Select a model for ${modelType}:${RESET}`,
|
||||||
|
choices: models,
|
||||||
|
pageSize: 15
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function configureTransformers(): Promise<TransformerConfig | undefined> {
|
||||||
|
const useTransformers = await confirm({
|
||||||
|
message: `\n${BOLDYELLOW}Add transformer configuration?${RESET}`,
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!useTransformers) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transformers: Array<string | [string, any]> = [];
|
||||||
|
let addMore = true;
|
||||||
|
|
||||||
|
while (addMore) {
|
||||||
|
const transformer = await select({
|
||||||
|
message: `\n${BOLDYELLOW}Select a transformer:${RESET}`,
|
||||||
|
choices: AVAILABLE_TRANSFORMERS.map(t => ({ name: t, value: t })),
|
||||||
|
pageSize: 15
|
||||||
|
}) as string;
|
||||||
|
|
||||||
|
// Check if transformer needs options
|
||||||
|
if (transformer === 'maxtoken') {
|
||||||
|
const maxTokens = await input({
|
||||||
|
message: `\n${BOLDYELLOW}Max tokens:${RESET}`,
|
||||||
|
default: '30000',
|
||||||
|
validate: (value: string) => {
|
||||||
|
const num = parseInt(value);
|
||||||
|
if (isNaN(num) || num <= 0) {
|
||||||
|
return 'Please enter a valid positive number';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
transformers.push(['maxtoken', { max_tokens: parseInt(maxTokens) }]);
|
||||||
|
} else if (transformer === 'openrouter') {
|
||||||
|
const addProvider = await confirm({
|
||||||
|
message: `\n${BOLDYELLOW}Add provider routing options?${RESET}`,
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (addProvider) {
|
||||||
|
const providerInput = await input({
|
||||||
|
message: 'Provider (e.g., moonshotai/fp8):',
|
||||||
|
validate: (value: string) => value.trim() !== '' || 'Provider cannot be empty'
|
||||||
|
});
|
||||||
|
transformers.push(['openrouter', { provider: { only: [providerInput] } }]);
|
||||||
|
} else {
|
||||||
|
transformers.push(transformer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
transformers.push(transformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
addMore = await confirm({
|
||||||
|
message: `\n${BOLDYELLOW}Add another transformer?${RESET}`,
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { use: transformers };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addNewModel(config: Config): Promise<ModelResult | null> {
|
||||||
|
const providerChoices = config.Providers.map(p => ({
|
||||||
|
name: p.name,
|
||||||
|
value: p.name
|
||||||
|
}));
|
||||||
|
|
||||||
|
providerChoices.push({ name: `${BOLDGREEN}+ Add New Provider${RESET}`, value: '__new__' });
|
||||||
|
|
||||||
|
const selectedProvider = await select({
|
||||||
|
message: `\n${BOLDYELLOW}Select provider for the new model:${RESET}`,
|
||||||
|
choices: providerChoices
|
||||||
|
}) as string;
|
||||||
|
|
||||||
|
if (selectedProvider === '__new__') {
|
||||||
|
return await addNewProvider(config);
|
||||||
|
} else {
|
||||||
|
return await addModelToExistingProvider(config, selectedProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addModelToExistingProvider(config: Config, providerName: string): Promise<ModelResult | null> {
|
||||||
|
const modelName = await input({
|
||||||
|
message: `\n${BOLDYELLOW}Enter the model name:${RESET}`,
|
||||||
|
validate: (value: string) => {
|
||||||
|
if (!value.trim()) {
|
||||||
|
return 'Model name cannot be empty';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const provider = config.Providers.find(p => p.name === providerName);
|
||||||
|
|
||||||
|
if (!provider) {
|
||||||
|
console.log(`${YELLOW}Provider not found${RESET}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provider.models.includes(modelName)) {
|
||||||
|
console.log(`${YELLOW}Model already exists in provider${RESET}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
provider.models.push(modelName);
|
||||||
|
|
||||||
|
// Ask about model-specific transformers
|
||||||
|
const addModelTransformer = await confirm({
|
||||||
|
message: `\n${BOLDYELLOW}Add model-specific transformer configuration?${RESET}`,
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (addModelTransformer) {
|
||||||
|
const transformerConfig = await configureTransformers();
|
||||||
|
if (transformerConfig && provider.transformer) {
|
||||||
|
provider.transformer[modelName] = transformerConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveConfig(config);
|
||||||
|
|
||||||
|
console.log(`${GREEN}✓ Model "${modelName}" added to provider "${providerName}"${RESET}`);
|
||||||
|
|
||||||
|
const setAsDefault = await confirm({
|
||||||
|
message: `\n${BOLDYELLOW}Do you want to set this model in router configuration?${RESET}`,
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (setAsDefault) {
|
||||||
|
const modelType = await select({
|
||||||
|
message: `\n${BOLDYELLOW}Select configuration type:${RESET}`,
|
||||||
|
choices: [
|
||||||
|
{ name: 'Default Model', value: 'default' },
|
||||||
|
{ name: 'Background Model', value: 'background' },
|
||||||
|
{ name: 'Think Model', value: 'think' },
|
||||||
|
{ name: 'Long Context Model', value: 'longContext' },
|
||||||
|
{ name: 'Web Search Model', value: 'webSearch' },
|
||||||
|
{ name: 'Image Model', value: 'image' }
|
||||||
|
]
|
||||||
|
}) as string;
|
||||||
|
|
||||||
|
return { providerName, modelName, modelType };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addNewProvider(config: Config): Promise<ModelResult | null> {
|
||||||
|
console.log(`\n${BOLDCYAN}Adding New Provider${RESET}\n`);
|
||||||
|
|
||||||
|
const providerName = await input({
|
||||||
|
message: `${BOLDYELLOW}Provider name:${RESET}`,
|
||||||
|
validate: (value: string) => {
|
||||||
|
if (!value.trim()) {
|
||||||
|
return 'Provider name cannot be empty';
|
||||||
|
}
|
||||||
|
if (config.Providers.some(p => p.name === value)) {
|
||||||
|
return 'Provider already exists';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const apiBaseUrl = await input({
|
||||||
|
message: `\n${BOLDYELLOW}API base URL:${RESET}`,
|
||||||
|
validate: (value: string) => {
|
||||||
|
if (!value.trim()) {
|
||||||
|
return 'API base URL cannot be empty';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
new URL(value);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return 'Please enter a valid URL';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const apiKey = await input({
|
||||||
|
message: `\n${BOLDYELLOW}API key:${RESET}`,
|
||||||
|
validate: (value: string) => {
|
||||||
|
if (!value.trim()) {
|
||||||
|
return 'API key cannot be empty';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const modelsInput = await input({
|
||||||
|
message: `\n${BOLDYELLOW}Model names (comma-separated):${RESET}`,
|
||||||
|
validate: (value: string) => {
|
||||||
|
if (!value.trim()) {
|
||||||
|
return 'At least one model name is required';
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const models = modelsInput.split(',').map(m => m.trim()).filter(m => m);
|
||||||
|
|
||||||
|
const newProvider: Provider = {
|
||||||
|
name: providerName,
|
||||||
|
api_base_url: apiBaseUrl,
|
||||||
|
api_key: apiKey,
|
||||||
|
models: models
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global transformer configuration
|
||||||
|
const transformerConfig = await configureTransformers();
|
||||||
|
if (transformerConfig) {
|
||||||
|
newProvider.transformer = transformerConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Providers.push(newProvider);
|
||||||
|
saveConfig(config);
|
||||||
|
|
||||||
|
console.log(`${GREEN}\n✓ Provider "${providerName}" added successfully${RESET}`);
|
||||||
|
|
||||||
|
const setAsDefault = await confirm({
|
||||||
|
message: `\n${BOLDYELLOW}Do you want to set one of these models in router configuration?${RESET}`,
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (setAsDefault && models.length > 0) {
|
||||||
|
let selectedModel = models[0];
|
||||||
|
|
||||||
|
if (models.length > 1) {
|
||||||
|
selectedModel = await select({
|
||||||
|
message: `\n${BOLDYELLOW}Select which model to configure:${RESET}`,
|
||||||
|
choices: models.map(m => ({ name: m, value: m }))
|
||||||
|
}) as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modelType = await select({
|
||||||
|
message: `\n${BOLDYELLOW}Select configuration type:${RESET}`,
|
||||||
|
choices: [
|
||||||
|
{ name: 'Default Model', value: 'default' },
|
||||||
|
{ name: 'Background Model', value: 'background' },
|
||||||
|
{ name: 'Think Model', value: 'think' },
|
||||||
|
{ name: 'Long Context Model', value: 'longContext' },
|
||||||
|
{ name: 'Web Search Model', value: 'webSearch' },
|
||||||
|
{ name: 'Image Model', value: 'image' }
|
||||||
|
]
|
||||||
|
}) as string;
|
||||||
|
|
||||||
|
return { providerName, modelName: selectedModel, modelType };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runModelSelector(): Promise<void> {
|
||||||
|
console.clear();
|
||||||
|
|
||||||
|
try {
|
||||||
|
let config = loadConfig();
|
||||||
|
displayCurrentConfig(config);
|
||||||
|
|
||||||
|
const action = await selectModelType() as string;
|
||||||
|
|
||||||
|
if (action === 'addModel') {
|
||||||
|
const result = await addNewModel(config);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
config = loadConfig();
|
||||||
|
config.Router[result.modelType] = `${result.providerName},${result.modelName}`;
|
||||||
|
saveConfig(config);
|
||||||
|
console.log(`${GREEN}✓ ${result.modelType} set to ${result.providerName},${result.modelName}${RESET}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const selectedModel = await selectModel(config, action) as string;
|
||||||
|
config.Router[action] = selectedModel;
|
||||||
|
saveConfig(config);
|
||||||
|
|
||||||
|
console.log(`${GREEN}✓ ${action} model updated to: ${selectedModel}${RESET}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
displayCurrentConfig(config);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`${YELLOW}Error:${RESET}`, error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
139
ui/package-lock.json
generated
139
ui/package-lock.json
generated
@@ -14,6 +14,7 @@
|
|||||||
"@radix-ui/react-popover": "^1.1.14",
|
"@radix-ui/react-popover": "^1.1.14",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@radix-ui/react-switch": "^1.2.5",
|
"@radix-ui/react-switch": "^1.2.5",
|
||||||
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@radix-ui/react-tooltip": "^1.2.7",
|
"@radix-ui/react-tooltip": "^1.2.7",
|
||||||
"@tailwindcss/vite": "^4.1.11",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
@@ -1181,6 +1182,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-collection": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-slot": "1.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-compose-refs": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
@@ -1247,6 +1274,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-direction": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-dismissable-layer": {
|
"node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
"version": "1.1.10",
|
"version": "1.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
|
||||||
@@ -1495,6 +1537,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-roving-focus": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.3",
|
||||||
|
"@radix-ui/react-collection": "1.1.7",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-direction": "1.1.1",
|
||||||
|
"@radix-ui/react-id": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.2.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
@@ -1542,6 +1621,66 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs": {
|
||||||
|
"version": "1.1.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz",
|
||||||
|
"integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.3",
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-direction": "1.1.1",
|
||||||
|
"@radix-ui/react-id": "1.1.1",
|
||||||
|
"@radix-ui/react-presence": "1.1.5",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-roving-focus": "1.1.11",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.2.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-tooltip": {
|
"node_modules/@radix-ui/react-tooltip": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"root":["./src/app.tsx","./src/i18n.ts","./src/main.tsx","./src/routes.tsx","./src/types.ts","./src/vite-env.d.ts","./src/components/configprovider.tsx","./src/components/debugpage.tsx","./src/components/jsoneditor.tsx","./src/components/logviewer.tsx","./src/components/login.tsx","./src/components/protectedroute.tsx","./src/components/providerlist.tsx","./src/components/providers.tsx","./src/components/publicroute.tsx","./src/components/requesthistorydrawer.tsx","./src/components/router.tsx","./src/components/settingsdialog.tsx","./src/components/statuslineconfigdialog.tsx","./src/components/statuslineimportexport.tsx","./src/components/transformerlist.tsx","./src/components/transformers.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/color-picker.tsx","./src/components/ui/combo-input.tsx","./src/components/ui/combobox.tsx","./src/components/ui/command.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/multi-combobox.tsx","./src/components/ui/popover.tsx","./src/components/ui/switch.tsx","./src/components/ui/tabs.tsx","./src/components/ui/toast.tsx","./src/lib/api.ts","./src/lib/db.ts","./src/lib/utils.ts","./src/utils/statusline.ts"],"version":"5.8.3"}
|
{"root":["./src/App.tsx","./src/i18n.ts","./src/main.tsx","./src/routes.tsx","./src/types.ts","./src/vite-env.d.ts","./src/components/ConfigProvider.tsx","./src/components/DebugPage.tsx","./src/components/JsonEditor.tsx","./src/components/LogViewer.tsx","./src/components/Login.tsx","./src/components/ProtectedRoute.tsx","./src/components/ProviderList.tsx","./src/components/Providers.tsx","./src/components/PublicRoute.tsx","./src/components/RequestHistoryDrawer.tsx","./src/components/Router.tsx","./src/components/SettingsDialog.tsx","./src/components/StatusLineConfigDialog.tsx","./src/components/StatusLineImportExport.tsx","./src/components/TransformerList.tsx","./src/components/Transformers.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/color-picker.tsx","./src/components/ui/combo-input.tsx","./src/components/ui/combobox.tsx","./src/components/ui/command.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/multi-combobox.tsx","./src/components/ui/popover.tsx","./src/components/ui/switch.tsx","./src/components/ui/tabs.tsx","./src/components/ui/toast.tsx","./src/lib/api.ts","./src/lib/db.ts","./src/lib/utils.ts","./src/utils/statusline.ts"],"version":"5.8.3"}
|
||||||
Reference in New Issue
Block a user