ronghua commited on
Commit
3c2af29
·
1 Parent(s): b491539
This view is limited to 50 files because it contains too many changes.   See raw diff
.env.example CHANGED
@@ -1,5 +1,100 @@
 
 
 
1
  PORT=3000
 
 
2
  ROUTE_PREFIX=
 
 
3
  AUTH_TOKEN=
4
- TOKEN_FILE=.token
5
- TOKEN_LIST_FILE=.token-list
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 当前配置为默认值,请根据需要修改
2
+
3
+ # 服务器监听端口
4
  PORT=3000
5
+
6
+ # 路由前缀,必须以 / 开头(如果不为空)
7
  ROUTE_PREFIX=
8
+
9
+ # 最高权限的认证令牌,必填
10
  AUTH_TOKEN=
11
+
12
+ # 共享的认证令牌,仅Chat端点权限(轮询与AUTH_TOKEN同步),无其余权限
13
+ SHARED_TOKEN=
14
+
15
+ # 启用流式响应检查,关闭则无法响应错误,代价是会对第一个块解析2次(已弃用)
16
+ # 新版本已经完成优化
17
+ # ENABLE_STREAM_CHECK=true
18
+
19
+ # 流式消息结束后发送包含"finish_reason"为"stop"的空消息块(已弃用)
20
+ # INCLUDE_STOP_REASON_STREAM=true
21
+
22
+ # 令牌文件路径(已弃用)
23
+ # TOKEN_FILE=.token
24
+
25
+ # 令牌列表文件路径
26
+ TOKEN_LIST_FILE=.tokens
27
+
28
+ # (实验性)是否启用慢速池(true/false)
29
+ ENABLE_SLOW_POOL=false
30
+
31
+ # 允许claude开头的模型请求绕过内置模型限制(true/false)
32
+ PASS_ANY_CLAUDE=false
33
+
34
+ # 图片处理能力配置
35
+ # 可选值:
36
+ # - none 或 disabled:禁用图片功能
37
+ # - base64 或 base64-only:仅支持 base64 编码的图片
38
+ # - all 或 base64-http:支持 base64 和 HTTP 图片
39
+ # 注意:启用 HTTP 支持可能会暴露服务器 IP
40
+ VISION_ABILITY=base64
41
+
42
+ # 额度检查配置
43
+ # 可选值:
44
+ # - none 或 disabled:禁用额度检查
45
+ # - default:详见 README
46
+ # - all 或 everything:额度无条件检查
47
+ # - 以,分隔的模型列表,为空时使用默认值
48
+ USAGE_CHECK=default
49
+
50
+ # 是否允许使用动态(自定义)配置的 API Key
51
+ DYNAMIC_KEY=false
52
+
53
+ # 动态 Key 的标识前缀
54
+ KEY_PREFIX=sk-
55
+
56
+ # 默认提示词
57
+ DEFAULT_INSTRUCTIONS="Respond in Chinese by default"
58
+
59
+ # 反向代理服务器主机名
60
+ REVERSE_PROXY_HOST=
61
+
62
+ # 代理地址配置说明
63
+ # - 留空或 `no`: 不使用任何代理
64
+ # - `system`: 使用系统代理(变量不存在时的默认值)
65
+ # - 代理地址: 支持以下格式
66
+ # - 多个代理: `http://localhost:7890,https://username:password@localhost:1234`
67
+ # 没有轮询,只是选择第一个格式正确的
68
+ # - 支持的协议: http, https, socks4, socks5, socks5h
69
+ PROXIES=
70
+
71
+ # 请求体大小限制(单位为MB)
72
+ # 默认为2MB (2,097,152 字节)
73
+ REQUEST_BODY_LIMIT_MB=2
74
+
75
+ # OpenAI 请求时,token 和 checksum 的分隔符
76
+ TOKEN_DELIMITER=,
77
+
78
+ # 同时兼容默认的,作为分隔符
79
+ USE_COMMA_DELIMITER=true
80
+
81
+ # 调试
82
+ DEBUG=false
83
+
84
+ # 调试文件
85
+ DEBUG_LOG_FILE=debug.log
86
+
87
+ # 日志储存条数(最大值2000)
88
+ REQUEST_LOGS_LIMIT=100
89
+
90
+ # Cursor 服务超时(秒)(最大值600)
91
+ SERVICE_TIMEOUT=30
92
+
93
+ # 包含网络引用
94
+ INCLUDE_WEB_REFERENCES=false
95
+
96
+ # 持久化日志文件路径
97
+ LOGS_FILE_PATH=logs.bin
98
+
99
+ # 持久化页面配置文件路径
100
+ PAGES_FILE_PATH=pages.bin
.gitattributes CHANGED
@@ -1,35 +1,6 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ # 统一使用 LF
2
+ * text=auto eol=lf
3
+
4
+ # 对特定文件类型设置
5
+ *.bat text eol=crlf
6
+ *.ps1 text eol=crlf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore CHANGED
@@ -1,14 +1,26 @@
1
  /target
2
- /get-token/target
3
  /*.log
4
  /*.env
5
- /static/tokeninfo.min.html
 
 
 
6
  node_modules
7
  .DS_Store
8
  /.vscode
9
  /.cargo
10
  /.token
11
  /.token-list
 
12
  /cursor-api
13
  /cursor-api.exe
14
  /release
 
 
 
 
 
 
 
 
 
1
  /target
2
+ /tools/*/target
3
  /*.log
4
  /*.env
5
+ /static/*.min.html
6
+ /static/*.min.css
7
+ /static/*.min.js
8
+ /scripts/.asset-hashes.json
9
  node_modules
10
  .DS_Store
11
  /.vscode
12
  /.cargo
13
  /.token
14
  /.token-list
15
+ /.tokens
16
  /cursor-api
17
  /cursor-api.exe
18
  /release
19
+
20
+ /*.py
21
+ /logs
22
+ /dev*
23
+ /build*
24
+ /*.bin
25
+ /result.txt
26
+ tools/tokenizer/
.license-compliance ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 合规使用指南
2
+ attribution_rules:
3
+ required_disclaimer:
4
+ text: "基于第三方技术构建,与原始开发者无关联"
5
+ placement:
6
+ - documentation
7
+ - marketing_materials
8
+ - about_sections
9
+ prohibited_actions:
10
+ - using_author_name_in_press_releases
11
+ - claiming_official_support
12
+ - using_project_logo_as_endorsement
13
+
14
+ enforcement:
15
+ grace_period: 72h
16
+ compliance_check: https://api.wisdgod.com/license/validate
Cargo.lock CHANGED
@@ -17,6 +17,17 @@ version = "2.0.0"
17
  source = "registry+https://github.com/rust-lang/crates.io-index"
18
  checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
19
 
 
 
 
 
 
 
 
 
 
 
 
20
  [[package]]
21
  name = "aho-corasick"
22
  version = "1.1.3"
@@ -26,6 +37,21 @@ dependencies = [
26
  "memchr",
27
  ]
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  [[package]]
30
  name = "android-tzdata"
31
  version = "0.1.1"
@@ -53,6 +79,7 @@ version = "0.4.18"
53
  source = "registry+https://github.com/rust-lang/crates.io-index"
54
  checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522"
55
  dependencies = [
 
56
  "flate2",
57
  "futures-core",
58
  "memchr",
@@ -60,17 +87,6 @@ dependencies = [
60
  "tokio",
61
  ]
62
 
63
- [[package]]
64
- name = "async-trait"
65
- version = "0.1.83"
66
- source = "registry+https://github.com/rust-lang/crates.io-index"
67
- checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
68
- dependencies = [
69
- "proc-macro2",
70
- "quote",
71
- "syn",
72
- ]
73
-
74
  [[package]]
75
  name = "atomic-waker"
76
  version = "1.1.2"
@@ -85,13 +101,13 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
85
 
86
  [[package]]
87
  name = "axum"
88
- version = "0.7.9"
89
  source = "registry+https://github.com/rust-lang/crates.io-index"
90
- checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
91
  dependencies = [
92
- "async-trait",
93
  "axum-core",
94
  "bytes",
 
95
  "futures-util",
96
  "http",
97
  "http-body",
@@ -119,11 +135,10 @@ dependencies = [
119
 
120
  [[package]]
121
  name = "axum-core"
122
- version = "0.4.5"
123
  source = "registry+https://github.com/rust-lang/crates.io-index"
124
- checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
125
  dependencies = [
126
- "async-trait",
127
  "bytes",
128
  "futures-util",
129
  "http",
@@ -161,9 +176,27 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
161
 
162
  [[package]]
163
  name = "bitflags"
164
- version = "2.6.0"
 
 
 
 
 
 
165
  source = "registry+https://github.com/rust-lang/crates.io-index"
166
- checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
  [[package]]
169
  name = "block-buffer"
@@ -174,18 +207,73 @@ dependencies = [
174
  "generic-array",
175
  ]
176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  [[package]]
178
  name = "bumpalo"
179
  version = "3.16.0"
180
  source = "registry+https://github.com/rust-lang/crates.io-index"
181
  checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
182
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  [[package]]
184
  name = "byteorder"
185
  version = "1.5.0"
186
  source = "registry+https://github.com/rust-lang/crates.io-index"
187
  checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
188
 
 
 
 
 
 
 
189
  [[package]]
190
  name = "bytes"
191
  version = "1.9.0"
@@ -194,9 +282,9 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
194
 
195
  [[package]]
196
  name = "cc"
197
- version = "1.2.5"
198
  source = "registry+https://github.com/rust-lang/crates.io-index"
199
- checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
200
  dependencies = [
201
  "shlex",
202
  ]
@@ -215,13 +303,18 @@ checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
215
  dependencies = [
216
  "android-tzdata",
217
  "iana-time-zone",
218
- "js-sys",
219
  "num-traits",
 
220
  "serde",
221
- "wasm-bindgen",
222
  "windows-targets",
223
  ]
224
 
 
 
 
 
 
 
225
  [[package]]
226
  name = "core-foundation"
227
  version = "0.9.4"
@@ -268,7 +361,7 @@ dependencies = [
268
 
269
  [[package]]
270
  name = "cursor-api"
271
- version = "0.1.0"
272
  dependencies = [
273
  "axum",
274
  "base64",
@@ -277,18 +370,27 @@ dependencies = [
277
  "dotenvy",
278
  "flate2",
279
  "futures",
 
280
  "hex",
 
 
 
 
281
  "prost",
282
  "prost-build",
283
  "rand",
284
  "regex",
285
  "reqwest",
 
286
  "serde",
287
  "serde_json",
288
  "sha2",
 
 
289
  "tokio",
290
  "tokio-stream",
291
  "tower-http",
 
292
  "uuid",
293
  ]
294
 
@@ -310,7 +412,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
310
  dependencies = [
311
  "proc-macro2",
312
  "quote",
313
- "syn",
314
  ]
315
 
316
  [[package]]
@@ -356,6 +458,27 @@ version = "2.3.0"
356
  source = "registry+https://github.com/rust-lang/crates.io-index"
357
  checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  [[package]]
360
  name = "fixedbitset"
361
  version = "0.4.2"
@@ -402,6 +525,12 @@ dependencies = [
402
  "percent-encoding",
403
  ]
404
 
 
 
 
 
 
 
405
  [[package]]
406
  name = "futures"
407
  version = "0.3.31"
@@ -446,7 +575,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
446
  dependencies = [
447
  "proc-macro2",
448
  "quote",
449
- "syn",
450
  ]
451
 
452
  [[package]]
@@ -500,6 +629,16 @@ dependencies = [
500
  "wasi",
501
  ]
502
 
 
 
 
 
 
 
 
 
 
 
503
  [[package]]
504
  name = "gimli"
505
  version = "0.31.1"
@@ -525,6 +664,15 @@ dependencies = [
525
  "tracing",
526
  ]
527
 
 
 
 
 
 
 
 
 
 
528
  [[package]]
529
  name = "hashbrown"
530
  version = "0.15.2"
@@ -673,7 +821,7 @@ dependencies = [
673
  "iana-time-zone-haiku",
674
  "js-sys",
675
  "wasm-bindgen",
676
- "windows-core",
677
  ]
678
 
679
  [[package]]
@@ -800,7 +948,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
800
  dependencies = [
801
  "proc-macro2",
802
  "quote",
803
- "syn",
804
  ]
805
 
806
  [[package]]
@@ -824,21 +972,48 @@ dependencies = [
824
  "icu_properties",
825
  ]
826
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
827
  [[package]]
828
  name = "indexmap"
829
- version = "2.7.0"
830
  source = "registry+https://github.com/rust-lang/crates.io-index"
831
- checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
832
  dependencies = [
833
  "equivalent",
834
- "hashbrown",
835
  ]
836
 
837
  [[package]]
838
  name = "ipnet"
839
- version = "2.10.1"
840
  source = "registry+https://github.com/rust-lang/crates.io-index"
841
- checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
842
 
843
  [[package]]
844
  name = "itertools"
@@ -857,9 +1032,9 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
857
 
858
  [[package]]
859
  name = "js-sys"
860
- version = "0.3.76"
861
  source = "registry+https://github.com/rust-lang/crates.io-index"
862
- checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
863
  dependencies = [
864
  "once_cell",
865
  "wasm-bindgen",
@@ -873,9 +1048,9 @@ checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
873
 
874
  [[package]]
875
  name = "linux-raw-sys"
876
- version = "0.4.14"
877
  source = "registry+https://github.com/rust-lang/crates.io-index"
878
- checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
879
 
880
  [[package]]
881
  name = "litemap"
@@ -883,17 +1058,27 @@ version = "0.7.4"
883
  source = "registry+https://github.com/rust-lang/crates.io-index"
884
  checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
885
 
 
 
 
 
 
 
 
 
 
 
886
  [[package]]
887
  name = "log"
888
- version = "0.4.22"
889
  source = "registry+https://github.com/rust-lang/crates.io-index"
890
- checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
891
 
892
  [[package]]
893
  name = "matchit"
894
- version = "0.7.3"
895
  source = "registry+https://github.com/rust-lang/crates.io-index"
896
- checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
897
 
898
  [[package]]
899
  name = "memchr"
@@ -901,6 +1086,15 @@ version = "2.7.4"
901
  source = "registry+https://github.com/rust-lang/crates.io-index"
902
  checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
903
 
 
 
 
 
 
 
 
 
 
904
  [[package]]
905
  name = "mime"
906
  version = "0.3.17"
@@ -909,11 +1103,12 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
909
 
910
  [[package]]
911
  name = "miniz_oxide"
912
- version = "0.8.2"
913
  source = "registry+https://github.com/rust-lang/crates.io-index"
914
- checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
915
  dependencies = [
916
  "adler2",
 
917
  ]
918
 
919
  [[package]]
@@ -933,6 +1128,26 @@ version = "0.10.0"
933
  source = "registry+https://github.com/rust-lang/crates.io-index"
934
  checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
935
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
936
  [[package]]
937
  name = "native-tls"
938
  version = "0.2.12"
@@ -950,6 +1165,15 @@ dependencies = [
950
  "tempfile",
951
  ]
952
 
 
 
 
 
 
 
 
 
 
953
  [[package]]
954
  name = "num-traits"
955
  version = "0.2.19"
@@ -980,7 +1204,7 @@ version = "0.10.68"
980
  source = "registry+https://github.com/rust-lang/crates.io-index"
981
  checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
982
  dependencies = [
983
- "bitflags",
984
  "cfg-if",
985
  "foreign-types",
986
  "libc",
@@ -997,7 +1221,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
997
  dependencies = [
998
  "proc-macro2",
999
  "quote",
1000
- "syn",
1001
  ]
1002
 
1003
  [[package]]
@@ -1018,6 +1242,35 @@ dependencies = [
1018
  "vcpkg",
1019
  ]
1020
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1021
  [[package]]
1022
  name = "percent-encoding"
1023
  version = "2.3.1"
@@ -1036,9 +1289,9 @@ dependencies = [
1036
 
1037
  [[package]]
1038
  name = "pin-project-lite"
1039
- version = "0.2.15"
1040
  source = "registry+https://github.com/rust-lang/crates.io-index"
1041
- checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
1042
 
1043
  [[package]]
1044
  name = "pin-utils"
@@ -1052,6 +1305,19 @@ version = "0.3.31"
1052
  source = "registry+https://github.com/rust-lang/crates.io-index"
1053
  checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
1054
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1055
  [[package]]
1056
  name = "ppv-lite86"
1057
  version = "0.2.20"
@@ -1063,19 +1329,19 @@ dependencies = [
1063
 
1064
  [[package]]
1065
  name = "prettyplease"
1066
- version = "0.2.25"
1067
  source = "registry+https://github.com/rust-lang/crates.io-index"
1068
- checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
1069
  dependencies = [
1070
  "proc-macro2",
1071
- "syn",
1072
  ]
1073
 
1074
  [[package]]
1075
  name = "proc-macro2"
1076
- version = "1.0.92"
1077
  source = "registry+https://github.com/rust-lang/crates.io-index"
1078
- checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
1079
  dependencies = [
1080
  "unicode-ident",
1081
  ]
@@ -1106,7 +1372,7 @@ dependencies = [
1106
  "prost",
1107
  "prost-types",
1108
  "regex",
1109
- "syn",
1110
  "tempfile",
1111
  ]
1112
 
@@ -1120,7 +1386,7 @@ dependencies = [
1120
  "itertools",
1121
  "proc-macro2",
1122
  "quote",
1123
- "syn",
1124
  ]
1125
 
1126
  [[package]]
@@ -1132,15 +1398,76 @@ dependencies = [
1132
  "prost",
1133
  ]
1134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1135
  [[package]]
1136
  name = "quote"
1137
- version = "1.0.37"
1138
  source = "registry+https://github.com/rust-lang/crates.io-index"
1139
- checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
1140
  dependencies = [
1141
  "proc-macro2",
1142
  ]
1143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1144
  [[package]]
1145
  name = "rand"
1146
  version = "0.8.5"
@@ -1171,6 +1498,35 @@ dependencies = [
1171
  "getrandom",
1172
  ]
1173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1174
  [[package]]
1175
  name = "regex"
1176
  version = "1.11.1"
@@ -1200,11 +1556,26 @@ version = "0.8.5"
1200
  source = "registry+https://github.com/rust-lang/crates.io-index"
1201
  checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
1202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1203
  [[package]]
1204
  name = "reqwest"
1205
- version = "0.12.9"
1206
  source = "registry+https://github.com/rust-lang/crates.io-index"
1207
- checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
1208
  dependencies = [
1209
  "async-compression",
1210
  "base64",
@@ -1236,7 +1607,9 @@ dependencies = [
1236
  "system-configuration",
1237
  "tokio",
1238
  "tokio-native-tls",
 
1239
  "tokio-util",
 
1240
  "tower-service",
1241
  "url",
1242
  "wasm-bindgen",
@@ -1261,6 +1634,64 @@ dependencies = [
1261
  "windows-sys 0.52.0",
1262
  ]
1263
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1264
  [[package]]
1265
  name = "rustc-demangle"
1266
  version = "0.1.24"
@@ -1269,11 +1700,11 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
1269
 
1270
  [[package]]
1271
  name = "rustix"
1272
- version = "0.38.42"
1273
  source = "registry+https://github.com/rust-lang/crates.io-index"
1274
- checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
1275
  dependencies = [
1276
- "bitflags",
1277
  "errno",
1278
  "libc",
1279
  "linux-raw-sys",
@@ -1282,9 +1713,9 @@ dependencies = [
1282
 
1283
  [[package]]
1284
  name = "rustls"
1285
- version = "0.23.20"
1286
  source = "registry+https://github.com/rust-lang/crates.io-index"
1287
- checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
1288
  dependencies = [
1289
  "once_cell",
1290
  "rustls-pki-types",
@@ -1321,9 +1752,9 @@ dependencies = [
1321
 
1322
  [[package]]
1323
  name = "rustversion"
1324
- version = "1.0.18"
1325
  source = "registry+https://github.com/rust-lang/crates.io-index"
1326
- checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
1327
 
1328
  [[package]]
1329
  name = "ryu"
@@ -1340,13 +1771,25 @@ dependencies = [
1340
  "windows-sys 0.59.0",
1341
  ]
1342
 
 
 
 
 
 
 
 
 
 
 
 
 
1343
  [[package]]
1344
  name = "security-framework"
1345
  version = "2.11.1"
1346
  source = "registry+https://github.com/rust-lang/crates.io-index"
1347
  checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
1348
  dependencies = [
1349
- "bitflags",
1350
  "core-foundation",
1351
  "core-foundation-sys",
1352
  "libc",
@@ -1355,9 +1798,9 @@ dependencies = [
1355
 
1356
  [[package]]
1357
  name = "security-framework-sys"
1358
- version = "2.13.0"
1359
  source = "registry+https://github.com/rust-lang/crates.io-index"
1360
- checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5"
1361
  dependencies = [
1362
  "core-foundation-sys",
1363
  "libc",
@@ -1365,29 +1808,29 @@ dependencies = [
1365
 
1366
  [[package]]
1367
  name = "serde"
1368
- version = "1.0.216"
1369
  source = "registry+https://github.com/rust-lang/crates.io-index"
1370
- checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
1371
  dependencies = [
1372
  "serde_derive",
1373
  ]
1374
 
1375
  [[package]]
1376
  name = "serde_derive"
1377
- version = "1.0.216"
1378
  source = "registry+https://github.com/rust-lang/crates.io-index"
1379
- checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
1380
  dependencies = [
1381
  "proc-macro2",
1382
  "quote",
1383
- "syn",
1384
  ]
1385
 
1386
  [[package]]
1387
  name = "serde_json"
1388
- version = "1.0.134"
1389
  source = "registry+https://github.com/rust-lang/crates.io-index"
1390
- checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
1391
  dependencies = [
1392
  "itoa",
1393
  "memchr",
@@ -1434,6 +1877,27 @@ version = "1.3.0"
1434
  source = "registry+https://github.com/rust-lang/crates.io-index"
1435
  checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
1436
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1437
  [[package]]
1438
  name = "slab"
1439
  version = "0.4.9"
@@ -1459,6 +1923,44 @@ dependencies = [
1459
  "windows-sys 0.52.0",
1460
  ]
1461
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1462
  [[package]]
1463
  name = "spin"
1464
  version = "0.9.8"
@@ -1479,9 +1981,20 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
1479
 
1480
  [[package]]
1481
  name = "syn"
1482
- version = "2.0.91"
 
 
 
 
 
 
 
 
 
 
 
1483
  source = "registry+https://github.com/rust-lang/crates.io-index"
1484
- checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035"
1485
  dependencies = [
1486
  "proc-macro2",
1487
  "quote",
@@ -1505,7 +2018,20 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
1505
  dependencies = [
1506
  "proc-macro2",
1507
  "quote",
1508
- "syn",
 
 
 
 
 
 
 
 
 
 
 
 
 
1509
  ]
1510
 
1511
  [[package]]
@@ -1514,7 +2040,7 @@ version = "0.6.1"
1514
  source = "registry+https://github.com/rust-lang/crates.io-index"
1515
  checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
1516
  dependencies = [
1517
- "bitflags",
1518
  "core-foundation",
1519
  "system-configuration-sys",
1520
  ]
@@ -1529,19 +2055,66 @@ dependencies = [
1529
  "libc",
1530
  ]
1531
 
 
 
 
 
 
 
1532
  [[package]]
1533
  name = "tempfile"
1534
- version = "3.14.0"
1535
  source = "registry+https://github.com/rust-lang/crates.io-index"
1536
- checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
1537
  dependencies = [
1538
  "cfg-if",
1539
  "fastrand",
 
1540
  "once_cell",
1541
  "rustix",
1542
  "windows-sys 0.59.0",
1543
  ]
1544
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1545
  [[package]]
1546
  name = "tinystr"
1547
  version = "0.7.6"
@@ -1552,17 +2125,33 @@ dependencies = [
1552
  "zerovec",
1553
  ]
1554
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1555
  [[package]]
1556
  name = "tokio"
1557
- version = "1.42.0"
1558
  source = "registry+https://github.com/rust-lang/crates.io-index"
1559
- checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
1560
  dependencies = [
1561
  "backtrace",
1562
  "bytes",
1563
  "libc",
1564
  "mio",
1565
  "pin-project-lite",
 
1566
  "socket2",
1567
  "tokio-macros",
1568
  "windows-sys 0.52.0",
@@ -1570,13 +2159,13 @@ dependencies = [
1570
 
1571
  [[package]]
1572
  name = "tokio-macros"
1573
- version = "2.4.0"
1574
  source = "registry+https://github.com/rust-lang/crates.io-index"
1575
- checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
1576
  dependencies = [
1577
  "proc-macro2",
1578
  "quote",
1579
- "syn",
1580
  ]
1581
 
1582
  [[package]]
@@ -1599,6 +2188,18 @@ dependencies = [
1599
  "tokio",
1600
  ]
1601
 
 
 
 
 
 
 
 
 
 
 
 
 
1602
  [[package]]
1603
  name = "tokio-stream"
1604
  version = "0.1.17"
@@ -1645,9 +2246,11 @@ version = "0.6.2"
1645
  source = "registry+https://github.com/rust-lang/crates.io-index"
1646
  checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697"
1647
  dependencies = [
1648
- "bitflags",
1649
  "bytes",
1650
  "http",
 
 
1651
  "pin-project-lite",
1652
  "tower-layer",
1653
  "tower-service",
@@ -1734,9 +2337,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
1734
 
1735
  [[package]]
1736
  name = "uuid"
1737
- version = "1.11.0"
1738
  source = "registry+https://github.com/rust-lang/crates.io-index"
1739
- checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
1740
  dependencies = [
1741
  "getrandom",
1742
  ]
@@ -1770,34 +2373,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
1770
 
1771
  [[package]]
1772
  name = "wasm-bindgen"
1773
- version = "0.2.99"
1774
  source = "registry+https://github.com/rust-lang/crates.io-index"
1775
- checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
1776
  dependencies = [
1777
  "cfg-if",
1778
  "once_cell",
 
1779
  "wasm-bindgen-macro",
1780
  ]
1781
 
1782
  [[package]]
1783
  name = "wasm-bindgen-backend"
1784
- version = "0.2.99"
1785
  source = "registry+https://github.com/rust-lang/crates.io-index"
1786
- checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
1787
  dependencies = [
1788
  "bumpalo",
1789
  "log",
1790
  "proc-macro2",
1791
  "quote",
1792
- "syn",
1793
  "wasm-bindgen-shared",
1794
  ]
1795
 
1796
  [[package]]
1797
  name = "wasm-bindgen-futures"
1798
- version = "0.4.49"
1799
  source = "registry+https://github.com/rust-lang/crates.io-index"
1800
- checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
1801
  dependencies = [
1802
  "cfg-if",
1803
  "js-sys",
@@ -1808,9 +2412,9 @@ dependencies = [
1808
 
1809
  [[package]]
1810
  name = "wasm-bindgen-macro"
1811
- version = "0.2.99"
1812
  source = "registry+https://github.com/rust-lang/crates.io-index"
1813
- checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
1814
  dependencies = [
1815
  "quote",
1816
  "wasm-bindgen-macro-support",
@@ -1818,22 +2422,25 @@ dependencies = [
1818
 
1819
  [[package]]
1820
  name = "wasm-bindgen-macro-support"
1821
- version = "0.2.99"
1822
  source = "registry+https://github.com/rust-lang/crates.io-index"
1823
- checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
1824
  dependencies = [
1825
  "proc-macro2",
1826
  "quote",
1827
- "syn",
1828
  "wasm-bindgen-backend",
1829
  "wasm-bindgen-shared",
1830
  ]
1831
 
1832
  [[package]]
1833
  name = "wasm-bindgen-shared"
1834
- version = "0.2.99"
1835
  source = "registry+https://github.com/rust-lang/crates.io-index"
1836
- checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
 
 
 
1837
 
1838
  [[package]]
1839
  name = "wasm-streams"
@@ -1850,14 +2457,52 @@ dependencies = [
1850
 
1851
  [[package]]
1852
  name = "web-sys"
1853
- version = "0.3.76"
1854
  source = "registry+https://github.com/rust-lang/crates.io-index"
1855
- checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
1856
  dependencies = [
1857
  "js-sys",
1858
  "wasm-bindgen",
1859
  ]
1860
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1861
  [[package]]
1862
  name = "windows-core"
1863
  version = "0.52.0"
@@ -1867,17 +2512,60 @@ dependencies = [
1867
  "windows-targets",
1868
  ]
1869
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1870
  [[package]]
1871
  name = "windows-registry"
1872
  version = "0.2.0"
1873
  source = "registry+https://github.com/rust-lang/crates.io-index"
1874
  checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
1875
  dependencies = [
1876
- "windows-result",
1877
  "windows-strings",
1878
  "windows-targets",
1879
  ]
1880
 
 
 
 
 
 
 
 
 
 
1881
  [[package]]
1882
  name = "windows-result"
1883
  version = "0.2.0"
@@ -1893,7 +2581,7 @@ version = "0.1.0"
1893
  source = "registry+https://github.com/rust-lang/crates.io-index"
1894
  checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
1895
  dependencies = [
1896
- "windows-result",
1897
  "windows-targets",
1898
  ]
1899
 
@@ -1991,6 +2679,15 @@ version = "0.5.5"
1991
  source = "registry+https://github.com/rust-lang/crates.io-index"
1992
  checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
1993
 
 
 
 
 
 
 
 
 
 
1994
  [[package]]
1995
  name = "yoke"
1996
  version = "0.7.5"
@@ -2011,7 +2708,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
2011
  dependencies = [
2012
  "proc-macro2",
2013
  "quote",
2014
- "syn",
2015
  "synstructure",
2016
  ]
2017
 
@@ -2033,7 +2730,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
2033
  dependencies = [
2034
  "proc-macro2",
2035
  "quote",
2036
- "syn",
2037
  ]
2038
 
2039
  [[package]]
@@ -2053,7 +2750,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
2053
  dependencies = [
2054
  "proc-macro2",
2055
  "quote",
2056
- "syn",
2057
  "synstructure",
2058
  ]
2059
 
@@ -2082,5 +2779,20 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
2082
  dependencies = [
2083
  "proc-macro2",
2084
  "quote",
2085
- "syn",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2086
  ]
 
17
  source = "registry+https://github.com/rust-lang/crates.io-index"
18
  checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
19
 
20
+ [[package]]
21
+ name = "ahash"
22
+ version = "0.7.8"
23
+ source = "registry+https://github.com/rust-lang/crates.io-index"
24
+ checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
25
+ dependencies = [
26
+ "getrandom",
27
+ "once_cell",
28
+ "version_check",
29
+ ]
30
+
31
  [[package]]
32
  name = "aho-corasick"
33
  version = "1.1.3"
 
37
  "memchr",
38
  ]
39
 
40
+ [[package]]
41
+ name = "alloc-no-stdlib"
42
+ version = "2.0.4"
43
+ source = "registry+https://github.com/rust-lang/crates.io-index"
44
+ checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
45
+
46
+ [[package]]
47
+ name = "alloc-stdlib"
48
+ version = "0.2.2"
49
+ source = "registry+https://github.com/rust-lang/crates.io-index"
50
+ checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
51
+ dependencies = [
52
+ "alloc-no-stdlib",
53
+ ]
54
+
55
  [[package]]
56
  name = "android-tzdata"
57
  version = "0.1.1"
 
79
  source = "registry+https://github.com/rust-lang/crates.io-index"
80
  checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522"
81
  dependencies = [
82
+ "brotli",
83
  "flate2",
84
  "futures-core",
85
  "memchr",
 
87
  "tokio",
88
  ]
89
 
 
 
 
 
 
 
 
 
 
 
 
90
  [[package]]
91
  name = "atomic-waker"
92
  version = "1.1.2"
 
101
 
102
  [[package]]
103
  name = "axum"
104
+ version = "0.8.1"
105
  source = "registry+https://github.com/rust-lang/crates.io-index"
106
+ checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
107
  dependencies = [
 
108
  "axum-core",
109
  "bytes",
110
+ "form_urlencoded",
111
  "futures-util",
112
  "http",
113
  "http-body",
 
135
 
136
  [[package]]
137
  name = "axum-core"
138
+ version = "0.5.0"
139
  source = "registry+https://github.com/rust-lang/crates.io-index"
140
+ checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733"
141
  dependencies = [
 
142
  "bytes",
143
  "futures-util",
144
  "http",
 
176
 
177
  [[package]]
178
  name = "bitflags"
179
+ version = "1.3.2"
180
+ source = "registry+https://github.com/rust-lang/crates.io-index"
181
+ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
182
+
183
+ [[package]]
184
+ name = "bitflags"
185
+ version = "2.8.0"
186
  source = "registry+https://github.com/rust-lang/crates.io-index"
187
+ checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
188
+
189
+ [[package]]
190
+ name = "bitvec"
191
+ version = "1.0.1"
192
+ source = "registry+https://github.com/rust-lang/crates.io-index"
193
+ checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
194
+ dependencies = [
195
+ "funty",
196
+ "radium",
197
+ "tap",
198
+ "wyz",
199
+ ]
200
 
201
  [[package]]
202
  name = "block-buffer"
 
207
  "generic-array",
208
  ]
209
 
210
+ [[package]]
211
+ name = "brotli"
212
+ version = "7.0.0"
213
+ source = "registry+https://github.com/rust-lang/crates.io-index"
214
+ checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
215
+ dependencies = [
216
+ "alloc-no-stdlib",
217
+ "alloc-stdlib",
218
+ "brotli-decompressor",
219
+ ]
220
+
221
+ [[package]]
222
+ name = "brotli-decompressor"
223
+ version = "4.0.1"
224
+ source = "registry+https://github.com/rust-lang/crates.io-index"
225
+ checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
226
+ dependencies = [
227
+ "alloc-no-stdlib",
228
+ "alloc-stdlib",
229
+ ]
230
+
231
  [[package]]
232
  name = "bumpalo"
233
  version = "3.16.0"
234
  source = "registry+https://github.com/rust-lang/crates.io-index"
235
  checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
236
 
237
+ [[package]]
238
+ name = "bytecheck"
239
+ version = "0.6.12"
240
+ source = "registry+https://github.com/rust-lang/crates.io-index"
241
+ checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
242
+ dependencies = [
243
+ "bytecheck_derive",
244
+ "ptr_meta 0.1.4",
245
+ "simdutf8",
246
+ ]
247
+
248
+ [[package]]
249
+ name = "bytecheck_derive"
250
+ version = "0.6.12"
251
+ source = "registry+https://github.com/rust-lang/crates.io-index"
252
+ checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
253
+ dependencies = [
254
+ "proc-macro2",
255
+ "quote",
256
+ "syn 1.0.109",
257
+ ]
258
+
259
+ [[package]]
260
+ name = "bytemuck"
261
+ version = "1.21.0"
262
+ source = "registry+https://github.com/rust-lang/crates.io-index"
263
+ checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
264
+
265
  [[package]]
266
  name = "byteorder"
267
  version = "1.5.0"
268
  source = "registry+https://github.com/rust-lang/crates.io-index"
269
  checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
270
 
271
+ [[package]]
272
+ name = "byteorder-lite"
273
+ version = "0.1.0"
274
+ source = "registry+https://github.com/rust-lang/crates.io-index"
275
+ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
276
+
277
  [[package]]
278
  name = "bytes"
279
  version = "1.9.0"
 
282
 
283
  [[package]]
284
  name = "cc"
285
+ version = "1.2.10"
286
  source = "registry+https://github.com/rust-lang/crates.io-index"
287
+ checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
288
  dependencies = [
289
  "shlex",
290
  ]
 
303
  dependencies = [
304
  "android-tzdata",
305
  "iana-time-zone",
 
306
  "num-traits",
307
+ "rkyv 0.7.45",
308
  "serde",
 
309
  "windows-targets",
310
  ]
311
 
312
+ [[package]]
313
+ name = "color_quant"
314
+ version = "1.1.0"
315
+ source = "registry+https://github.com/rust-lang/crates.io-index"
316
+ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
317
+
318
  [[package]]
319
  name = "core-foundation"
320
  version = "0.9.4"
 
361
 
362
  [[package]]
363
  name = "cursor-api"
364
+ version = "0.1.3-rc.4.3"
365
  dependencies = [
366
  "axum",
367
  "base64",
 
370
  "dotenvy",
371
  "flate2",
372
  "futures",
373
+ "gif",
374
  "hex",
375
+ "image",
376
+ "memmap2",
377
+ "parking_lot",
378
+ "paste",
379
  "prost",
380
  "prost-build",
381
  "rand",
382
  "regex",
383
  "reqwest",
384
+ "rkyv 0.7.45",
385
  "serde",
386
  "serde_json",
387
  "sha2",
388
+ "sonic-rs",
389
+ "sysinfo",
390
  "tokio",
391
  "tokio-stream",
392
  "tower-http",
393
+ "url",
394
  "uuid",
395
  ]
396
 
 
412
  dependencies = [
413
  "proc-macro2",
414
  "quote",
415
+ "syn 2.0.96",
416
  ]
417
 
418
  [[package]]
 
458
  source = "registry+https://github.com/rust-lang/crates.io-index"
459
  checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
460
 
461
+ [[package]]
462
+ name = "faststr"
463
+ version = "0.2.27"
464
+ source = "registry+https://github.com/rust-lang/crates.io-index"
465
+ checksum = "9154486833a83cb5d99de8c4d831314b8ae810dd4ef18d89ceb7a9c7c728dd74"
466
+ dependencies = [
467
+ "bytes",
468
+ "rkyv 0.8.10",
469
+ "serde",
470
+ "simdutf8",
471
+ ]
472
+
473
+ [[package]]
474
+ name = "fdeflate"
475
+ version = "0.3.7"
476
+ source = "registry+https://github.com/rust-lang/crates.io-index"
477
+ checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
478
+ dependencies = [
479
+ "simd-adler32",
480
+ ]
481
+
482
  [[package]]
483
  name = "fixedbitset"
484
  version = "0.4.2"
 
525
  "percent-encoding",
526
  ]
527
 
528
+ [[package]]
529
+ name = "funty"
530
+ version = "2.0.0"
531
+ source = "registry+https://github.com/rust-lang/crates.io-index"
532
+ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
533
+
534
  [[package]]
535
  name = "futures"
536
  version = "0.3.31"
 
575
  dependencies = [
576
  "proc-macro2",
577
  "quote",
578
+ "syn 2.0.96",
579
  ]
580
 
581
  [[package]]
 
629
  "wasi",
630
  ]
631
 
632
+ [[package]]
633
+ name = "gif"
634
+ version = "0.13.1"
635
+ source = "registry+https://github.com/rust-lang/crates.io-index"
636
+ checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
637
+ dependencies = [
638
+ "color_quant",
639
+ "weezl",
640
+ ]
641
+
642
  [[package]]
643
  name = "gimli"
644
  version = "0.31.1"
 
664
  "tracing",
665
  ]
666
 
667
+ [[package]]
668
+ name = "hashbrown"
669
+ version = "0.12.3"
670
+ source = "registry+https://github.com/rust-lang/crates.io-index"
671
+ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
672
+ dependencies = [
673
+ "ahash",
674
+ ]
675
+
676
  [[package]]
677
  name = "hashbrown"
678
  version = "0.15.2"
 
821
  "iana-time-zone-haiku",
822
  "js-sys",
823
  "wasm-bindgen",
824
+ "windows-core 0.52.0",
825
  ]
826
 
827
  [[package]]
 
948
  dependencies = [
949
  "proc-macro2",
950
  "quote",
951
+ "syn 2.0.96",
952
  ]
953
 
954
  [[package]]
 
972
  "icu_properties",
973
  ]
974
 
975
+ [[package]]
976
+ name = "image"
977
+ version = "0.25.5"
978
+ source = "registry+https://github.com/rust-lang/crates.io-index"
979
+ checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
980
+ dependencies = [
981
+ "bytemuck",
982
+ "byteorder-lite",
983
+ "color_quant",
984
+ "gif",
985
+ "image-webp",
986
+ "num-traits",
987
+ "png",
988
+ "zune-core",
989
+ "zune-jpeg",
990
+ ]
991
+
992
+ [[package]]
993
+ name = "image-webp"
994
+ version = "0.2.1"
995
+ source = "registry+https://github.com/rust-lang/crates.io-index"
996
+ checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f"
997
+ dependencies = [
998
+ "byteorder-lite",
999
+ "quick-error",
1000
+ ]
1001
+
1002
  [[package]]
1003
  name = "indexmap"
1004
+ version = "2.7.1"
1005
  source = "registry+https://github.com/rust-lang/crates.io-index"
1006
+ checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
1007
  dependencies = [
1008
  "equivalent",
1009
+ "hashbrown 0.15.2",
1010
  ]
1011
 
1012
  [[package]]
1013
  name = "ipnet"
1014
+ version = "2.11.0"
1015
  source = "registry+https://github.com/rust-lang/crates.io-index"
1016
+ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
1017
 
1018
  [[package]]
1019
  name = "itertools"
 
1032
 
1033
  [[package]]
1034
  name = "js-sys"
1035
+ version = "0.3.77"
1036
  source = "registry+https://github.com/rust-lang/crates.io-index"
1037
+ checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
1038
  dependencies = [
1039
  "once_cell",
1040
  "wasm-bindgen",
 
1048
 
1049
  [[package]]
1050
  name = "linux-raw-sys"
1051
+ version = "0.4.15"
1052
  source = "registry+https://github.com/rust-lang/crates.io-index"
1053
+ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
1054
 
1055
  [[package]]
1056
  name = "litemap"
 
1058
  source = "registry+https://github.com/rust-lang/crates.io-index"
1059
  checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
1060
 
1061
+ [[package]]
1062
+ name = "lock_api"
1063
+ version = "0.4.12"
1064
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1065
+ checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
1066
+ dependencies = [
1067
+ "autocfg",
1068
+ "scopeguard",
1069
+ ]
1070
+
1071
  [[package]]
1072
  name = "log"
1073
+ version = "0.4.25"
1074
  source = "registry+https://github.com/rust-lang/crates.io-index"
1075
+ checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
1076
 
1077
  [[package]]
1078
  name = "matchit"
1079
+ version = "0.8.4"
1080
  source = "registry+https://github.com/rust-lang/crates.io-index"
1081
+ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
1082
 
1083
  [[package]]
1084
  name = "memchr"
 
1086
  source = "registry+https://github.com/rust-lang/crates.io-index"
1087
  checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
1088
 
1089
+ [[package]]
1090
+ name = "memmap2"
1091
+ version = "0.9.5"
1092
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1093
+ checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
1094
+ dependencies = [
1095
+ "libc",
1096
+ ]
1097
+
1098
  [[package]]
1099
  name = "mime"
1100
  version = "0.3.17"
 
1103
 
1104
  [[package]]
1105
  name = "miniz_oxide"
1106
+ version = "0.8.3"
1107
  source = "registry+https://github.com/rust-lang/crates.io-index"
1108
+ checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
1109
  dependencies = [
1110
  "adler2",
1111
+ "simd-adler32",
1112
  ]
1113
 
1114
  [[package]]
 
1128
  source = "registry+https://github.com/rust-lang/crates.io-index"
1129
  checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
1130
 
1131
+ [[package]]
1132
+ name = "munge"
1133
+ version = "0.4.1"
1134
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1135
+ checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df"
1136
+ dependencies = [
1137
+ "munge_macro",
1138
+ ]
1139
+
1140
+ [[package]]
1141
+ name = "munge_macro"
1142
+ version = "0.4.1"
1143
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1144
+ checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e"
1145
+ dependencies = [
1146
+ "proc-macro2",
1147
+ "quote",
1148
+ "syn 2.0.96",
1149
+ ]
1150
+
1151
  [[package]]
1152
  name = "native-tls"
1153
  version = "0.2.12"
 
1165
  "tempfile",
1166
  ]
1167
 
1168
+ [[package]]
1169
+ name = "ntapi"
1170
+ version = "0.4.1"
1171
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1172
+ checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
1173
+ dependencies = [
1174
+ "winapi",
1175
+ ]
1176
+
1177
  [[package]]
1178
  name = "num-traits"
1179
  version = "0.2.19"
 
1204
  source = "registry+https://github.com/rust-lang/crates.io-index"
1205
  checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
1206
  dependencies = [
1207
+ "bitflags 2.8.0",
1208
  "cfg-if",
1209
  "foreign-types",
1210
  "libc",
 
1221
  dependencies = [
1222
  "proc-macro2",
1223
  "quote",
1224
+ "syn 2.0.96",
1225
  ]
1226
 
1227
  [[package]]
 
1242
  "vcpkg",
1243
  ]
1244
 
1245
+ [[package]]
1246
+ name = "parking_lot"
1247
+ version = "0.12.3"
1248
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1249
+ checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
1250
+ dependencies = [
1251
+ "lock_api",
1252
+ "parking_lot_core",
1253
+ ]
1254
+
1255
+ [[package]]
1256
+ name = "parking_lot_core"
1257
+ version = "0.9.10"
1258
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1259
+ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
1260
+ dependencies = [
1261
+ "cfg-if",
1262
+ "libc",
1263
+ "redox_syscall",
1264
+ "smallvec",
1265
+ "windows-targets",
1266
+ ]
1267
+
1268
+ [[package]]
1269
+ name = "paste"
1270
+ version = "1.0.15"
1271
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1272
+ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
1273
+
1274
  [[package]]
1275
  name = "percent-encoding"
1276
  version = "2.3.1"
 
1289
 
1290
  [[package]]
1291
  name = "pin-project-lite"
1292
+ version = "0.2.16"
1293
  source = "registry+https://github.com/rust-lang/crates.io-index"
1294
+ checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
1295
 
1296
  [[package]]
1297
  name = "pin-utils"
 
1305
  source = "registry+https://github.com/rust-lang/crates.io-index"
1306
  checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
1307
 
1308
+ [[package]]
1309
+ name = "png"
1310
+ version = "0.17.16"
1311
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1312
+ checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
1313
+ dependencies = [
1314
+ "bitflags 1.3.2",
1315
+ "crc32fast",
1316
+ "fdeflate",
1317
+ "flate2",
1318
+ "miniz_oxide",
1319
+ ]
1320
+
1321
  [[package]]
1322
  name = "ppv-lite86"
1323
  version = "0.2.20"
 
1329
 
1330
  [[package]]
1331
  name = "prettyplease"
1332
+ version = "0.2.29"
1333
  source = "registry+https://github.com/rust-lang/crates.io-index"
1334
+ checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
1335
  dependencies = [
1336
  "proc-macro2",
1337
+ "syn 2.0.96",
1338
  ]
1339
 
1340
  [[package]]
1341
  name = "proc-macro2"
1342
+ version = "1.0.93"
1343
  source = "registry+https://github.com/rust-lang/crates.io-index"
1344
+ checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
1345
  dependencies = [
1346
  "unicode-ident",
1347
  ]
 
1372
  "prost",
1373
  "prost-types",
1374
  "regex",
1375
+ "syn 2.0.96",
1376
  "tempfile",
1377
  ]
1378
 
 
1386
  "itertools",
1387
  "proc-macro2",
1388
  "quote",
1389
+ "syn 2.0.96",
1390
  ]
1391
 
1392
  [[package]]
 
1398
  "prost",
1399
  ]
1400
 
1401
+ [[package]]
1402
+ name = "ptr_meta"
1403
+ version = "0.1.4"
1404
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1405
+ checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
1406
+ dependencies = [
1407
+ "ptr_meta_derive 0.1.4",
1408
+ ]
1409
+
1410
+ [[package]]
1411
+ name = "ptr_meta"
1412
+ version = "0.3.0"
1413
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1414
+ checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90"
1415
+ dependencies = [
1416
+ "ptr_meta_derive 0.3.0",
1417
+ ]
1418
+
1419
+ [[package]]
1420
+ name = "ptr_meta_derive"
1421
+ version = "0.1.4"
1422
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1423
+ checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
1424
+ dependencies = [
1425
+ "proc-macro2",
1426
+ "quote",
1427
+ "syn 1.0.109",
1428
+ ]
1429
+
1430
+ [[package]]
1431
+ name = "ptr_meta_derive"
1432
+ version = "0.3.0"
1433
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1434
+ checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1"
1435
+ dependencies = [
1436
+ "proc-macro2",
1437
+ "quote",
1438
+ "syn 2.0.96",
1439
+ ]
1440
+
1441
+ [[package]]
1442
+ name = "quick-error"
1443
+ version = "2.0.1"
1444
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1445
+ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
1446
+
1447
  [[package]]
1448
  name = "quote"
1449
+ version = "1.0.38"
1450
  source = "registry+https://github.com/rust-lang/crates.io-index"
1451
+ checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
1452
  dependencies = [
1453
  "proc-macro2",
1454
  ]
1455
 
1456
+ [[package]]
1457
+ name = "radium"
1458
+ version = "0.7.0"
1459
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1460
+ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
1461
+
1462
+ [[package]]
1463
+ name = "rancor"
1464
+ version = "0.1.0"
1465
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1466
+ checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947"
1467
+ dependencies = [
1468
+ "ptr_meta 0.3.0",
1469
+ ]
1470
+
1471
  [[package]]
1472
  name = "rand"
1473
  version = "0.8.5"
 
1498
  "getrandom",
1499
  ]
1500
 
1501
+ [[package]]
1502
+ name = "redox_syscall"
1503
+ version = "0.5.8"
1504
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1505
+ checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
1506
+ dependencies = [
1507
+ "bitflags 2.8.0",
1508
+ ]
1509
+
1510
+ [[package]]
1511
+ name = "ref-cast"
1512
+ version = "1.0.23"
1513
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1514
+ checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931"
1515
+ dependencies = [
1516
+ "ref-cast-impl",
1517
+ ]
1518
+
1519
+ [[package]]
1520
+ name = "ref-cast-impl"
1521
+ version = "1.0.23"
1522
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1523
+ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
1524
+ dependencies = [
1525
+ "proc-macro2",
1526
+ "quote",
1527
+ "syn 2.0.96",
1528
+ ]
1529
+
1530
  [[package]]
1531
  name = "regex"
1532
  version = "1.11.1"
 
1556
  source = "registry+https://github.com/rust-lang/crates.io-index"
1557
  checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
1558
 
1559
+ [[package]]
1560
+ name = "rend"
1561
+ version = "0.4.2"
1562
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1563
+ checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
1564
+ dependencies = [
1565
+ "bytecheck",
1566
+ ]
1567
+
1568
+ [[package]]
1569
+ name = "rend"
1570
+ version = "0.5.2"
1571
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1572
+ checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215"
1573
+
1574
  [[package]]
1575
  name = "reqwest"
1576
+ version = "0.12.12"
1577
  source = "registry+https://github.com/rust-lang/crates.io-index"
1578
+ checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
1579
  dependencies = [
1580
  "async-compression",
1581
  "base64",
 
1607
  "system-configuration",
1608
  "tokio",
1609
  "tokio-native-tls",
1610
+ "tokio-socks",
1611
  "tokio-util",
1612
+ "tower",
1613
  "tower-service",
1614
  "url",
1615
  "wasm-bindgen",
 
1634
  "windows-sys 0.52.0",
1635
  ]
1636
 
1637
+ [[package]]
1638
+ name = "rkyv"
1639
+ version = "0.7.45"
1640
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1641
+ checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
1642
+ dependencies = [
1643
+ "bitvec",
1644
+ "bytecheck",
1645
+ "bytes",
1646
+ "hashbrown 0.12.3",
1647
+ "ptr_meta 0.1.4",
1648
+ "rend 0.4.2",
1649
+ "rkyv_derive 0.7.45",
1650
+ "seahash",
1651
+ "tinyvec",
1652
+ "uuid",
1653
+ ]
1654
+
1655
+ [[package]]
1656
+ name = "rkyv"
1657
+ version = "0.8.10"
1658
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1659
+ checksum = "1e147371c75553e1e2fcdb483944a8540b8438c31426279553b9a8182a9b7b65"
1660
+ dependencies = [
1661
+ "bytes",
1662
+ "hashbrown 0.15.2",
1663
+ "indexmap",
1664
+ "munge",
1665
+ "ptr_meta 0.3.0",
1666
+ "rancor",
1667
+ "rend 0.5.2",
1668
+ "rkyv_derive 0.8.10",
1669
+ "tinyvec",
1670
+ "uuid",
1671
+ ]
1672
+
1673
+ [[package]]
1674
+ name = "rkyv_derive"
1675
+ version = "0.7.45"
1676
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1677
+ checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
1678
+ dependencies = [
1679
+ "proc-macro2",
1680
+ "quote",
1681
+ "syn 1.0.109",
1682
+ ]
1683
+
1684
+ [[package]]
1685
+ name = "rkyv_derive"
1686
+ version = "0.8.10"
1687
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1688
+ checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a"
1689
+ dependencies = [
1690
+ "proc-macro2",
1691
+ "quote",
1692
+ "syn 2.0.96",
1693
+ ]
1694
+
1695
  [[package]]
1696
  name = "rustc-demangle"
1697
  version = "0.1.24"
 
1700
 
1701
  [[package]]
1702
  name = "rustix"
1703
+ version = "0.38.43"
1704
  source = "registry+https://github.com/rust-lang/crates.io-index"
1705
+ checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
1706
  dependencies = [
1707
+ "bitflags 2.8.0",
1708
  "errno",
1709
  "libc",
1710
  "linux-raw-sys",
 
1713
 
1714
  [[package]]
1715
  name = "rustls"
1716
+ version = "0.23.21"
1717
  source = "registry+https://github.com/rust-lang/crates.io-index"
1718
+ checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
1719
  dependencies = [
1720
  "once_cell",
1721
  "rustls-pki-types",
 
1752
 
1753
  [[package]]
1754
  name = "rustversion"
1755
+ version = "1.0.19"
1756
  source = "registry+https://github.com/rust-lang/crates.io-index"
1757
+ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
1758
 
1759
  [[package]]
1760
  name = "ryu"
 
1771
  "windows-sys 0.59.0",
1772
  ]
1773
 
1774
+ [[package]]
1775
+ name = "scopeguard"
1776
+ version = "1.2.0"
1777
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1778
+ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1779
+
1780
+ [[package]]
1781
+ name = "seahash"
1782
+ version = "4.1.0"
1783
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1784
+ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
1785
+
1786
  [[package]]
1787
  name = "security-framework"
1788
  version = "2.11.1"
1789
  source = "registry+https://github.com/rust-lang/crates.io-index"
1790
  checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
1791
  dependencies = [
1792
+ "bitflags 2.8.0",
1793
  "core-foundation",
1794
  "core-foundation-sys",
1795
  "libc",
 
1798
 
1799
  [[package]]
1800
  name = "security-framework-sys"
1801
+ version = "2.14.0"
1802
  source = "registry+https://github.com/rust-lang/crates.io-index"
1803
+ checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
1804
  dependencies = [
1805
  "core-foundation-sys",
1806
  "libc",
 
1808
 
1809
  [[package]]
1810
  name = "serde"
1811
+ version = "1.0.217"
1812
  source = "registry+https://github.com/rust-lang/crates.io-index"
1813
+ checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
1814
  dependencies = [
1815
  "serde_derive",
1816
  ]
1817
 
1818
  [[package]]
1819
  name = "serde_derive"
1820
+ version = "1.0.217"
1821
  source = "registry+https://github.com/rust-lang/crates.io-index"
1822
+ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
1823
  dependencies = [
1824
  "proc-macro2",
1825
  "quote",
1826
+ "syn 2.0.96",
1827
  ]
1828
 
1829
  [[package]]
1830
  name = "serde_json"
1831
+ version = "1.0.137"
1832
  source = "registry+https://github.com/rust-lang/crates.io-index"
1833
+ checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
1834
  dependencies = [
1835
  "itoa",
1836
  "memchr",
 
1877
  source = "registry+https://github.com/rust-lang/crates.io-index"
1878
  checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
1879
 
1880
+ [[package]]
1881
+ name = "signal-hook-registry"
1882
+ version = "1.4.2"
1883
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1884
+ checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
1885
+ dependencies = [
1886
+ "libc",
1887
+ ]
1888
+
1889
+ [[package]]
1890
+ name = "simd-adler32"
1891
+ version = "0.3.7"
1892
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1893
+ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
1894
+
1895
+ [[package]]
1896
+ name = "simdutf8"
1897
+ version = "0.1.5"
1898
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1899
+ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
1900
+
1901
  [[package]]
1902
  name = "slab"
1903
  version = "0.4.9"
 
1923
  "windows-sys 0.52.0",
1924
  ]
1925
 
1926
+ [[package]]
1927
+ name = "sonic-number"
1928
+ version = "0.1.0"
1929
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1930
+ checksum = "a8a74044c092f4f43ca7a6cfd62854cf9fb5ac8502b131347c990bf22bef1dfe"
1931
+ dependencies = [
1932
+ "cfg-if",
1933
+ ]
1934
+
1935
+ [[package]]
1936
+ name = "sonic-rs"
1937
+ version = "0.3.17"
1938
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1939
+ checksum = "0275f9f2f07d47556fe60c2759da8bc4be6083b047b491b2d476aa0bfa558eb1"
1940
+ dependencies = [
1941
+ "bumpalo",
1942
+ "bytes",
1943
+ "cfg-if",
1944
+ "faststr",
1945
+ "itoa",
1946
+ "ref-cast",
1947
+ "ryu",
1948
+ "serde",
1949
+ "simdutf8",
1950
+ "sonic-number",
1951
+ "sonic-simd",
1952
+ "thiserror 2.0.11",
1953
+ ]
1954
+
1955
+ [[package]]
1956
+ name = "sonic-simd"
1957
+ version = "0.1.0"
1958
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1959
+ checksum = "940a24e82c9a97483ef66cef06b92160a8fa5cd74042c57c10b24d99d169d2fc"
1960
+ dependencies = [
1961
+ "cfg-if",
1962
+ ]
1963
+
1964
  [[package]]
1965
  name = "spin"
1966
  version = "0.9.8"
 
1981
 
1982
  [[package]]
1983
  name = "syn"
1984
+ version = "1.0.109"
1985
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1986
+ checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
1987
+ dependencies = [
1988
+ "proc-macro2",
1989
+ "quote",
1990
+ "unicode-ident",
1991
+ ]
1992
+
1993
+ [[package]]
1994
+ name = "syn"
1995
+ version = "2.0.96"
1996
  source = "registry+https://github.com/rust-lang/crates.io-index"
1997
+ checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
1998
  dependencies = [
1999
  "proc-macro2",
2000
  "quote",
 
2018
  dependencies = [
2019
  "proc-macro2",
2020
  "quote",
2021
+ "syn 2.0.96",
2022
+ ]
2023
+
2024
+ [[package]]
2025
+ name = "sysinfo"
2026
+ version = "0.33.1"
2027
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2028
+ checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01"
2029
+ dependencies = [
2030
+ "core-foundation-sys",
2031
+ "libc",
2032
+ "memchr",
2033
+ "ntapi",
2034
+ "windows",
2035
  ]
2036
 
2037
  [[package]]
 
2040
  source = "registry+https://github.com/rust-lang/crates.io-index"
2041
  checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
2042
  dependencies = [
2043
+ "bitflags 2.8.0",
2044
  "core-foundation",
2045
  "system-configuration-sys",
2046
  ]
 
2055
  "libc",
2056
  ]
2057
 
2058
+ [[package]]
2059
+ name = "tap"
2060
+ version = "1.0.1"
2061
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2062
+ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
2063
+
2064
  [[package]]
2065
  name = "tempfile"
2066
+ version = "3.15.0"
2067
  source = "registry+https://github.com/rust-lang/crates.io-index"
2068
+ checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
2069
  dependencies = [
2070
  "cfg-if",
2071
  "fastrand",
2072
+ "getrandom",
2073
  "once_cell",
2074
  "rustix",
2075
  "windows-sys 0.59.0",
2076
  ]
2077
 
2078
+ [[package]]
2079
+ name = "thiserror"
2080
+ version = "1.0.69"
2081
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2082
+ checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
2083
+ dependencies = [
2084
+ "thiserror-impl 1.0.69",
2085
+ ]
2086
+
2087
+ [[package]]
2088
+ name = "thiserror"
2089
+ version = "2.0.11"
2090
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2091
+ checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
2092
+ dependencies = [
2093
+ "thiserror-impl 2.0.11",
2094
+ ]
2095
+
2096
+ [[package]]
2097
+ name = "thiserror-impl"
2098
+ version = "1.0.69"
2099
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2100
+ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
2101
+ dependencies = [
2102
+ "proc-macro2",
2103
+ "quote",
2104
+ "syn 2.0.96",
2105
+ ]
2106
+
2107
+ [[package]]
2108
+ name = "thiserror-impl"
2109
+ version = "2.0.11"
2110
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2111
+ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
2112
+ dependencies = [
2113
+ "proc-macro2",
2114
+ "quote",
2115
+ "syn 2.0.96",
2116
+ ]
2117
+
2118
  [[package]]
2119
  name = "tinystr"
2120
  version = "0.7.6"
 
2125
  "zerovec",
2126
  ]
2127
 
2128
+ [[package]]
2129
+ name = "tinyvec"
2130
+ version = "1.8.1"
2131
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2132
+ checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
2133
+ dependencies = [
2134
+ "tinyvec_macros",
2135
+ ]
2136
+
2137
+ [[package]]
2138
+ name = "tinyvec_macros"
2139
+ version = "0.1.1"
2140
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2141
+ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
2142
+
2143
  [[package]]
2144
  name = "tokio"
2145
+ version = "1.43.0"
2146
  source = "registry+https://github.com/rust-lang/crates.io-index"
2147
+ checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
2148
  dependencies = [
2149
  "backtrace",
2150
  "bytes",
2151
  "libc",
2152
  "mio",
2153
  "pin-project-lite",
2154
+ "signal-hook-registry",
2155
  "socket2",
2156
  "tokio-macros",
2157
  "windows-sys 0.52.0",
 
2159
 
2160
  [[package]]
2161
  name = "tokio-macros"
2162
+ version = "2.5.0"
2163
  source = "registry+https://github.com/rust-lang/crates.io-index"
2164
+ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
2165
  dependencies = [
2166
  "proc-macro2",
2167
  "quote",
2168
+ "syn 2.0.96",
2169
  ]
2170
 
2171
  [[package]]
 
2188
  "tokio",
2189
  ]
2190
 
2191
+ [[package]]
2192
+ name = "tokio-socks"
2193
+ version = "0.5.2"
2194
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2195
+ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f"
2196
+ dependencies = [
2197
+ "either",
2198
+ "futures-util",
2199
+ "thiserror 1.0.69",
2200
+ "tokio",
2201
+ ]
2202
+
2203
  [[package]]
2204
  name = "tokio-stream"
2205
  version = "0.1.17"
 
2246
  source = "registry+https://github.com/rust-lang/crates.io-index"
2247
  checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697"
2248
  dependencies = [
2249
+ "bitflags 2.8.0",
2250
  "bytes",
2251
  "http",
2252
+ "http-body",
2253
+ "http-body-util",
2254
  "pin-project-lite",
2255
  "tower-layer",
2256
  "tower-service",
 
2337
 
2338
  [[package]]
2339
  name = "uuid"
2340
+ version = "1.12.1"
2341
  source = "registry+https://github.com/rust-lang/crates.io-index"
2342
+ checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
2343
  dependencies = [
2344
  "getrandom",
2345
  ]
 
2373
 
2374
  [[package]]
2375
  name = "wasm-bindgen"
2376
+ version = "0.2.100"
2377
  source = "registry+https://github.com/rust-lang/crates.io-index"
2378
+ checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
2379
  dependencies = [
2380
  "cfg-if",
2381
  "once_cell",
2382
+ "rustversion",
2383
  "wasm-bindgen-macro",
2384
  ]
2385
 
2386
  [[package]]
2387
  name = "wasm-bindgen-backend"
2388
+ version = "0.2.100"
2389
  source = "registry+https://github.com/rust-lang/crates.io-index"
2390
+ checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
2391
  dependencies = [
2392
  "bumpalo",
2393
  "log",
2394
  "proc-macro2",
2395
  "quote",
2396
+ "syn 2.0.96",
2397
  "wasm-bindgen-shared",
2398
  ]
2399
 
2400
  [[package]]
2401
  name = "wasm-bindgen-futures"
2402
+ version = "0.4.50"
2403
  source = "registry+https://github.com/rust-lang/crates.io-index"
2404
+ checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
2405
  dependencies = [
2406
  "cfg-if",
2407
  "js-sys",
 
2412
 
2413
  [[package]]
2414
  name = "wasm-bindgen-macro"
2415
+ version = "0.2.100"
2416
  source = "registry+https://github.com/rust-lang/crates.io-index"
2417
+ checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
2418
  dependencies = [
2419
  "quote",
2420
  "wasm-bindgen-macro-support",
 
2422
 
2423
  [[package]]
2424
  name = "wasm-bindgen-macro-support"
2425
+ version = "0.2.100"
2426
  source = "registry+https://github.com/rust-lang/crates.io-index"
2427
+ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
2428
  dependencies = [
2429
  "proc-macro2",
2430
  "quote",
2431
+ "syn 2.0.96",
2432
  "wasm-bindgen-backend",
2433
  "wasm-bindgen-shared",
2434
  ]
2435
 
2436
  [[package]]
2437
  name = "wasm-bindgen-shared"
2438
+ version = "0.2.100"
2439
  source = "registry+https://github.com/rust-lang/crates.io-index"
2440
+ checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
2441
+ dependencies = [
2442
+ "unicode-ident",
2443
+ ]
2444
 
2445
  [[package]]
2446
  name = "wasm-streams"
 
2457
 
2458
  [[package]]
2459
  name = "web-sys"
2460
+ version = "0.3.77"
2461
  source = "registry+https://github.com/rust-lang/crates.io-index"
2462
+ checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
2463
  dependencies = [
2464
  "js-sys",
2465
  "wasm-bindgen",
2466
  ]
2467
 
2468
+ [[package]]
2469
+ name = "weezl"
2470
+ version = "0.1.8"
2471
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2472
+ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
2473
+
2474
+ [[package]]
2475
+ name = "winapi"
2476
+ version = "0.3.9"
2477
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2478
+ checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
2479
+ dependencies = [
2480
+ "winapi-i686-pc-windows-gnu",
2481
+ "winapi-x86_64-pc-windows-gnu",
2482
+ ]
2483
+
2484
+ [[package]]
2485
+ name = "winapi-i686-pc-windows-gnu"
2486
+ version = "0.4.0"
2487
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2488
+ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
2489
+
2490
+ [[package]]
2491
+ name = "winapi-x86_64-pc-windows-gnu"
2492
+ version = "0.4.0"
2493
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2494
+ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
2495
+
2496
+ [[package]]
2497
+ name = "windows"
2498
+ version = "0.57.0"
2499
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2500
+ checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
2501
+ dependencies = [
2502
+ "windows-core 0.57.0",
2503
+ "windows-targets",
2504
+ ]
2505
+
2506
  [[package]]
2507
  name = "windows-core"
2508
  version = "0.52.0"
 
2512
  "windows-targets",
2513
  ]
2514
 
2515
+ [[package]]
2516
+ name = "windows-core"
2517
+ version = "0.57.0"
2518
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2519
+ checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
2520
+ dependencies = [
2521
+ "windows-implement",
2522
+ "windows-interface",
2523
+ "windows-result 0.1.2",
2524
+ "windows-targets",
2525
+ ]
2526
+
2527
+ [[package]]
2528
+ name = "windows-implement"
2529
+ version = "0.57.0"
2530
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2531
+ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
2532
+ dependencies = [
2533
+ "proc-macro2",
2534
+ "quote",
2535
+ "syn 2.0.96",
2536
+ ]
2537
+
2538
+ [[package]]
2539
+ name = "windows-interface"
2540
+ version = "0.57.0"
2541
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2542
+ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
2543
+ dependencies = [
2544
+ "proc-macro2",
2545
+ "quote",
2546
+ "syn 2.0.96",
2547
+ ]
2548
+
2549
  [[package]]
2550
  name = "windows-registry"
2551
  version = "0.2.0"
2552
  source = "registry+https://github.com/rust-lang/crates.io-index"
2553
  checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
2554
  dependencies = [
2555
+ "windows-result 0.2.0",
2556
  "windows-strings",
2557
  "windows-targets",
2558
  ]
2559
 
2560
+ [[package]]
2561
+ name = "windows-result"
2562
+ version = "0.1.2"
2563
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2564
+ checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
2565
+ dependencies = [
2566
+ "windows-targets",
2567
+ ]
2568
+
2569
  [[package]]
2570
  name = "windows-result"
2571
  version = "0.2.0"
 
2581
  source = "registry+https://github.com/rust-lang/crates.io-index"
2582
  checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
2583
  dependencies = [
2584
+ "windows-result 0.2.0",
2585
  "windows-targets",
2586
  ]
2587
 
 
2679
  source = "registry+https://github.com/rust-lang/crates.io-index"
2680
  checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
2681
 
2682
+ [[package]]
2683
+ name = "wyz"
2684
+ version = "0.5.1"
2685
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2686
+ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
2687
+ dependencies = [
2688
+ "tap",
2689
+ ]
2690
+
2691
  [[package]]
2692
  name = "yoke"
2693
  version = "0.7.5"
 
2708
  dependencies = [
2709
  "proc-macro2",
2710
  "quote",
2711
+ "syn 2.0.96",
2712
  "synstructure",
2713
  ]
2714
 
 
2730
  dependencies = [
2731
  "proc-macro2",
2732
  "quote",
2733
+ "syn 2.0.96",
2734
  ]
2735
 
2736
  [[package]]
 
2750
  dependencies = [
2751
  "proc-macro2",
2752
  "quote",
2753
+ "syn 2.0.96",
2754
  "synstructure",
2755
  ]
2756
 
 
2779
  dependencies = [
2780
  "proc-macro2",
2781
  "quote",
2782
+ "syn 2.0.96",
2783
+ ]
2784
+
2785
+ [[package]]
2786
+ name = "zune-core"
2787
+ version = "0.4.12"
2788
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2789
+ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
2790
+
2791
+ [[package]]
2792
+ name = "zune-jpeg"
2793
+ version = "0.4.14"
2794
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2795
+ checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028"
2796
+ dependencies = [
2797
+ "zune-core",
2798
  ]
Cargo.toml CHANGED
@@ -1,50 +1,55 @@
1
  [package]
2
  name = "cursor-api"
3
- version = "0.1.0"
4
  edition = "2021"
5
  authors = ["wisdgod <nav@wisdgod.com>"]
 
 
6
 
7
  [build-dependencies]
8
  prost-build = "0.13.4"
 
 
9
 
10
  [dependencies]
11
- axum = { version = "0.7.9", features = ["json"] }
12
  base64 = { version = "0.22.1", default-features = false, features = ["std"] }
 
13
  bytes = "1.9.0"
14
- chrono = { version = "0.4.39", features = ["serde"] }
15
  dotenvy = "0.15.7"
16
  flate2 = { version = "1.0.35", default-features = false, features = ["rust_backend"] }
17
  futures = { version = "0.3.31", default-features = false, features = ["std"] }
 
18
  hex = { version = "0.4.3", default-features = false, features = ["std"] }
 
 
 
 
 
19
  prost = "0.13.4"
20
  rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"] }
21
  regex = { version = "1.11.1", default-features = false, features = ["std", "perf"] }
22
- reqwest = { version = "0.12.9", features = ["json", "gzip", "stream"] }
23
- serde = { version = "1.0.216", features = ["derive"] }
24
- serde_json = { version = "1.0.134", features = ["std"] }
 
 
25
  sha2 = { version = "0.10.8", default-features = false }
26
- tokio = { version = "1.42.0", features = ["rt-multi-thread", "macros", "net", "sync", "time"] }
 
27
  tokio-stream = { version = "0.1.17", features = ["time"] }
28
- tower-http = { version = "0.6.2", features = ["cors"] }
29
- uuid = { version = "1.11.0", features = ["v4"] }
 
30
 
31
- # 优化设置
32
  [profile.release]
33
- lto = true # 启用链接时优化
34
- codegen-units = 1 # 减少并行编译单元以提高优化
35
- panic = 'abort' # 在 panic 时直接终止,减小二进制大小
36
- strip = true # 移除调试符号
37
- opt-level = 3 # 最高优化级别
38
-
39
- # 构建脚本设置
40
- [package.metadata.cross.target.x86_64-unknown-linux-gnu]
41
- image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main"
42
-
43
- [package.metadata.cross.target.aarch64-unknown-linux-gnu]
44
- image = "ghcr.io/cross-rs/aarch64-unknown-linux-gnu:main"
45
-
46
- [package.metadata.cross.target.x86_64-apple-darwin]
47
- image = "ghcr.io/cross-rs/x86_64-apple-darwin:main"
48
 
49
- [package.metadata.cross.target.aarch64-apple-darwin]
50
- image = "ghcr.io/cross-rs/aarch64-apple-darwin:main"
 
 
1
  [package]
2
  name = "cursor-api"
3
+ version = "0.1.3-rc.4.3"
4
  edition = "2021"
5
  authors = ["wisdgod <nav@wisdgod.com>"]
6
+ description = "OpenAI format compatibility layer for the Cursor API"
7
+ repository = "https://github.com/wisdgod/cursor-api"
8
 
9
  [build-dependencies]
10
  prost-build = "0.13.4"
11
+ sha2 = { version = "0.10.8", default-features = false }
12
+ serde_json = "1.0.134"
13
 
14
  [dependencies]
15
+ axum = { version = "0.8.1", features = ["json"] }
16
  base64 = { version = "0.22.1", default-features = false, features = ["std"] }
17
+ # brotli = { version = "7.0.0", default-features = false, features = ["std"] }
18
  bytes = "1.9.0"
19
+ chrono = { version = "0.4.39", default-features = false, features = ["std", "clock", "now", "serde", "rkyv-64"] }
20
  dotenvy = "0.15.7"
21
  flate2 = { version = "1.0.35", default-features = false, features = ["rust_backend"] }
22
  futures = { version = "0.3.31", default-features = false, features = ["std"] }
23
+ gif = { version = "0.13.1", default-features = false, features = ["std"] }
24
  hex = { version = "0.4.3", default-features = false, features = ["std"] }
25
+ image = { version = "0.25.5", default-features = false, features = ["jpeg", "png", "gif", "webp"] }
26
+ memmap2 = "0.9.5"
27
+ # openssl = { version = "0.10.68", features = ["vendored"] }
28
+ parking_lot = "0.12.3"
29
+ paste = "1.0.15"
30
  prost = "0.13.4"
31
  rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"] }
32
  regex = { version = "1.11.1", default-features = false, features = ["std", "perf"] }
33
+ reqwest = { version = "0.12.12", default-features = false, features = ["gzip", "brotli", "json", "stream", "socks", "__tls", "charset", "default-tls", "h2", "http2", "macos-system-configuration"] }
34
+ rkyv = { version = "0.7.45", default-features = false, features = ["alloc", "std", "bytecheck", "size_64", "validation", "std"] }
35
+ serde = { version = "1.0.217", default-features = false, features = ["std", "derive"] }
36
+ serde_json = { package = "sonic-rs", version = "0.3.17" }
37
+ # serde_json = "1.0.137"
38
  sha2 = { version = "0.10.8", default-features = false }
39
+ sysinfo = { version = "0.33.1", default-features = false, features = ["system"] }
40
+ tokio = { version = "1.43.0", features = ["rt-multi-thread", "macros", "net", "sync", "time", "fs", "signal"] }
41
  tokio-stream = { version = "0.1.17", features = ["time"] }
42
+ tower-http = { version = "0.6.2", features = ["cors", "limit"] }
43
+ url = { version = "2.5.4", default-features = false }
44
+ uuid = { version = "1.12.1", features = ["v4"] }
45
 
 
46
  [profile.release]
47
+ lto = true
48
+ codegen-units = 1
49
+ panic = 'abort'
50
+ strip = true
51
+ opt-level = 3
 
 
 
 
 
 
 
 
 
 
52
 
53
+ [features]
54
+ default = []
55
+ use-minified = []
Cross.toml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ [target.x86_64-unknown-linux-gnu]
2
+ dockerfile = "Dockerfile.cross"
3
+
4
+ [target.aarch64-unknown-linux-gnu]
5
+ dockerfile = "Dockerfile.cross.arm64"
Cursor API.md ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Cursor API
2
+
3
+ ## 项目说明
4
+
5
+ ### 版本声明
6
+ - 当前版本已进入稳定阶段
7
+ - 以下问题与程序无关,请勿反馈:
8
+ - 响应缺字漏字
9
+ - 首字延迟现象
10
+ - 响应出现乱码
11
+ - 性能优势:
12
+ - 达到原生客户端响应速度
13
+ - 部分场景下表现更优
14
+ - 开源协议要求:
15
+ - Fork 项目禁止以原作者名义进行宣传推广
16
+ - 禁止发布任何形式的官方声明
17
+
18
+ ![Cursor API 架构示意图](https://via.placeholder.com/800x400.png?text=Cursor+API+Architecture)
19
+
20
+ ## 快速入门
21
+
22
+ ### 密钥获取
23
+ 1. 访问 [Cursor 官网](https://www.cursor.com) 完成注册登录
24
+ 2. 开启浏览器开发者工具 (F12)
25
+ 3. 在 Application → Cookies 中定位 `WorkosCursorSessionToken`
26
+ 4. 复制第三个字段值(注意:`%3A%3A` 为 `::` 的 URL 编码形式)
27
+
28
+ ## 配置指南
29
+
30
+ ### 环境变量
31
+ | 变量名 | 类型 | 默认值 | 说明 |
32
+ |--------|------|--------|-----|
33
+ | PORT | int | 3000 | 服务端口号 |
34
+ | AUTH_TOKEN | string | 无 | 认证令牌(必需) |
35
+ | ROUTE_PREFIX | string | 无 | 路由前缀 |
36
+ | TOKEN_LIST_FILE | string | .tokens | Token 存储文件 |
37
+
38
+ 完整配置参见 [env-example](/env-example)
39
+
40
+ ### Token 文件规范
41
+ `.tokens` 文件格式:
42
+ ```plaintext
43
+ # 注释行将在下次读取时自动删除
44
+ token1,checksum1
45
+ token2,checksum2
46
+ ```
47
+
48
+ 文件管理原则:
49
+ - 系统自动维护文件内容
50
+ - 仅以下情况需要手动编辑:
51
+ - 删除特定 token
52
+ - 绑定已有 checksum 到指定 token
53
+
54
+ ## 模型支持列表
55
+ ```json
56
+ [
57
+ "claude-3.5-sonnet",
58
+ "gpt-4",
59
+ "gpt-4o",
60
+ "cursor-fast",
61
+ "gpt-4o-mini",
62
+ "deepseek-v3"
63
+ ]
64
+ ```
65
+ *注:模型列表为固定配置,暂不支持自定义扩展*
66
+
67
+ ## API 文档
68
+
69
+ ### 基础对话接口
70
+ **Endpoint**
71
+ `POST /v1/chat/completions`
72
+
73
+ **认证方式**
74
+ `Bearer Token` 三级认证机制:
75
+ 1. 环境变量 `AUTH_TOKEN`
76
+ 2. `.token` 文件轮询
77
+ 3. 直接 token,checksum 认证(v0.1.3-rc.3+)
78
+
79
+ **请求示例**
80
+ ```json
81
+ {
82
+ "model": "gpt-4",
83
+ "messages": [
84
+ {
85
+ "role": "user",
86
+ "content": "解释量子计算的基本原理"
87
+ }
88
+ ],
89
+ "stream": false
90
+ }
91
+ ```
92
+
93
+ **响应示例(非流式)**
94
+ ```json
95
+ {
96
+ "id": "chatcmpl-9Xy...",
97
+ "object": "chat.completion",
98
+ "created": 1628063500,
99
+ "model": "gpt-4",
100
+ "choices": [{
101
+ "index": 0,
102
+ "message": {
103
+ "role": "assistant",
104
+ "content": "量子计算基于量子比特..."
105
+ },
106
+ "finish_reason": "stop"
107
+ }]
108
+ }
109
+ ```
110
+
111
+ ### Token 管理接口
112
+ | 端点 | 方法 | 功能 |
113
+ |------|------|-----|
114
+ | `/tokens` | GET | Token 信息管理界面 |
115
+ | `/tokens/update` | POST | 批量更新 Token 列表 |
116
+ | `/tokens/add` | POST | 增量添加 Token |
117
+ | `/tokens/delete` | POST | 删除指定 Token |
118
+
119
+ ```mermaid
120
+ sequenceDiagram
121
+ participant Client
122
+ participant API
123
+ Client->>API: POST /tokens/add
124
+ API->>API: 验证Token有效性
125
+ API->>File: 写入.tokens
126
+ API-->>Client: 返回更新结果
127
+ ```
128
+
129
+ ## 高级功能
130
+
131
+ ### 动态密钥生成
132
+ **Endpoint**
133
+ `POST /build-key`
134
+
135
+ **优势对比**
136
+ | 特性 | 传统模式 | 动态密钥 |
137
+ |------|---------|---------|
138
+ | 密钥长度 | 较长 | 优化缩短 |
139
+ | 配置扩展 | 无 | 支持自定义 |
140
+ | 安全等级 | 基础 | 增强编码 |
141
+ | 验证效率 | 预校验耗时 | 即时验证 |
142
+
143
+ ## 系统监控
144
+
145
+ ### 健康检查
146
+ **Endpoint**
147
+ `GET /health`
148
+
149
+ **响应示例**
150
+ ```json
151
+ {
152
+ "status": "success",
153
+ "version": "1.2.0",
154
+ "uptime": 86400,
155
+ "models": ["gpt-4", "claude-3.5"],
156
+ "endpoints": ["/v1/chat", "/tokens"]
157
+ }
158
+ ```
159
+
160
+ ## 生态工具
161
+
162
+ ### 开发辅助工具
163
+ - [Token 获取工具](https://github.com/wisdgod/cursor-api/tree/main/tools/get-token)
164
+ 支持 Windows/Linux/macOS 系统
165
+ - [遥测数据重置工具](https://github.com/wisdgod/cursor-api/tree/main/tools/reset-telemetry)
166
+ 清除用户使用数据记录
167
+
168
+ ## 致谢声明
169
+ 本项目的发展离不开以下开源项目的启发:
170
+ - [zhx47/cursor-api](https://github.com/zhx47/cursor-api) - 基础架构参考
171
+ - [cursorToApi](https://github.com/luolazyandlazy/cursorToApi) - 认证机制优化方案
172
+
173
+ ---
174
+
175
+ > **项目维护说明**
176
+ > 我们欢迎社区贡献,但请注意:
177
+ > 1. 功能请求需附带使用场景说明
178
+ > 2. Bug 报告请提供复现步骤和环境信息
179
+ > 3. 重要变更需通过 CI/CD 测试流程
Dockerfile CHANGED
@@ -1,48 +1,36 @@
1
- # 构建阶段
2
- FROM rust:1.83.0-slim-bookworm as builder
3
 
4
- WORKDIR /app
5
 
6
- # 安装构建依赖
7
  RUN apt-get update && \
8
  apt-get install -y --no-install-recommends \
9
- build-essential \
10
- protobuf-compiler \
11
- pkg-config \
12
- libssl-dev \
13
- nodejs \
14
- npm \
15
  && rm -rf /var/lib/apt/lists/*
16
 
17
- # 复制项目文件
18
  COPY . .
19
-
20
- # 构建
21
- RUN rustup target add x86_64-unknown-linux-gnu && \
22
- cargo build --target x86_64-unknown-linux-gnu --release && \
23
- cp target/x86_64-unknown-linux-gnu/release/cursor-api /app/cursor-api
 
 
24
 
25
  # 运行阶段
26
- FROM debian:bookworm-slim
27
 
28
  WORKDIR /app
29
-
30
  ENV TZ=Asia/Shanghai
31
 
32
- # 安装运行时依赖
33
  RUN apt-get update && \
34
  apt-get install -y --no-install-recommends \
35
- ca-certificates \
36
- tzdata \
37
  && rm -rf /var/lib/apt/lists/*
38
 
39
- # 复制构建产物
40
  COPY --from=builder /app/cursor-api .
41
 
42
- # 设置默认端口
43
  ENV PORT=3000
44
-
45
- # 动态暴露端口
46
  EXPOSE ${PORT}
47
-
48
  CMD ["./cursor-api"]
 
1
+ ARG TARGETARCH
2
+ FROM --platform=linux/${TARGETARCH} rust:1.84.0-slim-bookworm as builder
3
 
4
+ ARG TARGETARCH
5
 
6
+ WORKDIR /app
7
  RUN apt-get update && \
8
  apt-get install -y --no-install-recommends \
9
+ build-essential protobuf-compiler pkg-config libssl-dev nodejs npm openssl \
 
 
 
 
 
10
  && rm -rf /var/lib/apt/lists/*
11
 
 
12
  COPY . .
13
+ RUN case "$TARGETARCH" in \
14
+ amd64) TARGET_CPU="x86-64-v3" ;; \
15
+ arm64) TARGET_CPU="neoverse-n1" ;; \
16
+ *) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; \
17
+ esac && \
18
+ RUSTFLAGS="-C link-arg=-s -C target-cpu=$TARGET_CPU" cargo build --release && \
19
+ cp target/release/cursor-api /app/cursor-api
20
 
21
  # 运行阶段
22
+ FROM --platform=linux/${TARGETARCH} debian:bookworm-slim
23
 
24
  WORKDIR /app
 
25
  ENV TZ=Asia/Shanghai
26
 
 
27
  RUN apt-get update && \
28
  apt-get install -y --no-install-recommends \
29
+ ca-certificates tzdata openssl \
 
30
  && rm -rf /var/lib/apt/lists/*
31
 
 
32
  COPY --from=builder /app/cursor-api .
33
 
 
34
  ENV PORT=3000
 
 
35
  EXPOSE ${PORT}
 
36
  CMD ["./cursor-api"]
Dockerfile.cross ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile.cross
2
+
3
+ FROM --platform=linux/amd64 rust:1.84.0-slim-bookworm
4
+
5
+ WORKDIR /app
6
+
7
+ # 安装必要的软件包
8
+ RUN apt-get update && \
9
+ apt-get install -y --no-install-recommends \
10
+ build-essential \
11
+ pkg-config \
12
+ libssl-dev \
13
+ protobuf-compiler \
14
+ openssl \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ # 设置环境变量 (如果需要)
18
+ # ENV RUSTFLAGS="-C link-arg=-s"
19
+
20
+ # 设置 PROTOC 环境变量 (因为你的 build.rs 需要)
21
+ ENV PROTOC=/usr/bin/protoc
22
+
23
+ # 安装特定版本的 protoc (如果你需要特定版本,例如 29.3;否则可以删除这部分)
24
+ # ENV PROTOC_VERSION=29.3
25
+ # ENV PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-x86_64.zip
26
+ # RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${PROTOC_ZIP} -O /tmp/${PROTOC_ZIP} && \
27
+ # unzip /tmp/${PROTOC_ZIP} -d /usr && \
28
+ # rm /tmp/${PROTOC_ZIP}
29
+
30
+ # 验证安装
31
+ RUN protoc --version
Dockerfile.cross.arm64 ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile.cross
2
+
3
+ FROM --platform=linux/arm64 rust:1.84.0-slim-bookworm
4
+
5
+ WORKDIR /app
6
+
7
+ # 安装必要的软件包
8
+ RUN apt-get update && \
9
+ apt-get install -y --no-install-recommends \
10
+ build-essential \
11
+ pkg-config \
12
+ libssl-dev \
13
+ protobuf-compiler \
14
+ openssl \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ # 设置环境变量 (如果需要)
18
+ # ENV RUSTFLAGS="-C link-arg=-s"
19
+
20
+ # 设置 PROTOC 环境变量 (因为你的 build.rs 需要)
21
+ ENV PROTOC=/usr/bin/protoc
22
+
23
+ # 安装特定版本的 protoc (如果你需要特定版本,例如 29.3;否则可以删除这部分)
24
+ # ENV PROTOC_VERSION=29.3
25
+ # ENV PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-x86_64.zip
26
+ # RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${PROTOC_ZIP} -O /tmp/${PROTOC_ZIP} && \
27
+ # unzip /tmp/${PROTOC_ZIP} -d /usr && \
28
+ # rm /tmp/${PROTOC_ZIP}
29
+
30
+ # 验证安装
31
+ RUN protoc --version
LICENSE ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License with Attribution Restrictions (MIT-AR)
2
+ Copyright (c) 2025 wisdgod <nav@wisdgod.com>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ 1. The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ 2. Any public reference to this software in promotional materials must include
15
+ the following disclaimer in a prominent position:
16
+ "This product utilizes components developed by third-party contributors.
17
+ There is no affiliation, endorsement, or sponsorship by the original author."
18
+
19
+ 3. Explicit prohibition against:
20
+ a) Using the author's name/alias in marketing collateral
21
+ b) Suggesting official certification or partnership
22
+ c) Using project name as technical endorsement
23
+
24
+ 4. Violation of these terms automatically terminates granted rights and
25
+ requires immediate cessation of software use within 72 hours.
26
+
27
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
+ SOFTWARE.
34
+
35
+ --- Special Provisions ---
36
+ * This is a modified MIT license approved by SPDX as "MIT-AR" (Attribution-Restricted)
37
+ * Commercial users may request certification waiver via nav@wisdgod.com
38
+ * Community projects may display "Powered by" logo pack available at /branding
README.md DELETED
@@ -1,10 +0,0 @@
1
- ---
2
- title: Cursor
3
- emoji: 👁
4
- colorFrom: green
5
- colorTo: red
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
build.rs CHANGED
@@ -1,15 +1,27 @@
 
 
 
 
 
 
1
  use std::io::Result;
 
2
  use std::path::Path;
 
 
3
  use std::process::Command;
4
 
 
 
 
 
 
5
  fn check_and_install_deps() -> Result<()> {
6
  let scripts_dir = Path::new("scripts");
7
  let node_modules = scripts_dir.join("node_modules");
8
 
9
- // 如果 node_modules 不存在,运行 npm install
10
  if !node_modules.exists() {
11
- println!("cargo:warning=Installing HTML minifier dependencies...");
12
-
13
  let status = Command::new("npm")
14
  .current_dir(scripts_dir)
15
  .arg("install")
@@ -23,38 +35,186 @@ fn check_and_install_deps() -> Result<()> {
23
  Ok(())
24
  }
25
 
26
- fn minify_html() -> Result<()> {
27
- println!("cargo:warning=Minifying HTML files...");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  let status = Command::new("node")
30
- .args(&["scripts/minify-html.js"])
 
31
  .status()?;
32
 
33
  if !status.success() {
34
- panic!("HTML minification failed");
35
  }
 
 
 
 
36
  Ok(())
37
  }
38
 
39
  fn main() -> Result<()> {
40
  // Proto 文件处理
41
- println!("cargo:rerun-if-changed=src/message.proto");
 
 
 
 
 
 
 
 
 
 
42
  let mut config = prost_build::Config::new();
43
- config.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]");
 
 
 
 
 
 
 
 
 
 
44
  config
45
- .compile_protos(&["src/message.proto"], &["src/"])
46
  .unwrap();
47
 
48
- // HTML 文件处理
49
- println!("cargo:rerun-if-changed=static/tokeninfo.html");
50
- println!("cargo:rerun-if-changed=scripts/minify-html.js");
51
  println!("cargo:rerun-if-changed=scripts/package.json");
 
 
 
 
 
 
 
 
52
 
53
- // 检查并安装依赖
54
- check_and_install_deps()?;
 
 
55
 
56
- // 运行 HTML 压缩
57
- minify_html()?;
 
58
 
59
  Ok(())
60
  }
 
1
+ #[cfg(not(any(feature = "use-minified")))]
2
+ use sha2::{Digest, Sha256};
3
+ #[cfg(not(any(feature = "use-minified")))]
4
+ use std::collections::HashMap;
5
+ #[cfg(not(any(feature = "use-minified")))]
6
+ use std::fs;
7
  use std::io::Result;
8
+ #[cfg(not(any(feature = "use-minified")))]
9
  use std::path::Path;
10
+ use std::path::PathBuf;
11
+ #[cfg(not(any(feature = "use-minified")))]
12
  use std::process::Command;
13
 
14
+ // 支持的文件类型
15
+ #[cfg(not(any(feature = "use-minified")))]
16
+ const SUPPORTED_EXTENSIONS: [&str; 4] = ["html", "js", "css", "md"];
17
+
18
+ #[cfg(not(any(feature = "use-minified")))]
19
  fn check_and_install_deps() -> Result<()> {
20
  let scripts_dir = Path::new("scripts");
21
  let node_modules = scripts_dir.join("node_modules");
22
 
 
23
  if !node_modules.exists() {
24
+ println!("cargo:warning=Installing minifier dependencies...");
 
25
  let status = Command::new("npm")
26
  .current_dir(scripts_dir)
27
  .arg("install")
 
35
  Ok(())
36
  }
37
 
38
+ #[cfg(not(any(feature = "use-minified")))]
39
+ fn get_files_hash() -> Result<HashMap<PathBuf, String>> {
40
+ let mut file_hashes = HashMap::new();
41
+ let static_dir = Path::new("static");
42
+
43
+ // 首先处理 README.md
44
+ let readme_path = Path::new("README.md");
45
+ if readme_path.exists() {
46
+ let content = fs::read(readme_path)?;
47
+ let mut hasher = Sha256::new();
48
+ hasher.update(&content);
49
+ let hash = format!("{:x}", hasher.finalize());
50
+ file_hashes.insert(readme_path.to_path_buf(), hash);
51
+ }
52
+
53
+ if static_dir.exists() {
54
+ for entry in fs::read_dir(static_dir)? {
55
+ let entry = entry?;
56
+ let path = entry.path();
57
+
58
+ // 检查是否是支持的文件类型,且不是已经压缩的文件
59
+ if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
60
+ if SUPPORTED_EXTENSIONS.contains(&ext) && !path.to_string_lossy().contains(".min.")
61
+ {
62
+ let content = fs::read(&path)?;
63
+ let mut hasher = Sha256::new();
64
+ hasher.update(&content);
65
+ let hash = format!("{:x}", hasher.finalize());
66
+ file_hashes.insert(path, hash);
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ Ok(file_hashes)
73
+ }
74
+
75
+ #[cfg(not(any(feature = "use-minified")))]
76
+ fn load_saved_hashes() -> Result<HashMap<PathBuf, String>> {
77
+ let hash_file = Path::new("scripts/.asset-hashes.json");
78
+ if hash_file.exists() {
79
+ let content = fs::read_to_string(hash_file)?;
80
+ let hash_map: HashMap<String, String> = serde_json::from_str(&content)?;
81
+ Ok(hash_map
82
+ .into_iter()
83
+ .map(|(k, v)| (PathBuf::from(k), v))
84
+ .collect())
85
+ } else {
86
+ Ok(HashMap::new())
87
+ }
88
+ }
89
+
90
+ #[cfg(not(any(feature = "use-minified")))]
91
+ fn save_hashes(hashes: &HashMap<PathBuf, String>) -> Result<()> {
92
+ let hash_file = Path::new("scripts/.asset-hashes.json");
93
+ let string_map: HashMap<String, String> = hashes
94
+ .iter()
95
+ .map(|(k, v)| (k.to_string_lossy().into_owned(), v.clone()))
96
+ .collect();
97
+ let content = serde_json::to_string_pretty(&string_map)?;
98
+ fs::write(hash_file, content)?;
99
+ Ok(())
100
+ }
101
+
102
+ #[cfg(not(any(feature = "use-minified")))]
103
+ fn minify_assets() -> Result<()> {
104
+ // 获取现有文件的哈希
105
+ let current_hashes = get_files_hash()?;
106
 
107
+ if current_hashes.is_empty() {
108
+ println!("cargo:warning=No files to minify");
109
+ return Ok(());
110
+ }
111
+
112
+ // 加载保存的哈希值
113
+ let saved_hashes = load_saved_hashes()?;
114
+
115
+ // 找出需要更新的文件
116
+ let files_to_update: Vec<_> = current_hashes
117
+ .iter()
118
+ .filter(|(path, current_hash)| {
119
+ let is_readme = path.file_name().map_or(false, |f| f == "README.md");
120
+ let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
121
+
122
+ // 为 README.md 和其他文件使用不同的输出路径检查
123
+ let min_path = if is_readme {
124
+ PathBuf::from("static/readme.min.html")
125
+ } else {
126
+ path.with_file_name(format!(
127
+ "{}.min.{}",
128
+ path.file_stem().unwrap().to_string_lossy(),
129
+ ext
130
+ ))
131
+ };
132
+
133
+ // 检查压缩/转换后的文件是否存在
134
+ if !min_path.exists() {
135
+ return true;
136
+ }
137
+
138
+ // 检查原始文件是否发生变化
139
+ saved_hashes
140
+ .get(*path)
141
+ .map_or(true, |saved_hash| saved_hash != *current_hash)
142
+ })
143
+ .map(|(path, _)| path.file_name().unwrap().to_string_lossy().into_owned())
144
+ .collect();
145
+
146
+ if files_to_update.is_empty() {
147
+ println!("cargo:warning=No files need to be updated");
148
+ return Ok(());
149
+ }
150
+
151
+ println!("cargo:warning=Minifying {} files...", files_to_update.len());
152
+
153
+ // 运行压缩脚本
154
  let status = Command::new("node")
155
+ .arg("scripts/minify.js")
156
+ .args(&files_to_update)
157
  .status()?;
158
 
159
  if !status.success() {
160
+ panic!("Asset minification failed");
161
  }
162
+
163
+ // 保存新的哈希值
164
+ save_hashes(&current_hashes)?;
165
+
166
  Ok(())
167
  }
168
 
169
  fn main() -> Result<()> {
170
  // Proto 文件处理
171
+ println!("cargo:rerun-if-changed=src/chat/aiserver/v1/lite.proto");
172
+ println!("cargo:rerun-if-changed=src/chat/config/key.proto");
173
+ // 获取环境变量 PROTOC
174
+ let protoc_path = match std::env::var_os("PROTOC") {
175
+ Some(path) => PathBuf::from(path),
176
+ None => {
177
+ println!("cargo:warning=PROTOC environment variable not set, using default protoc.");
178
+ // 如果 PROTOC 未设置,则返回一个空的 PathBuf,prost-build 会尝试使用默认的 protoc
179
+ PathBuf::new()
180
+ }
181
+ };
182
  let mut config = prost_build::Config::new();
183
+ // 如果 protoc_path 不为空,则配置使用指定的 protoc
184
+ if !protoc_path.as_os_str().is_empty() {
185
+ config.protoc_executable(protoc_path);
186
+ }
187
+ // config.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]");
188
+ config
189
+ .compile_protos(
190
+ &["src/chat/aiserver/v1/lite.proto"],
191
+ &["src/chat/aiserver/v1/"],
192
+ )
193
+ .unwrap();
194
  config
195
+ .compile_protos(&["src/chat/config/key.proto"], &["src/chat/config/"])
196
  .unwrap();
197
 
198
+ // 静态资源文件处理
199
+ println!("cargo:rerun-if-changed=scripts/minify.js");
 
200
  println!("cargo:rerun-if-changed=scripts/package.json");
201
+ println!("cargo:rerun-if-changed=static/api.html");
202
+ println!("cargo:rerun-if-changed=static/build_key.html");
203
+ println!("cargo:rerun-if-changed=static/config.html");
204
+ println!("cargo:rerun-if-changed=static/logs.html");
205
+ println!("cargo:rerun-if-changed=static/shared-styles.css");
206
+ println!("cargo:rerun-if-changed=static/shared.js");
207
+ println!("cargo:rerun-if-changed=static/tokens.html");
208
+ println!("cargo:rerun-if-changed=README.md");
209
 
210
+ #[cfg(not(any(feature = "use-minified")))]
211
+ {
212
+ // 检查并安装依赖
213
+ check_and_install_deps()?;
214
 
215
+ // 运行资源压缩
216
+ minify_assets()?;
217
+ }
218
 
219
  Ok(())
220
  }
scripts/build.ps1 CHANGED
@@ -1,158 +1,126 @@
1
- # ��ɫ�������
2
- function Write-Info { Write-Host "[INFO] $args" -ForegroundColor Blue }
3
- function Write-Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow }
4
- function Write-Error { Write-Host "[ERROR] $args" -ForegroundColor Red; exit 1 }
5
-
6
- # ����Ҫ�Ĺ���
7
- function Test-Requirements {
8
- $tools = @("cargo", "protoc", "npm", "node")
9
- $missing = @()
10
-
11
- foreach ($tool in $tools) {
12
- if (!(Get-Command $tool -ErrorAction SilentlyContinue)) {
13
- $missing += $tool
 
 
 
 
 
 
 
 
 
 
 
 
14
  }
15
- }
16
 
17
- if ($missing.Count -gt 0) {
18
- Write-Error "ȱ�ٱ�Ҫ����: $($missing -join ', ')"
19
- }
20
  }
21
 
22
- # �� Test-Requirements �����������º���
23
- function Initialize-VSEnvironment {
24
- Write-Info "���ڳ�ʼ�� Visual Studio ����..."
25
-
26
- # ֱ��ʹ����֪�� vcvarsall.bat ·��
27
- $vcvarsallPath = "E:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat"
28
-
29
- if (-not (Test-Path $vcvarsallPath)) {
30
- Write-Error "δ�ҵ� vcvarsall.bat: $vcvarsallPath"
31
- return
32
- }
33
 
34
- Write-Info "ʹ�� vcvarsall.bat ·��: $vcvarsallPath"
 
 
35
 
36
- # ��ȡ��������
37
- $archArg = "x64"
38
- $command = "`"$vcvarsallPath`" $archArg && set"
39
 
40
- try {
41
- $output = cmd /c "$command" 2>&1
 
 
 
 
42
 
43
- # ��������Ƿ�ɹ�ִ��
44
- if ($LASTEXITCODE -ne 0) {
45
- Write-Error "vcvarsall.bat ִ��ʧ�ܣ��˳���: $LASTEXITCODE"
46
- return
47
- }
48
 
49
- # ���µ�ǰ PowerShell �Ự�Ļ�������
50
- foreach ($line in $output) {
51
- if ($line -match "^([^=]+)=(.*)$") {
52
- $name = $matches[1]
53
- $value = $matches[2]
54
- if (![string]::IsNullOrEmpty($name)) {
55
- Set-Item -Path "env:$name" -Value $value -ErrorAction SilentlyContinue
56
- }
57
- }
58
- }
59
 
60
- Write-Info "Visual Studio ������ʼ�����"
 
 
 
 
61
  }
62
- catch {
63
- Write-Error "��ʼ�� Visual Studio ����ʱ��������: $_"
64
- }
65
- }
66
 
67
- # ������Ϣ
68
- function Show-Help {
69
- Write-Host @"
70
- �÷�: $(Split-Path $MyInvocation.MyCommand.Path -Leaf) [ѡ��]
 
71
 
72
- ѡ��:
73
- --static ʹ�þ�̬���ӣ�Ĭ�϶�̬���ӣ�
74
- --help ��ʾ�˰�����Ϣ
 
 
75
 
76
- Ĭ�ϱ������� Windows ֧�ֵļܹ� (x64 �� arm64)
77
- "@
78
- }
 
 
 
 
 
 
 
79
 
80
- # ��������
81
- function New-Target {
82
- param (
83
- [string]$target,
84
- [string]$rustflags
85
- )
86
-
87
- Write-Info "���ڹ��� $target..."
88
-
89
- # ���û���������ִ�й���
90
- $env:RUSTFLAGS = $rustflags
91
- cargo build --target $target --release
92
-
93
- # �ƶ���������
94
- $binaryName = "cursor-api"
95
- if ($UseStatic) {
96
- $binaryName += "-static"
97
- }
98
-
99
- $sourcePath = "target/$target/release/cursor-api.exe"
100
- $targetPath = "release/${binaryName}-${target}.exe"
101
-
102
- if (Test-Path $sourcePath) {
103
- Copy-Item $sourcePath $targetPath -Force
104
- Write-Info "��ɹ��� $target"
105
- }
106
- else {
107
- Write-Warn "��������δ�ҵ�: $target"
108
- return $false
109
- }
110
- return $true
111
  }
112
 
113
- # ��������
114
- $UseStatic = $false
115
-
116
- foreach ($arg in $args) {
117
- switch ($arg) {
118
- "--static" { $UseStatic = $true }
119
- "--help" { Show-Help; exit 0 }
120
- default { Write-Error "δ֪����: $arg" }
121
- }
122
  }
123
 
124
- # ������
125
- try {
126
- # �������
127
- Test-Requirements
128
-
129
- # ��ʼ�� Visual Studio ����
130
- Initialize-VSEnvironment
131
 
132
- # ���� release Ŀ¼
133
- New-Item -ItemType Directory -Force -Path "release" | Out-Null
134
 
135
- # ����Ŀ��ƽ̨
136
- $targets = @(
137
- "x86_64-pc-windows-msvc",
138
- "aarch64-pc-windows-msvc"
139
- )
140
-
141
- # ���þ�̬���ӱ�־
142
- $rustflags = ""
143
- if ($UseStatic) {
144
- $rustflags = "-C target-feature=+crt-static"
145
- }
146
-
147
- Write-Info "��ʼ����..."
148
 
149
- # ��������Ŀ��
150
- foreach ($target in $targets) {
151
- New-Target -target $target -rustflags $rustflags
152
- }
 
 
 
 
 
 
 
 
153
 
154
- Write-Info "������ɣ�"
 
 
155
  }
156
- catch {
157
- Write-Error "���������������: $_"
158
- }
 
1
+ # 参数处理
2
+ param(
3
+ [switch]$Static,
4
+ [switch]$Help,
5
+ [ValidateSet("x86_64", "aarch64", "i686")]
6
+ [string]$Architecture
7
+ )
8
+
9
+ # 设置错误时停止执行
10
+ $ErrorActionPreference = "Stop"
11
+
12
+ # 颜色输出函数
13
+ function Write-Info { param($Message) Write-Host "[INFO] $Message" -ForegroundColor Blue }
14
+ function Write-Warn { param($Message) Write-Host "[WARN] $Message" -ForegroundColor Yellow }
15
+ function Write-Error { param($Message) Write-Host "[ERROR] $Message" -ForegroundColor Red; exit 1 }
16
+
17
+ # 检查必要的工具
18
+ function Check-Requirements {
19
+ $tools = @("cargo", "protoc", "npm", "node")
20
+ $missing = @()
21
+
22
+ foreach ($tool in $tools) {
23
+ if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) {
24
+ $missing += $tool
25
+ }
26
  }
 
27
 
28
+ if ($missing.Count -gt 0) {
29
+ Write-Error "缺少必要工具: $($missing -join ', ')"
30
+ }
31
  }
32
 
33
+ # 帮助信息
34
+ function Show-Help {
35
+ Write-Host @"
36
+ 用法: $(Split-Path $MyInvocation.ScriptName -Leaf) [选项]
 
 
 
 
 
 
 
37
 
38
+ 选项:
39
+ -Static 使用静态链接(默认动态链接)
40
+ -Help 显示此帮助信息
41
 
42
+ 不带参数时使用默认配置构建
43
+ "@
44
+ }
45
 
46
+ # 构建函数
47
+ function Build-Target {
48
+ param (
49
+ [string]$Target,
50
+ [string]$RustFlags
51
+ )
52
 
53
+ Write-Info "正在构建 $Target..."
 
 
 
 
54
 
55
+ # 设置环境变量
56
+ $env:RUSTFLAGS = $RustFlags
 
 
 
 
 
 
 
 
57
 
58
+ # 构建
59
+ if ($Target -ne (rustc -Vv | Select-String "host: (.*)" | ForEach-Object { $_.Matches.Groups[1].Value })) {
60
+ cargo build --target $Target --release
61
+ } else {
62
+ cargo build --release
63
  }
 
 
 
 
64
 
65
+ # 移动编译产物到 release 目录
66
+ $binaryName = "cursor-api"
67
+ if ($Static) {
68
+ $binaryName += "-static"
69
+ }
70
 
71
+ $binaryPath = if ($Target -eq (rustc -Vv | Select-String "host: (.*)" | ForEach-Object { $_.Matches.Groups[1].Value })) {
72
+ "target/release/cursor-api.exe"
73
+ } else {
74
+ "target/$Target/release/cursor-api.exe"
75
+ }
76
 
77
+ if (Test-Path $binaryPath) {
78
+ Copy-Item $binaryPath "release/$binaryName-$Target.exe"
79
+ Write-Info "完成构建 $Target"
80
+ } else {
81
+ Write-Warn "构建产物未找到: $Target"
82
+ Write-Warn "查找路径: $binaryPath"
83
+ Write-Warn "当前目录内容:"
84
+ Get-ChildItem -Recurse target/
85
+ return $false
86
+ }
87
 
88
+ return $true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
 
91
+ if ($Help) {
92
+ Show-Help
93
+ exit 0
 
 
 
 
 
 
94
  }
95
 
96
+ # 检查依赖
97
+ Check-Requirements
 
 
 
 
 
98
 
99
+ # 创建 release 目录
100
+ New-Item -ItemType Directory -Force -Path release | Out-Null
101
 
102
+ # 设置静态链接标志
103
+ $rustFlags = ""
104
+ if ($Static) {
105
+ $rustFlags = "-C target-feature=+crt-static"
106
+ }
 
 
 
 
 
 
 
 
107
 
108
+ # 获取目标架构
109
+ $arch = if ($Architecture) {
110
+ $Architecture
111
+ } else {
112
+ switch ($env:PROCESSOR_ARCHITECTURE) {
113
+ "AMD64" { "x86_64" }
114
+ "ARM64" { "aarch64" }
115
+ "X86" { "i686" }
116
+ default { Write-Error "不支持的架构: $env:PROCESSOR_ARCHITECTURE" }
117
+ }
118
+ }
119
+ $target = "$arch-pc-windows-msvc"
120
 
121
+ Write-Info "开始构建..."
122
+ if (-not (Build-Target -Target $target -RustFlags $rustFlags)) {
123
+ Write-Error "构建失败"
124
  }
125
+
126
+ Write-Info "构建完成!"
 
scripts/build.sh CHANGED
@@ -6,11 +6,6 @@ info() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
6
  warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
7
  error() { echo -e "\033[1;31m[ERROR]\033[0m $*" >&2; exit 1; }
8
 
9
- # 检查是否在 Linux 环境
10
- is_linux() {
11
- [ "$(uname -s)" = "Linux" ]
12
- }
13
-
14
  # 检查必要的工具
15
  check_requirements() {
16
  local missing_tools=()
@@ -22,23 +17,29 @@ check_requirements() {
22
  fi
23
  done
24
 
25
- # Linux 特定检查
26
- if [[ $USE_CROSS == true ]] && ! command -v cross &>/dev/null; then
27
- missing_tools+=("cross")
28
- fi
29
-
30
  if [[ ${#missing_tools[@]} -gt 0 ]]; then
31
  error "缺少必要工具: ${missing_tools[*]}"
32
  fi
33
  }
34
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  # 帮助信息
36
  show_help() {
37
  cat << EOF
38
  用法: $(basename "$0") [选项]
39
 
40
  选项:
41
- --cross 使用 cross 进行交叉编译(仅在 Linux 上有效)
42
  --static 使用静态链接(默认动态链接)
43
  --help 显示此帮助信息
44
 
@@ -57,42 +58,37 @@ build_target() {
57
  # 确定文件后缀
58
  [[ $target == *"windows"* ]] && extension=".exe"
59
 
60
- # 设置目标特定的环境变量
61
- local build_env=()
62
- if [[ $target == "aarch64-unknown-linux-gnu" ]]; then
63
- build_env+=(
64
- "CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc"
65
- "CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++"
66
- "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc"
67
- "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig"
68
- "PKG_CONFIG_ALLOW_CROSS=1"
69
- "OPENSSL_DIR=/usr"
70
- "OPENSSL_INCLUDE_DIR=/usr/include"
71
- "OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu"
72
- )
73
- fi
74
-
75
- # 判断是否使用 cross(仅在 Linux 上)
76
  if [[ $target != "$CURRENT_TARGET" ]]; then
77
- env ${build_env[@]+"${build_env[@]}"} RUSTFLAGS="$rustflags" cargo build --target "$target" --release
78
  else
79
- env ${build_env[@]+"${build_env[@]}"} RUSTFLAGS="$rustflags" cargo build --release
80
  fi
81
 
82
  # 移动编译产物到 release 目录
83
  local binary_name="cursor-api"
84
  [[ $USE_STATIC == true ]] && binary_name+="-static"
85
 
86
- if [[ -f "target/$target/release/cursor-api$extension" ]]; then
87
- cp "target/$target/release/cursor-api$extension" "release/${binary_name}-$target$extension"
 
 
 
 
 
 
 
88
  info "完成构建 $target"
89
  else
90
  warn "构建产物未找到: $target"
 
 
 
91
  return 1
92
  fi
93
  }
94
 
95
- # 获取 CPU 架构
96
  ARCH=$(uname -m | sed 's/^aarch64\|arm64$/aarch64/;s/^x86_64\|x86-64\|x64\|amd64$/x86_64/')
97
  OS=$(uname -s)
98
 
@@ -102,9 +98,15 @@ get_target() {
102
  local os=$2
103
  case "$os" in
104
  "Darwin") echo "${arch}-apple-darwin" ;;
105
- "Linux") echo "${arch}-unknown-linux-gnu" ;;
 
 
 
 
 
 
106
  "MINGW"*|"MSYS"*|"CYGWIN"*|"Windows_NT") echo "${arch}-pc-windows-msvc" ;;
107
- "FreeBSD") echo "x86_64-unknown-freebsd" ;;
108
  *) error "不支持的系统: $os" ;;
109
  esac
110
  }
@@ -118,52 +120,52 @@ CURRENT_TARGET=$(get_target "$ARCH" "$OS")
118
  # 获取系统对应的所有目标
119
  get_targets() {
120
  case "$1" in
121
- "linux") echo "x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu" ;;
122
- "windows") echo "x86_64-pc-windows-msvc aarch64-pc-windows-msvc" ;;
123
- "macos") echo "x86_64-apple-darwin aarch64-apple-darwin" ;;
124
- "freebsd") echo "x86_64-unknown-freebsd" ;;
 
 
 
 
 
 
 
 
 
 
 
 
125
  *) error "不支持的系统组: $1" ;;
126
  esac
127
  }
128
 
129
- # 解析参数
130
- USE_CROSS=false
131
- USE_STATIC=false
132
-
133
- while [[ $# -gt 0 ]]; do
134
- case $1 in
135
- --cross) USE_CROSS=true ;;
136
- --static) USE_STATIC=true ;;
137
- --help) show_help; exit 0 ;;
138
- *) error "未知参数: $1" ;;
139
- esac
140
- shift
141
- done
142
-
143
  # 检查依赖
144
  check_requirements
145
 
146
  # 确定要构建的目标
147
- if [[ $USE_CROSS == true ]] && is_linux; then
148
- # 只在 Linux 上使用 cross 进行多架构构建
149
- TARGETS=($(get_targets "linux"))
150
- else
151
- # 其他系统或不使用 cross 时只构建当前系统的所有架构
152
- case "$OS" in
153
- "Darwin") TARGETS=($(get_targets "macos")) ;;
154
- "Linux") TARGETS=("$CURRENT_TARGET") ;;
155
- "MINGW"*|"MSYS"*|"CYGWIN"*|"Windows_NT") TARGETS=($(get_targets "windows")) ;;
156
- "FreeBSD") TARGETS=("$CURRENT_TARGET") ;;
157
- *) error "不支持的系统: $OS" ;;
158
- esac
159
- fi
 
 
160
 
161
  # 创建 release 目录
162
  mkdir -p release
163
 
164
  # 设置静态链接标志
165
- RUSTFLAGS=""
166
- [[ $USE_STATIC == true ]] && RUSTFLAGS="-C target-feature=+crt-static"
167
 
168
  # 并行构建所有目标
169
  info "开始构建..."
 
6
  warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
7
  error() { echo -e "\033[1;31m[ERROR]\033[0m $*" >&2; exit 1; }
8
 
 
 
 
 
 
9
  # 检查必要的工具
10
  check_requirements() {
11
  local missing_tools=()
 
17
  fi
18
  done
19
 
 
 
 
 
 
20
  if [[ ${#missing_tools[@]} -gt 0 ]]; then
21
  error "缺少必要工具: ${missing_tools[*]}"
22
  fi
23
  }
24
 
25
+ # 解析参数
26
+ USE_STATIC=false
27
+
28
+ while [[ $# -gt 0 ]]; do
29
+ case $1 in
30
+ --static) USE_STATIC=true ;;
31
+ --help) show_help; exit 0 ;;
32
+ *) error "未知参数: $1" ;;
33
+ esac
34
+ shift
35
+ done
36
+
37
  # 帮助信息
38
  show_help() {
39
  cat << EOF
40
  用法: $(basename "$0") [选项]
41
 
42
  选项:
 
43
  --static 使用静态链接(默认动态链接)
44
  --help 显示此帮助信息
45
 
 
58
  # 确定文件后缀
59
  [[ $target == *"windows"* ]] && extension=".exe"
60
 
61
+ # 构建
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  if [[ $target != "$CURRENT_TARGET" ]]; then
63
+ env RUSTFLAGS="$rustflags" cargo build --target "$target" --release
64
  else
65
+ env RUSTFLAGS="$rustflags" cargo build --release
66
  fi
67
 
68
  # 移动编译产物到 release 目录
69
  local binary_name="cursor-api"
70
  [[ $USE_STATIC == true ]] && binary_name+="-static"
71
 
72
+ local binary_path
73
+ if [[ $target == "$CURRENT_TARGET" ]]; then
74
+ binary_path="target/release/cursor-api$extension"
75
+ else
76
+ binary_path="target/$target/release/cursor-api$extension"
77
+ fi
78
+
79
+ if [[ -f "$binary_path" ]]; then
80
+ cp "$binary_path" "release/${binary_name}-$target$extension"
81
  info "完成构建 $target"
82
  else
83
  warn "构建产物未找到: $target"
84
+ warn "查找路径: $binary_path"
85
+ warn "当前目录内容:"
86
+ ls -R target/
87
  return 1
88
  fi
89
  }
90
 
91
+ # 获取 CPU 架构和操作系统
92
  ARCH=$(uname -m | sed 's/^aarch64\|arm64$/aarch64/;s/^x86_64\|x86-64\|x64\|amd64$/x86_64/')
93
  OS=$(uname -s)
94
 
 
98
  local os=$2
99
  case "$os" in
100
  "Darwin") echo "${arch}-apple-darwin" ;;
101
+ "Linux")
102
+ if [[ $USE_STATIC == true ]]; then
103
+ echo "${arch}-unknown-linux-musl"
104
+ else
105
+ echo "${arch}-unknown-linux-gnu"
106
+ fi
107
+ ;;
108
  "MINGW"*|"MSYS"*|"CYGWIN"*|"Windows_NT") echo "${arch}-pc-windows-msvc" ;;
109
+ "FreeBSD") echo "${arch}-unknown-freebsd" ;;
110
  *) error "不支持的系统: $os" ;;
111
  esac
112
  }
 
120
  # 获取系统对应的所有目标
121
  get_targets() {
122
  case "$1" in
123
+ "linux")
124
+ # Linux 只构建当前架构
125
+ echo "$CURRENT_TARGET"
126
+ ;;
127
+ "freebsd")
128
+ # FreeBSD 只构建当前架构
129
+ echo "$CURRENT_TARGET"
130
+ ;;
131
+ "windows")
132
+ # Windows 只构建当前架构
133
+ echo "$CURRENT_TARGET"
134
+ ;;
135
+ "macos")
136
+ # macOS 构建所有 macOS 目标
137
+ echo "x86_64-apple-darwin aarch64-apple-darwin"
138
+ ;;
139
  *) error "不支持的系统组: $1" ;;
140
  esac
141
  }
142
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  # 检查依赖
144
  check_requirements
145
 
146
  # 确定要构建的目标
147
+ case "$OS" in
148
+ Darwin)
149
+ TARGETS=($(get_targets "macos"))
150
+ ;;
151
+ Linux)
152
+ TARGETS=($(get_targets "linux"))
153
+ ;;
154
+ FreeBSD)
155
+ TARGETS=($(get_targets "freebsd"))
156
+ ;;
157
+ MINGW*|MSYS*|CYGWIN*|Windows_NT)
158
+ TARGETS=($(get_targets "windows"))
159
+ ;;
160
+ *) error "不支持的系统: $OS" ;;
161
+ esac
162
 
163
  # 创建 release 目录
164
  mkdir -p release
165
 
166
  # 设置静态链接标志
167
+ RUSTFLAGS="-C link-arg=-s"
168
+ [[ $USE_STATIC == true ]] && RUSTFLAGS="-C target-feature=+crt-static -C link-arg=-s"
169
 
170
  # 并行构建所有目标
171
  info "开始构建..."
scripts/minify-html.js DELETED
@@ -1,49 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const { minify } = require('html-minifier-terser');
4
- const fs = require('fs');
5
- const path = require('path');
6
-
7
- // 配置选项
8
- const options = {
9
- collapseWhitespace: true,
10
- removeComments: true,
11
- removeEmptyAttributes: true,
12
- removeOptionalTags: true,
13
- removeRedundantAttributes: true,
14
- removeScriptTypeAttributes: true,
15
- removeStyleLinkTypeAttributes: true,
16
- minifyCSS: true,
17
- minifyJS: true,
18
- processScripts: ['application/json'],
19
- };
20
-
21
- // 处理文件
22
- async function minifyFile(inputPath, outputPath) {
23
- try {
24
- const html = fs.readFileSync(inputPath, 'utf8');
25
- const minified = await minify(html, options);
26
- fs.writeFileSync(outputPath, minified);
27
- console.log(`✓ Minified ${path.basename(inputPath)} -> ${path.basename(outputPath)}`);
28
- } catch (err) {
29
- console.error(`✗ Error processing ${inputPath}:`, err);
30
- process.exit(1);
31
- }
32
- }
33
-
34
- // 主函数
35
- async function main() {
36
- const staticDir = path.join(__dirname, '..', 'static');
37
- const files = [
38
- ['tokeninfo.html', 'tokeninfo.min.html'],
39
- ];
40
-
41
- for (const [input, output] of files) {
42
- await minifyFile(
43
- path.join(staticDir, input),
44
- path.join(staticDir, output)
45
- );
46
- }
47
- }
48
-
49
- main();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
scripts/minify.js ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ const { minify: minifyHtml } = require('html-minifier-terser');
4
+ const { minify: minifyJs } = require('terser');
5
+ const CleanCSS = require('clean-css');
6
+ const MarkdownIt = require('markdown-it');
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ // 配置选项
11
+ const options = {
12
+ collapseWhitespace: true,
13
+ removeComments: true,
14
+ removeEmptyAttributes: true,
15
+ removeOptionalTags: true,
16
+ removeRedundantAttributes: true,
17
+ removeScriptTypeAttributes: true,
18
+ removeStyleLinkTypeAttributes: true,
19
+ minifyCSS: true,
20
+ minifyJS: true,
21
+ processScripts: ['application/json'],
22
+ };
23
+
24
+ // CSS 压缩选项
25
+ const cssOptions = {
26
+ level: 2
27
+ };
28
+
29
+ // 处理文件
30
+ async function minifyFile(inputPath, outputPath) {
31
+ try {
32
+ let ext = path.extname(inputPath).toLowerCase();
33
+ if (ext === '.md') ext = '.html';
34
+ const filename = path.basename(inputPath);
35
+ let content = fs.readFileSync(inputPath, 'utf8');
36
+ let minified;
37
+
38
+ // 特殊处理 readme.html
39
+ if (filename.toLowerCase() === 'readme.md') {
40
+ const md = new MarkdownIt({
41
+ html: true,
42
+ linkify: true,
43
+ typographer: true
44
+ });
45
+ const readmeMdPath = path.join(__dirname, '..', 'README.md');
46
+ const markdownContent = fs.readFileSync(readmeMdPath, 'utf8');
47
+ // 添加基本的 markdown 样式
48
+ const htmlContent = `
49
+ <!DOCTYPE html>
50
+ <html>
51
+ <head>
52
+ <meta charset="UTF-8">
53
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
54
+ <title>README</title>
55
+ <style>
56
+ body {
57
+ max-width: 800px;
58
+ margin: 0 auto;
59
+ padding: 20px;
60
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
61
+ line-height: 1.6;
62
+ }
63
+ pre {
64
+ background-color: #f6f8fa;
65
+ padding: 16px;
66
+ border-radius: 6px;
67
+ overflow: auto;
68
+ }
69
+ code {
70
+ background-color: #f6f8fa;
71
+ padding: 0.2em 0.4em;
72
+ border-radius: 3px;
73
+ }
74
+ img {
75
+ max-width: 100%;
76
+ }
77
+ table {
78
+ border-collapse: collapse;
79
+ width: 100%;
80
+ }
81
+ table td, table th {
82
+ border: 1px solid #dfe2e5;
83
+ padding: 6px 13px;
84
+ }
85
+ blockquote {
86
+ border-left: 4px solid #dfe2e5;
87
+ margin: 0;
88
+ padding: 0 1em;
89
+ color: #6a737d;
90
+ }
91
+ </style>
92
+ </head>
93
+ <body>
94
+ ${md.render(markdownContent)}
95
+ </body>
96
+ </html>
97
+ `;
98
+ content = htmlContent;
99
+ }
100
+
101
+ switch (ext) {
102
+ case '.html':
103
+ minified = await minifyHtml(content, options);
104
+ break;
105
+ case '.js':
106
+ const result = await minifyJs(content);
107
+ minified = result.code;
108
+ break;
109
+ case '.css':
110
+ minified = new CleanCSS(cssOptions).minify(content).styles;
111
+ break;
112
+ default:
113
+ throw new Error(`Unsupported file type: ${ext}`);
114
+ }
115
+
116
+ fs.writeFileSync(outputPath, minified);
117
+ console.log(`✓ Minified ${path.basename(inputPath)} -> ${path.basename(outputPath)}`);
118
+ } catch (err) {
119
+ console.error(`✗ Error processing ${inputPath}:`, err);
120
+ process.exit(1);
121
+ }
122
+ }
123
+
124
+ // 主函数
125
+ async function main() {
126
+ // 获取命令行参数,跳过前两个参数(node和脚本路径)
127
+ const files = process.argv.slice(2);
128
+
129
+ if (files.length === 0) {
130
+ console.error('No input files specified');
131
+ process.exit(1);
132
+ }
133
+
134
+ const staticDir = path.join(__dirname, '..', 'static');
135
+
136
+ for (const file of files) {
137
+ // 特殊处理 README.md 的输入路径
138
+ let inputPath;
139
+ let outputPath;
140
+
141
+ if (file.toLowerCase() === 'readme.md') {
142
+ inputPath = path.join(__dirname, '..', 'README.md');
143
+ outputPath = path.join(staticDir, 'readme.min.html');
144
+ } else {
145
+ inputPath = path.join(staticDir, file);
146
+ const ext = path.extname(file);
147
+ outputPath = path.join(
148
+ staticDir,
149
+ file.replace(ext, `.min${ext}`)
150
+ );
151
+ }
152
+
153
+ await minifyFile(inputPath, outputPath);
154
+ }
155
+ }
156
+
157
+ main();
scripts/package-lock.json CHANGED
@@ -8,7 +8,10 @@
8
  "name": "html-minifier-scripts",
9
  "version": "1.0.0",
10
  "dependencies": {
11
- "html-minifier-terser": "^7.2.0"
 
 
 
12
  },
13
  "engines": {
14
  "node": ">=14.0.0"
@@ -84,6 +87,12 @@
84
  "node": ">=0.4.0"
85
  }
86
  },
 
 
 
 
 
 
87
  "node_modules/buffer-from": {
88
  "version": "1.1.2",
89
  "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -164,6 +173,15 @@
164
  "node": "^14.13.1 || >=16.0.0"
165
  }
166
  },
 
 
 
 
 
 
 
 
 
167
  "node_modules/lower-case": {
168
  "version": "2.0.2",
169
  "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
@@ -173,6 +191,29 @@
173
  "tslib": "^2.0.3"
174
  }
175
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  "node_modules/no-case": {
177
  "version": "3.0.4",
178
  "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@@ -203,6 +244,15 @@
203
  "tslib": "^2.0.3"
204
  }
205
  },
 
 
 
 
 
 
 
 
 
206
  "node_modules/relateurl": {
207
  "version": "0.2.7",
208
  "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
@@ -260,6 +310,12 @@
260
  "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
261
  "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
262
  "license": "0BSD"
 
 
 
 
 
 
263
  }
264
  }
265
  }
 
8
  "name": "html-minifier-scripts",
9
  "version": "1.0.0",
10
  "dependencies": {
11
+ "clean-css": "^5.3.3",
12
+ "html-minifier-terser": "^7.2.0",
13
+ "markdown-it": "^14.1.0",
14
+ "terser": "^5.37.0"
15
  },
16
  "engines": {
17
  "node": ">=14.0.0"
 
87
  "node": ">=0.4.0"
88
  }
89
  },
90
+ "node_modules/argparse": {
91
+ "version": "2.0.1",
92
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
93
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
94
+ "license": "Python-2.0"
95
+ },
96
  "node_modules/buffer-from": {
97
  "version": "1.1.2",
98
  "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
 
173
  "node": "^14.13.1 || >=16.0.0"
174
  }
175
  },
176
+ "node_modules/linkify-it": {
177
+ "version": "5.0.0",
178
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
179
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
180
+ "license": "MIT",
181
+ "dependencies": {
182
+ "uc.micro": "^2.0.0"
183
+ }
184
+ },
185
  "node_modules/lower-case": {
186
  "version": "2.0.2",
187
  "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
 
191
  "tslib": "^2.0.3"
192
  }
193
  },
194
+ "node_modules/markdown-it": {
195
+ "version": "14.1.0",
196
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
197
+ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
198
+ "license": "MIT",
199
+ "dependencies": {
200
+ "argparse": "^2.0.1",
201
+ "entities": "^4.4.0",
202
+ "linkify-it": "^5.0.0",
203
+ "mdurl": "^2.0.0",
204
+ "punycode.js": "^2.3.1",
205
+ "uc.micro": "^2.1.0"
206
+ },
207
+ "bin": {
208
+ "markdown-it": "bin/markdown-it.mjs"
209
+ }
210
+ },
211
+ "node_modules/mdurl": {
212
+ "version": "2.0.0",
213
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
214
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
215
+ "license": "MIT"
216
+ },
217
  "node_modules/no-case": {
218
  "version": "3.0.4",
219
  "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
 
244
  "tslib": "^2.0.3"
245
  }
246
  },
247
+ "node_modules/punycode.js": {
248
+ "version": "2.3.1",
249
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
250
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
251
+ "license": "MIT",
252
+ "engines": {
253
+ "node": ">=6"
254
+ }
255
+ },
256
  "node_modules/relateurl": {
257
  "version": "0.2.7",
258
  "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
 
310
  "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
311
  "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
312
  "license": "0BSD"
313
+ },
314
+ "node_modules/uc.micro": {
315
+ "version": "2.1.0",
316
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
317
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
318
+ "license": "MIT"
319
  }
320
  }
321
  }
scripts/package.json CHANGED
@@ -6,6 +6,9 @@
6
  "node": ">=14.0.0"
7
  },
8
  "dependencies": {
9
- "html-minifier-terser": "^7.2.0"
 
 
 
10
  }
11
- }
 
6
  "node": ">=14.0.0"
7
  },
8
  "dependencies": {
9
+ "clean-css": "^5.3.3",
10
+ "html-minifier-terser": "^7.2.0",
11
+ "markdown-it": "^14.1.0",
12
+ "terser": "^5.37.0"
13
  }
14
+ }
scripts/setup-windows.ps1 DELETED
@@ -1,31 +0,0 @@
1
- # ���� PowerShell ����Ϊ UTF-8
2
- [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
3
- $OutputEncoding = [System.Text.Encoding]::UTF8
4
-
5
- # ����Ƿ��Թ���ԱȨ������
6
- if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
7
- Write-Warning "���Թ���ԱȨ�����д˽ű�"
8
- exit 1
9
- }
10
-
11
- # ��鲢��װ Chocolatey
12
- if (!(Get-Command choco -ErrorAction SilentlyContinue)) {
13
- Write-Output "���ڰ�װ Chocolatey..."
14
- Set-ExecutionPolicy Bypass -Scope Process -Force
15
- [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
16
- iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
17
- }
18
-
19
- # ��װ��Ҫ�Ĺ���
20
- Write-Output "���ڰ�װ��Ҫ�Ĺ���..."
21
- choco install -y mingw
22
- choco install -y protoc
23
- choco install -y git
24
-
25
- # ��װ Rust ����
26
- Write-Output "���ڰ�װ Rust ����..."
27
- rustup target add x86_64-pc-windows-msvc
28
- rustup target add x86_64-unknown-linux-gnu
29
- cargo install cross
30
-
31
- Write-Output "��װ��ɣ�"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
scripts/setup.ps1 ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ���ô���ʱִֹͣ��
2
+ $ErrorActionPreference = "Stop"
3
+ $ProgressPreference = "SilentlyContinue" # �ӿ������ٶ�
4
+
5
+ # ��ɫ�������
6
+ function Write-Info { param($Message) Write-Host "[INFO] $Message" -ForegroundColor Blue }
7
+ function Write-Warn { param($Message) Write-Host "[WARN] $Message" -ForegroundColor Yellow }
8
+ function Write-Success { param($Message) Write-Host "[SUCCESS] $Message" -ForegroundColor Green }
9
+ function Write-Error { param($Message) Write-Host "[ERROR] $Message" -ForegroundColor Red; exit 1 }
10
+
11
+ # ������ԱȨ��
12
+ function Test-Administrator {
13
+ $user = [Security.Principal.WindowsIdentity]::GetCurrent()
14
+ $principal = New-Object Security.Principal.WindowsPrincipal $user
15
+ return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
16
+ }
17
+
18
+ if (-not (Test-Administrator)) {
19
+ Write-Error "���Թ���ԱȨ�����д˽ű�"
20
+ }
21
+
22
+ # ������Ϣ
23
+ function Show-Help {
24
+ Write-Host @"
25
+ �÷�: $(Split-Path $MyInvocation.ScriptName -Leaf) [ѡ��]
26
+
27
+ ѡ��:
28
+ -NoVS ����װ Visual Studio Build Tools
29
+ -NoRust ����װ Rust
30
+ -NoNode ����װ Node.js
31
+ -Help ��ʾ�˰�����Ϣ
32
+
33
+ ʾ��:
34
+ .\setup.ps1
35
+ .\setup.ps1 -NoVS
36
+ .\setup.ps1 -NoRust -NoNode
37
+ "@
38
+ }
39
+
40
+ # ��������
41
+ param(
42
+ [switch]$NoVS,
43
+ [switch]$NoRust,
44
+ [switch]$NoNode,
45
+ [switch]$Help
46
+ )
47
+
48
+ if ($Help) {
49
+ Show-Help
50
+ exit 0
51
+ }
52
+
53
+ # ��鲢��װ Chocolatey
54
+ function Install-Chocolatey {
55
+ Write-Info "��� Chocolatey..."
56
+ if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
57
+ Write-Info "��װ Chocolatey..."
58
+ Set-ExecutionPolicy Bypass -Scope Process -Force
59
+ [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
60
+ try {
61
+ Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
62
+ }
63
+ catch {
64
+ Write-Error "��װ Chocolatey ʧ��: $_"
65
+ }
66
+ # ˢ�»�������
67
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
68
+ }
69
+ }
70
+
71
+ # ��װ Visual Studio Build Tools
72
+ function Install-VSBuildTools {
73
+ if ($NoVS) {
74
+ Write-Info "���� Visual Studio Build Tools ��װ"
75
+ return
76
+ }
77
+
78
+ Write-Info "��� Visual Studio Build Tools..."
79
+ $vsPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
80
+ if (-not (Test-Path $vsPath)) {
81
+ Write-Info "��װ Visual Studio Build Tools..."
82
+ try {
83
+ # ���ذ�װ����
84
+ $vsInstallerUrl = "https://aka.ms/vs/17/release/vs_BuildTools.exe"
85
+ $vsInstallerPath = "$env:TEMP\vs_BuildTools.exe"
86
+ Invoke-WebRequest -Uri $vsInstallerUrl -OutFile $vsInstallerPath
87
+
88
+ # ��װ
89
+ $process = Start-Process -FilePath $vsInstallerPath -ArgumentList `
90
+ "--quiet", "--wait", "--norestart", "--nocache", `
91
+ "--installPath", "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2022\BuildTools", `
92
+ "--add", "Microsoft.VisualStudio.Workload.VCTools" `
93
+ -NoNewWindow -Wait -PassThru
94
+
95
+ if ($process.ExitCode -ne 0) {
96
+ Write-Error "Visual Studio Build Tools ��װʧ��"
97
+ }
98
+
99
+ Remove-Item $vsInstallerPath -Force
100
+ }
101
+ catch {
102
+ Write-Error "��װ Visual Studio Build Tools ʧ��: $_"
103
+ }
104
+ }
105
+ else {
106
+ Write-Info "Visual Studio Build Tools �Ѱ�װ"
107
+ }
108
+ }
109
+
110
+ # ��װ Rust
111
+ function Install-Rust {
112
+ if ($NoRust) {
113
+ Write-Info "���� Rust ��װ"
114
+ return
115
+ }
116
+
117
+ Write-Info "��� Rust..."
118
+ if (-not (Get-Command rustc -ErrorAction SilentlyContinue)) {
119
+ Write-Info "��װ Rust..."
120
+ try {
121
+ $rustupInit = "$env:TEMP\rustup-init.exe"
122
+ Invoke-WebRequest -Uri "https://win.rustup.rs" -OutFile $rustupInit
123
+ Start-Process -FilePath $rustupInit -ArgumentList "-y" -Wait
124
+ Remove-Item $rustupInit -Force
125
+
126
+ # ˢ�»�������
127
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
128
+ }
129
+ catch {
130
+ Write-Error "��װ Rust ʧ��: $_"
131
+ }
132
+ }
133
+
134
+ # ����Ŀ��ƽ̨
135
+ Write-Info "���� Rust Ŀ��ƽ̨..."
136
+ $arch = if ([Environment]::Is64BitOperatingSystem) { "x86_64" } else { "i686" }
137
+ rustup target add "$arch-pc-windows-msvc"
138
+ }
139
+
140
+ # ��װ��������
141
+ function Install-Tools {
142
+ Write-Info "��װ��Ҫ����..."
143
+
144
+ # ��װ protoc
145
+ if (-not (Get-Command protoc -ErrorAction SilentlyContinue)) {
146
+ Write-Info "��װ Protocol Buffers..."
147
+ choco install -y protoc
148
+ }
149
+
150
+ # ��װ Git
151
+ if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
152
+ Write-Info "��װ Git..."
153
+ choco install -y git
154
+ }
155
+
156
+ # ��װ Node.js
157
+ if (-not $NoNode -and -not (Get-Command node -ErrorAction SilentlyContinue)) {
158
+ Write-Info "��װ Node.js..."
159
+ choco install -y nodejs
160
+ }
161
+
162
+ # ˢ�»�������
163
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
164
+ }
165
+
166
+ # ������
167
+ try {
168
+ Write-Info "��ʼ��װ��Ҫ���..."
169
+
170
+ Install-Chocolatey
171
+ Install-VSBuildTools
172
+ Install-Rust
173
+ Install-Tools
174
+
175
+ Write-Success "��װ��ɣ�"
176
+ }
177
+ catch {
178
+ Write-Error "��װ�����г��ִ���: $_"
179
+ }
scripts/setup.sh ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # 设置错误时退出
4
+ set -e
5
+
6
+ # 颜色输出
7
+ RED='\033[0;31m'
8
+ GREEN='\033[0;32m'
9
+ BLUE='\033[0;34m'
10
+ NC='\033[0m' # No Color
11
+
12
+ info() {
13
+ echo -e "${BLUE}[INFO] $1${NC}"
14
+ }
15
+
16
+ error() {
17
+ echo -e "${RED}[ERROR] $1${NC}"
18
+ exit 1
19
+ }
20
+
21
+ # 检查是否为 root 用户(FreeBSD 和 Linux)
22
+ if [ "$(uname)" != "Darwin" ] && [ "$EUID" -ne 0 ]; then
23
+ error "请使用 root 权限运行此脚本 (sudo ./setup.sh)"
24
+ fi
25
+
26
+ # 检测包管理器
27
+ if command -v brew &> /dev/null; then
28
+ PKG_MANAGER="brew"
29
+ info "检测到 macOS/Homebrew 系统"
30
+ elif command -v pkg &> /dev/null; then
31
+ PKG_MANAGER="pkg"
32
+ info "检测到 FreeBSD 系统"
33
+ elif command -v apt-get &> /dev/null; then
34
+ PKG_MANAGER="apt-get"
35
+ info "检测到 Debian/Ubuntu 系统"
36
+ elif command -v dnf &> /dev/null; then
37
+ PKG_MANAGER="dnf"
38
+ info "检测到 Fedora/RHEL 系统"
39
+ elif command -v yum &> /dev/null; then
40
+ PKG_MANAGER="yum"
41
+ info "检测到 CentOS 系统"
42
+ else
43
+ error "未检测到支持的包管理器"
44
+ fi
45
+
46
+ # 更新包管理器缓存
47
+ info "更新包管理器缓存..."
48
+ case $PKG_MANAGER in
49
+ "brew")
50
+ brew update
51
+ ;;
52
+ "pkg")
53
+ pkg update
54
+ ;;
55
+ *)
56
+ $PKG_MANAGER update -y
57
+ ;;
58
+ esac
59
+
60
+ # 安装基础构建工具
61
+ info "安装基础构建工具..."
62
+ case $PKG_MANAGER in
63
+ "brew")
64
+ brew install \
65
+ protobuf \
66
+ pkg-config \
67
+ openssl \
68
+ curl \
69
+ git \
70
+ node
71
+ ;;
72
+ "pkg")
73
+ pkg install -y \
74
+ gmake \
75
+ protobuf \
76
+ pkgconf \
77
+ openssl \
78
+ curl \
79
+ git \
80
+ node
81
+ ;;
82
+ "apt-get")
83
+ $PKG_MANAGER install -y --no-install-recommends \
84
+ build-essential \
85
+ protobuf-compiler \
86
+ pkg-config \
87
+ libssl-dev \
88
+ ca-certificates \
89
+ curl \
90
+ tzdata \
91
+ git
92
+ ;;
93
+ *)
94
+ $PKG_MANAGER install -y \
95
+ gcc \
96
+ gcc-c++ \
97
+ make \
98
+ protobuf-compiler \
99
+ pkg-config \
100
+ openssl-devel \
101
+ ca-certificates \
102
+ curl \
103
+ tzdata \
104
+ git
105
+ ;;
106
+ esac
107
+
108
+ # 安装 Node.js 和 npm(如果还没有通过包管理器安装)
109
+ if ! command -v node &> /dev/null && [ "$PKG_MANAGER" != "brew" ] && [ "$PKG_MANAGER" != "pkg" ]; then
110
+ info "安装 Node.js 和 npm..."
111
+ if [ "$PKG_MANAGER" = "apt-get" ]; then
112
+ curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
113
+ $PKG_MANAGER install -y nodejs
114
+ else
115
+ curl -fsSL https://rpm.nodesource.com/setup_lts.x | bash -
116
+ $PKG_MANAGER install -y nodejs
117
+ fi
118
+ fi
119
+
120
+ # 安装 Rust(如果未安装)
121
+ if ! command -v rustc &> /dev/null; then
122
+ info "安装 Rust..."
123
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
124
+ . "$HOME/.cargo/env"
125
+ fi
126
+
127
+ # 添加目标平台
128
+ info "添加 Rust 目标平台..."
129
+ case "$(uname)" in
130
+ "FreeBSD")
131
+ rustup target add x86_64-unknown-freebsd
132
+ ;;
133
+ "Darwin")
134
+ rustup target add x86_64-apple-darwin aarch64-apple-darwin
135
+ ;;
136
+ *)
137
+ rustup target add x86_64-unknown-linux-gnu
138
+ ;;
139
+ esac
140
+
141
+ # 清理包管理器缓存
142
+ case $PKG_MANAGER in
143
+ "apt-get")
144
+ rm -rf /var/lib/apt/lists/*
145
+ ;;
146
+ "pkg")
147
+ pkg clean -y
148
+ ;;
149
+ esac
150
+
151
+ # 设置时区(除了 macOS)
152
+ if [ "$(uname)" != "Darwin" ]; then
153
+ info "设置时区为 Asia/Shanghai..."
154
+ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
155
+ fi
156
+
157
+ echo -e "${GREEN}安装完成!${NC}"
serve.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ Deno.serve(async(r:Request)=>{const rs=(s:number,m:string)=>new Response(m,{status:s,headers:{"Access-Control-Allow-Origin":"*"}});const h=r.headers.get("x-co");if(!h)return rs(400,"Missing header");const a=["api2.cursor.sh","www.cursor.com"];if(!a.includes(h))return rs(403,"Host denied");const u=new URL(r.url),p=["/aiserver.v1.AiService/StreamChat","/aiserver.v1.AiService/StreamChatWeb","/auth/full_stripe_profile","/api/usage","/api/auth/me"];if(!p.includes(u.pathname))return rs(404,"Path invalid");const hd=new Headers(r.headers);hd.delete("x-co");hd.set("Host",h);try{const f=await fetch(`https://${h}${u.pathname}${u.search}`,{method:r.method,headers:hd,body:r.body});const fh=new Headers(f.headers);fh.set("Access-Control-Allow-Origin","*");return new Response(f.body,{status:f.status,headers:fh})}catch(e){return rs(500,"Server error")}});
src/app.rs ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ pub mod config;
2
+ pub mod constant;
3
+ pub mod model;
4
+ pub mod lazy;
src/app/config.rs ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use super::{constant::AUTHORIZATION_BEARER_PREFIX, lazy::AUTH_TOKEN, model::AppConfig};
2
+ use crate::common::model::{
3
+ config::{ConfigData, ConfigUpdateRequest},
4
+ ApiStatus, ErrorResponse, NormalResponse,
5
+ };
6
+ use axum::{
7
+ http::{header::AUTHORIZATION, HeaderMap, StatusCode},
8
+ Json,
9
+ };
10
+
11
+ // 定义处理更新操作的宏
12
+ macro_rules! handle_updates {
13
+ ($request:expr, $($field:ident => $update_fn:expr),* $(,)?) => {
14
+ $(
15
+ if let Some(value) = $request.$field {
16
+ $update_fn(value);
17
+ }
18
+ )*
19
+ };
20
+ }
21
+
22
+ // 定义处理重置操作的宏
23
+ macro_rules! handle_resets {
24
+ ($request:expr, $($field:ident => $reset_fn:expr),* $(,)?) => {
25
+ $(
26
+ if $request.$field.is_some() {
27
+ $reset_fn();
28
+ }
29
+ )*
30
+ };
31
+ }
32
+
33
+ pub async fn handle_config_update(
34
+ headers: HeaderMap,
35
+ Json(request): Json<ConfigUpdateRequest>,
36
+ ) -> Result<Json<NormalResponse<ConfigData>>, (StatusCode, Json<ErrorResponse>)> {
37
+ let auth_header = headers
38
+ .get(AUTHORIZATION)
39
+ .and_then(|h| h.to_str().ok())
40
+ .and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
41
+ .ok_or((
42
+ StatusCode::UNAUTHORIZED,
43
+ Json(ErrorResponse {
44
+ status: ApiStatus::Failed,
45
+ code: Some(401),
46
+ error: Some("未提供认证令牌".to_string()),
47
+ message: None,
48
+ }),
49
+ ))?;
50
+
51
+ if auth_header != AUTH_TOKEN.as_str() {
52
+ return Err((
53
+ StatusCode::UNAUTHORIZED,
54
+ Json(ErrorResponse {
55
+ status: ApiStatus::Failed,
56
+ code: Some(401),
57
+ error: Some("无效的认证令牌".to_string()),
58
+ message: None,
59
+ }),
60
+ ));
61
+ }
62
+
63
+ match request.action.as_str() {
64
+ "get" => Ok(Json(NormalResponse {
65
+ status: ApiStatus::Success,
66
+ data: Some(ConfigData {
67
+ page_content: AppConfig::get_page_content(&request.path),
68
+ vision_ability: AppConfig::get_vision_ability(),
69
+ enable_slow_pool: AppConfig::get_slow_pool(),
70
+ enable_all_claude: AppConfig::get_allow_claude(),
71
+ usage_check_models: AppConfig::get_usage_check(),
72
+ enable_dynamic_key: AppConfig::get_dynamic_key(),
73
+ share_token: AppConfig::get_share_token(),
74
+ proxies: AppConfig::get_proxies(),
75
+ include_web_references: AppConfig::get_web_refs(),
76
+ }),
77
+ message: None,
78
+ })),
79
+
80
+ "update" => {
81
+ // 处理页面内容更新
82
+ if !request.path.is_empty() && request.content.is_some() {
83
+ let content = request.content.unwrap();
84
+ if let Err(e) = AppConfig::update_page_content(&request.path, content) {
85
+ return Err((
86
+ StatusCode::INTERNAL_SERVER_ERROR,
87
+ Json(ErrorResponse {
88
+ status: ApiStatus::Failed,
89
+ code: Some(500),
90
+ error: Some(format!("更新页面内容失败: {}", e)),
91
+ message: None,
92
+ }),
93
+ ));
94
+ }
95
+ }
96
+
97
+ handle_updates!(request,
98
+ vision_ability => AppConfig::update_vision_ability,
99
+ enable_slow_pool => AppConfig::update_slow_pool,
100
+ enable_all_claude => AppConfig::update_allow_claude,
101
+ usage_check_models => AppConfig::update_usage_check,
102
+ enable_dynamic_key => AppConfig::update_dynamic_key,
103
+ share_token => AppConfig::update_share_token,
104
+ proxies => AppConfig::update_proxies,
105
+ include_web_references => AppConfig::update_web_refs,
106
+ );
107
+
108
+ Ok(Json(NormalResponse {
109
+ status: ApiStatus::Success,
110
+ data: None,
111
+ message: Some("配置已更新".to_string()),
112
+ }))
113
+ }
114
+
115
+ "reset" => {
116
+ // 重置页面内容
117
+ if !request.path.is_empty() {
118
+ if let Err(e) = AppConfig::reset_page_content(&request.path) {
119
+ return Err((
120
+ StatusCode::INTERNAL_SERVER_ERROR,
121
+ Json(ErrorResponse {
122
+ status: ApiStatus::Failed,
123
+ code: Some(500),
124
+ error: Some(format!("重置页面内容失败: {}", e)),
125
+ message: None,
126
+ }),
127
+ ));
128
+ }
129
+ }
130
+
131
+ handle_resets!(request,
132
+ vision_ability => AppConfig::reset_vision_ability,
133
+ enable_slow_pool => AppConfig::reset_slow_pool,
134
+ enable_all_claude => AppConfig::reset_allow_claude,
135
+ usage_check_models => AppConfig::reset_usage_check,
136
+ enable_dynamic_key => AppConfig::reset_dynamic_key,
137
+ share_token => AppConfig::reset_share_token,
138
+ proxies => AppConfig::reset_proxies,
139
+ include_web_references => AppConfig::reset_web_refs,
140
+ );
141
+
142
+ Ok(Json(NormalResponse {
143
+ status: ApiStatus::Success,
144
+ data: None,
145
+ message: Some("配置已重置".to_string()),
146
+ }))
147
+ }
148
+
149
+ _ => Err((
150
+ StatusCode::BAD_REQUEST,
151
+ Json(ErrorResponse {
152
+ status: ApiStatus::Failed,
153
+ code: Some(400),
154
+ error: Some("无效的操作类型".to_string()),
155
+ message: None,
156
+ }),
157
+ )),
158
+ }
159
+ }
src/app/constant.rs ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ macro_rules! def_pub_const {
2
+ ($name:ident, $value:expr) => {
3
+ pub const $name: &'static str = $value;
4
+ };
5
+ }
6
+
7
+ pub const COMMA: char = ',';
8
+
9
+ def_pub_const!(PKG_VERSION, env!("CARGO_PKG_VERSION"));
10
+ // def_pub_const!(PKG_NAME, env!("CARGO_PKG_NAME"));
11
+ // def_pub_const!(PKG_DESCRIPTION, env!("CARGO_PKG_DESCRIPTION"));
12
+ // def_pub_const!(PKG_AUTHORS, env!("CARGO_PKG_AUTHORS"));
13
+ // def_pub_const!(PKG_REPOSITORY, env!("CARGO_PKG_REPOSITORY"));
14
+
15
+ def_pub_const!(EMPTY_STRING, "");
16
+
17
+ def_pub_const!(COMMA_STRING, ",");
18
+
19
+ def_pub_const!(ROUTE_ROOT_PATH, "/");
20
+ def_pub_const!(ROUTE_HEALTH_PATH, "/health");
21
+ def_pub_const!(ROUTE_GET_HASH, "/get-hash");
22
+ def_pub_const!(ROUTE_GET_CHECKSUM, "/get-checksum");
23
+ def_pub_const!(ROUTE_GET_TIMESTAMP_HEADER, "/get-tsheader");
24
+ def_pub_const!(ROUTE_USER_INFO_PATH, "/userinfo");
25
+ def_pub_const!(ROUTE_API_PATH, "/api");
26
+ def_pub_const!(ROUTE_LOGS_PATH, "/logs");
27
+ def_pub_const!(ROUTE_CONFIG_PATH, "/config");
28
+ def_pub_const!(ROUTE_TOKENS_PATH, "/tokens");
29
+ def_pub_const!(ROUTE_TOKENS_GET_PATH, "/tokens/get");
30
+ def_pub_const!(ROUTE_TOKENS_RELOAD_PATH, "/tokens/reload");
31
+ def_pub_const!(ROUTE_TOKENS_UPDATE_PATH, "/tokens/update");
32
+ def_pub_const!(ROUTE_TOKENS_ADD_PATH, "/tokens/add");
33
+ def_pub_const!(ROUTE_TOKENS_DELETE_PATH, "/tokens/delete");
34
+ def_pub_const!(ROUTE_ENV_EXAMPLE_PATH, "/env-example");
35
+ def_pub_const!(ROUTE_STATIC_PATH, "/static/{path}");
36
+ def_pub_const!(ROUTE_SHARED_STYLES_PATH, "/static/shared-styles.css");
37
+ def_pub_const!(ROUTE_SHARED_JS_PATH, "/static/shared.js");
38
+ def_pub_const!(ROUTE_ABOUT_PATH, "/about");
39
+ def_pub_const!(ROUTE_README_PATH, "/readme");
40
+ def_pub_const!(ROUTE_BASIC_CALIBRATION_PATH, "/basic-calibration");
41
+ def_pub_const!(ROUTE_BUILD_KEY_PATH, "/build-key");
42
+
43
+ def_pub_const!(DEFAULT_TOKEN_LIST_FILE_NAME, ".tokens");
44
+
45
+ def_pub_const!(STATUS_PENDING, "pending");
46
+ def_pub_const!(STATUS_SUCCESS, "success");
47
+ def_pub_const!(STATUS_FAILED, "failed");
48
+
49
+ def_pub_const!(HEADER_NAME_GHOST_MODE, "x-ghost-mode");
50
+
51
+ def_pub_const!(TRUE, "true");
52
+ def_pub_const!(FALSE, "false");
53
+
54
+ // def_pub_const!(CONTENT_TYPE_PROTO, "application/proto");
55
+ def_pub_const!(CONTENT_TYPE_CONNECT_PROTO, "application/connect+proto");
56
+ def_pub_const!(CONTENT_TYPE_TEXT_HTML_WITH_UTF8, "text/html;charset=utf-8");
57
+ def_pub_const!(
58
+ CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8,
59
+ "text/plain;charset=utf-8"
60
+ );
61
+ def_pub_const!(CONTENT_TYPE_TEXT_CSS_WITH_UTF8, "text/css;charset=utf-8");
62
+ def_pub_const!(
63
+ CONTENT_TYPE_TEXT_JS_WITH_UTF8,
64
+ "text/javascript;charset=utf-8"
65
+ );
66
+
67
+ def_pub_const!(AUTHORIZATION_BEARER_PREFIX, "Bearer ");
68
+
69
+ def_pub_const!(CURSOR_API2_HOST, "api2.cursor.sh");
70
+ def_pub_const!(CURSOR_HOST, "www.cursor.com");
71
+ def_pub_const!(CURSOR_SETTINGS_URL, "https://www.cursor.com/settings");
72
+
73
+ def_pub_const!(OBJECT_CHAT_COMPLETION, "chat.completion");
74
+ def_pub_const!(OBJECT_CHAT_COMPLETION_CHUNK, "chat.completion.chunk");
75
+
76
+ // def_pub_const!(CURSOR_API2_STREAM_CHAT, "StreamChat");
77
+ // def_pub_const!(CURSOR_API2_GET_USER_INFO, "GetUserInfo");
78
+
79
+ def_pub_const!(FINISH_REASON_STOP, "stop");
80
+
81
+ def_pub_const!(ERR_INVALID_PATH, "无效的路径");
82
+
83
+ // def_pub_const!(ERR_CHECKSUM_NO_GOOD, "checksum no good");
src/app/lazy.rs ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use super::constant::{
2
+ COMMA, CURSOR_API2_HOST, CURSOR_HOST, DEFAULT_TOKEN_LIST_FILE_NAME, EMPTY_STRING,
3
+ };
4
+ use crate::common::utils::{
5
+ parse_ascii_char_from_env, parse_bool_from_env, parse_string_from_env, parse_usize_from_env,
6
+ };
7
+ use std::sync::LazyLock;
8
+ use tokio::sync::{Mutex, OnceCell};
9
+
10
+ macro_rules! def_pub_static {
11
+ // 基础版本:直接存储 String
12
+ ($name:ident, $value:expr) => {
13
+ pub static $name: LazyLock<String> = LazyLock::new(|| $value);
14
+ };
15
+
16
+ // 环境变量版本
17
+ ($name:ident, env: $env_key:expr, default: $default:expr) => {
18
+ pub static $name: LazyLock<String> =
19
+ LazyLock::new(|| parse_string_from_env($env_key, $default).trim().to_string());
20
+ };
21
+ }
22
+
23
+ // macro_rules! def_pub_static_getter {
24
+ // ($name:ident) => {
25
+ // paste::paste! {
26
+ // pub fn [<get_ $name:lower>]() -> String {
27
+ // (*$name).clone()
28
+ // }
29
+ // }
30
+ // };
31
+ // }
32
+
33
+ def_pub_static!(ROUTE_PREFIX, env: "ROUTE_PREFIX", default: EMPTY_STRING);
34
+ def_pub_static!(AUTH_TOKEN, env: "AUTH_TOKEN", default: EMPTY_STRING);
35
+ def_pub_static!(TOKEN_LIST_FILE, env: "TOKEN_LIST_FILE", default: DEFAULT_TOKEN_LIST_FILE_NAME);
36
+ def_pub_static!(ROUTE_MODELS_PATH, format!("{}/v1/models", *ROUTE_PREFIX));
37
+ def_pub_static!(
38
+ ROUTE_CHAT_PATH,
39
+ format!("{}/v1/chat/completions", *ROUTE_PREFIX)
40
+ );
41
+
42
+ pub static START_TIME: LazyLock<chrono::DateTime<chrono::Local>> =
43
+ LazyLock::new(chrono::Local::now);
44
+
45
+ pub fn get_start_time() -> chrono::DateTime<chrono::Local> {
46
+ *START_TIME
47
+ }
48
+
49
+ def_pub_static!(DEFAULT_INSTRUCTIONS, env: "DEFAULT_INSTRUCTIONS", default: "Respond in Chinese by default");
50
+
51
+ def_pub_static!(REVERSE_PROXY_HOST, env: "REVERSE_PROXY_HOST", default: EMPTY_STRING);
52
+
53
+ const DEFAULT_KEY_PREFIX: &str = "sk-";
54
+
55
+ pub static KEY_PREFIX: LazyLock<String> = LazyLock::new(|| {
56
+ let value = parse_string_from_env("KEY_PREFIX", DEFAULT_KEY_PREFIX)
57
+ .trim()
58
+ .to_string();
59
+ if value.is_empty() {
60
+ DEFAULT_KEY_PREFIX.to_string()
61
+ } else {
62
+ value
63
+ }
64
+ });
65
+
66
+ pub static KEY_PREFIX_LEN: LazyLock<usize> = LazyLock::new(|| KEY_PREFIX.len());
67
+
68
+ pub static TOKEN_DELIMITER: LazyLock<char> = LazyLock::new(|| {
69
+ let delimiter = parse_ascii_char_from_env("TOKEN_DELIMITER", COMMA);
70
+ if delimiter.is_ascii_alphabetic()
71
+ || delimiter.is_ascii_digit()
72
+ || delimiter == '+'
73
+ || delimiter == '/'
74
+ {
75
+ COMMA
76
+ } else {
77
+ delimiter
78
+ }
79
+ });
80
+
81
+ pub static USE_COMMA_DELIMITER: LazyLock<bool> = LazyLock::new(|| {
82
+ let enable = parse_bool_from_env("USE_COMMA_DELIMITER", true);
83
+ if enable && *TOKEN_DELIMITER == COMMA {
84
+ false
85
+ } else {
86
+ enable
87
+ }
88
+ });
89
+
90
+ pub static USE_REVERSE_PROXY: LazyLock<bool> = LazyLock::new(|| !REVERSE_PROXY_HOST.is_empty());
91
+
92
+ macro_rules! def_cursor_api_url {
93
+ ($name:ident, $api_host:expr, $path:expr) => {
94
+ pub static $name: LazyLock<String> = LazyLock::new(|| {
95
+ let host = if *USE_REVERSE_PROXY {
96
+ &*REVERSE_PROXY_HOST
97
+ } else {
98
+ $api_host
99
+ };
100
+ format!("https://{}{}", host, $path)
101
+ });
102
+ };
103
+ }
104
+
105
+ def_cursor_api_url!(
106
+ CURSOR_API2_CHAT_URL,
107
+ CURSOR_API2_HOST,
108
+ "/aiserver.v1.AiService/StreamChat"
109
+ );
110
+
111
+ def_cursor_api_url!(
112
+ CURSOR_API2_CHAT_WEB_URL,
113
+ CURSOR_API2_HOST,
114
+ "/aiserver.v1.AiService/StreamChatWeb"
115
+ );
116
+
117
+ def_cursor_api_url!(
118
+ CURSOR_API2_STRIPE_URL,
119
+ CURSOR_API2_HOST,
120
+ "/auth/full_stripe_profile"
121
+ );
122
+
123
+ def_cursor_api_url!(CURSOR_USAGE_API_URL, CURSOR_HOST, "/api/usage");
124
+
125
+ def_cursor_api_url!(CURSOR_USER_API_URL, CURSOR_HOST, "/api/auth/me");
126
+
127
+ pub(super) static LOGS_FILE_PATH: LazyLock<String> =
128
+ LazyLock::new(|| parse_string_from_env("LOGS_FILE_PATH", "logs.bin"));
129
+
130
+ pub(super) static PAGES_FILE_PATH: LazyLock<String> =
131
+ LazyLock::new(|| parse_string_from_env("PAGES_FILE_PATH", "pages.bin"));
132
+
133
+ pub static DEBUG: LazyLock<bool> = LazyLock::new(|| parse_bool_from_env("DEBUG", false));
134
+
135
+ // 使用环境变量 "DEBUG_LOG_FILE" 来指定日志文件路径,默认值为 "debug.log"
136
+ static DEBUG_LOG_FILE: LazyLock<String> =
137
+ LazyLock::new(|| parse_string_from_env("DEBUG_LOG_FILE", "debug.log"));
138
+
139
+ // 使用 OnceCell 结合 Mutex 来异步初始化 LOG_FILE
140
+ static LOG_FILE: OnceCell<Mutex<tokio::fs::File>> = OnceCell::const_new();
141
+
142
+ pub(crate) async fn get_log_file() -> &'static Mutex<tokio::fs::File> {
143
+ LOG_FILE
144
+ .get_or_init(|| async {
145
+ Mutex::new(
146
+ tokio::fs::OpenOptions::new()
147
+ .create(true)
148
+ .append(true)
149
+ .open(&*DEBUG_LOG_FILE)
150
+ .await
151
+ .expect("无法打开日志文件"),
152
+ )
153
+ })
154
+ .await
155
+ }
156
+
157
+ #[macro_export]
158
+ macro_rules! debug_println {
159
+ ($($arg:tt)*) => {
160
+ if *crate::app::lazy::DEBUG {
161
+ let time = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
162
+ let log_message = format!("{} - {}", time, format!($($arg)*));
163
+ use tokio::io::AsyncWriteExt as _;
164
+
165
+ // 使用 tokio 的 spawn 在后台异步写入日志
166
+ tokio::spawn(async move {
167
+ let log_file = crate::app::lazy::get_log_file().await;
168
+ // 使用 MutexGuard 获取可变引用
169
+ let mut file = log_file.lock().await;
170
+ if let Err(err) = file.write_all(log_message.as_bytes()).await {
171
+ eprintln!("写入日志文件失败: {}", err);
172
+ }
173
+ if let Err(err) = file.write_all(b"\n").await {
174
+ eprintln!("写入换行符失败: {}", err);
175
+ }
176
+ // 可以选择在写入失败时 panic,或者忽略
177
+ // panic!("写入日志文件失败: {}", err);
178
+ });
179
+ }
180
+ };
181
+ }
182
+
183
+ pub static REQUEST_LOGS_LIMIT: LazyLock<usize> =
184
+ LazyLock::new(|| std::cmp::min(parse_usize_from_env("REQUEST_LOGS_LIMIT", 100), 2000));
185
+
186
+ pub static SERVICE_TIMEOUT: LazyLock<u64> = LazyLock::new(|| {
187
+ let timeout = parse_usize_from_env("SERVICE_TIMEOUT", 30);
188
+ u64::try_from(timeout).map(|t| t.min(600)).unwrap_or(30)
189
+ });
src/app/model.rs ADDED
@@ -0,0 +1,486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use crate::{
2
+ app::constant::{
3
+ EMPTY_STRING, ERR_INVALID_PATH, ROUTE_ABOUT_PATH, ROUTE_API_PATH, ROUTE_BUILD_KEY_PATH,
4
+ ROUTE_CONFIG_PATH, ROUTE_LOGS_PATH, ROUTE_README_PATH, ROUTE_ROOT_PATH,
5
+ ROUTE_SHARED_JS_PATH, ROUTE_SHARED_STYLES_PATH, ROUTE_TOKENS_PATH,
6
+ },
7
+ chat::model::Message,
8
+ common::{
9
+ client::rebuild_http_client,
10
+ model::{userinfo::TokenProfile, ApiStatus},
11
+ utils::{generate_checksum_with_repair, parse_bool_from_env, parse_string_from_env},
12
+ },
13
+ };
14
+ use parking_lot::RwLock;
15
+ use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
16
+ use serde::{Deserialize, Serialize};
17
+ use std::sync::LazyLock;
18
+
19
+ mod usage_check;
20
+ pub use usage_check::UsageCheck;
21
+ mod config;
22
+ mod proxies;
23
+ pub use proxies::Proxies;
24
+ mod build_key;
25
+ pub use build_key::*;
26
+
27
+ use super::constant::{STATUS_FAILED, STATUS_PENDING, STATUS_SUCCESS};
28
+
29
+ // 页面内容类型枚举
30
+ #[derive(Clone, Serialize, Deserialize, Archive, RkyvDeserialize, RkyvSerialize)]
31
+ #[serde(tag = "type", content = "content")]
32
+ pub enum PageContent {
33
+ #[serde(rename = "default")]
34
+ Default, // 默认行为
35
+ #[serde(rename = "text")]
36
+ Text(String), // 纯文本
37
+ #[serde(rename = "html")]
38
+ Html(String), // HTML 内容
39
+ }
40
+
41
+ impl Default for PageContent {
42
+ fn default() -> Self {
43
+ Self::Default
44
+ }
45
+ }
46
+
47
+ // 静态配置
48
+ #[derive(Default, Clone)]
49
+ pub struct AppConfig {
50
+ vision_ability: VisionAbility,
51
+ slow_pool: bool,
52
+ allow_claude: bool,
53
+ pages: Pages,
54
+ usage_check: UsageCheck,
55
+ dynamic_key: bool,
56
+ share_token: String,
57
+ is_share: bool,
58
+ proxies: Proxies,
59
+ web_refs: bool,
60
+ }
61
+
62
+ #[derive(Serialize, Deserialize, Clone, Copy, PartialEq)]
63
+ pub enum VisionAbility {
64
+ #[serde(rename = "none", alias = "disabled")]
65
+ None,
66
+ #[serde(rename = "base64", alias = "base64-only")]
67
+ Base64,
68
+ #[serde(rename = "all", alias = "base64-http")]
69
+ All,
70
+ }
71
+
72
+ impl VisionAbility {
73
+ pub fn from_str(s: &str) -> Self {
74
+ match s.to_lowercase().as_str() {
75
+ "none" | "disabled" => Self::None,
76
+ "base64" | "base64-only" => Self::Base64,
77
+ "all" | "base64-http" => Self::All,
78
+ _ => Self::default(),
79
+ }
80
+ }
81
+
82
+ pub fn is_none(&self) -> bool {
83
+ matches!(self, VisionAbility::None)
84
+ }
85
+ }
86
+
87
+ impl Default for VisionAbility {
88
+ fn default() -> Self {
89
+ Self::Base64
90
+ }
91
+ }
92
+
93
+ #[derive(Clone, Default, Archive, RkyvDeserialize, RkyvSerialize)]
94
+ pub struct Pages {
95
+ pub root_content: PageContent,
96
+ pub logs_content: PageContent,
97
+ pub config_content: PageContent,
98
+ pub tokeninfo_content: PageContent,
99
+ pub shared_styles_content: PageContent,
100
+ pub shared_js_content: PageContent,
101
+ pub about_content: PageContent,
102
+ pub readme_content: PageContent,
103
+ pub api_content: PageContent,
104
+ pub build_key_content: PageContent,
105
+ }
106
+
107
+ // 运行时状态
108
+ pub struct AppState {
109
+ pub total_requests: u64,
110
+ pub active_requests: u64,
111
+ pub error_requests: u64,
112
+ pub request_logs: Vec<RequestLog>,
113
+ pub token_infos: Vec<TokenInfo>,
114
+ }
115
+
116
+ // 全局配置实例
117
+ pub static APP_CONFIG: LazyLock<RwLock<AppConfig>> =
118
+ LazyLock::new(|| RwLock::new(AppConfig::default()));
119
+
120
+ macro_rules! config_methods {
121
+ ($($field:ident: $type:ty, $default:expr;)*) => {
122
+ $(
123
+ paste::paste! {
124
+ pub fn [<get_ $field>]() -> $type
125
+ where
126
+ $type: Copy + PartialEq,
127
+ {
128
+ APP_CONFIG.read().$field
129
+ }
130
+
131
+ pub fn [<update_ $field>](value: $type)
132
+ where
133
+ $type: Copy + PartialEq,
134
+ {
135
+ let current = Self::[<get_ $field>]();
136
+ if current != value {
137
+ APP_CONFIG.write().$field = value;
138
+ }
139
+ }
140
+
141
+ pub fn [<reset_ $field>]()
142
+ where
143
+ $type: Copy + PartialEq,
144
+ {
145
+ let default_value = $default;
146
+ let current = Self::[<get_ $field>]();
147
+ if current != default_value {
148
+ APP_CONFIG.write().$field = default_value;
149
+ }
150
+ }
151
+ }
152
+ )*
153
+ };
154
+ }
155
+
156
+ macro_rules! config_methods_clone {
157
+ ($($field:ident: $type:ty, $default:expr;)*) => {
158
+ $(
159
+ paste::paste! {
160
+ pub fn [<get_ $field>]() -> $type
161
+ where
162
+ $type: Clone + PartialEq,
163
+ {
164
+ APP_CONFIG.read().$field.clone()
165
+ }
166
+
167
+ pub fn [<update_ $field>](value: $type)
168
+ where
169
+ $type: Clone + PartialEq,
170
+ {
171
+ let current = Self::[<get_ $field>]();
172
+ if current != value {
173
+ APP_CONFIG.write().$field = value;
174
+ }
175
+ }
176
+
177
+ pub fn [<reset_ $field>]()
178
+ where
179
+ $type: Clone + PartialEq,
180
+ {
181
+ let default_value = $default;
182
+ let current = Self::[<get_ $field>]();
183
+ if current != default_value {
184
+ APP_CONFIG.write().$field = default_value;
185
+ }
186
+ }
187
+ }
188
+ )*
189
+ };
190
+ }
191
+
192
+ impl AppConfig {
193
+ pub fn init() {
194
+ let mut config = APP_CONFIG.write();
195
+ config.vision_ability =
196
+ VisionAbility::from_str(&parse_string_from_env("VISION_ABILITY", EMPTY_STRING));
197
+ config.slow_pool = parse_bool_from_env("ENABLE_SLOW_POOL", false);
198
+ config.allow_claude = parse_bool_from_env("PASS_ANY_CLAUDE", false);
199
+ config.usage_check =
200
+ UsageCheck::from_str(&parse_string_from_env("USAGE_CHECK", EMPTY_STRING));
201
+ config.dynamic_key = parse_bool_from_env("DYNAMIC_KEY", false);
202
+ config.share_token = parse_string_from_env("SHARED_TOKEN", EMPTY_STRING);
203
+ config.is_share = !config.share_token.is_empty();
204
+ config.proxies = match std::env::var("PROXIES") {
205
+ Ok(proxies) => Proxies::from_str(proxies.as_str()),
206
+ Err(_) => Proxies::default(),
207
+ };
208
+ config.web_refs = parse_bool_from_env("INCLUDE_WEB_REFERENCES", false)
209
+ }
210
+
211
+ config_methods! {
212
+ slow_pool: bool, false;
213
+ allow_claude: bool, false;
214
+ dynamic_key: bool, false;
215
+ web_refs: bool, false;
216
+ }
217
+
218
+ config_methods_clone! {
219
+ vision_ability: VisionAbility, VisionAbility::default();
220
+ usage_check: UsageCheck, UsageCheck::default();
221
+ }
222
+
223
+ pub fn get_share_token() -> String {
224
+ APP_CONFIG.read().share_token.clone()
225
+ }
226
+
227
+ pub fn update_share_token(value: String) {
228
+ let current = Self::get_share_token();
229
+ if current != value {
230
+ let mut config = APP_CONFIG.write();
231
+ config.share_token = value;
232
+ config.is_share = !config.share_token.is_empty();
233
+ }
234
+ }
235
+
236
+ pub fn reset_share_token() {
237
+ let current = Self::get_share_token();
238
+ if !current.is_empty() {
239
+ let mut config = APP_CONFIG.write();
240
+ config.share_token = String::new();
241
+ config.is_share = false;
242
+ }
243
+ }
244
+
245
+ pub fn get_proxies() -> Proxies {
246
+ APP_CONFIG.read().proxies.clone()
247
+ }
248
+
249
+ pub fn update_proxies(value: Proxies) {
250
+ let current = Self::get_proxies();
251
+ if current != value {
252
+ let mut config = APP_CONFIG.write();
253
+ config.proxies = value;
254
+ rebuild_http_client();
255
+ }
256
+ }
257
+
258
+ pub fn reset_proxies() {
259
+ let default_value = Proxies::default();
260
+ let current = Self::get_proxies();
261
+ if current != default_value {
262
+ let mut config = APP_CONFIG.write();
263
+ config.proxies = default_value;
264
+ rebuild_http_client();
265
+ }
266
+ }
267
+
268
+ pub fn get_page_content(path: &str) -> Option<PageContent> {
269
+ match path {
270
+ ROUTE_ROOT_PATH => Some(APP_CONFIG.read().pages.root_content.clone()),
271
+ ROUTE_LOGS_PATH => Some(APP_CONFIG.read().pages.logs_content.clone()),
272
+ ROUTE_CONFIG_PATH => Some(APP_CONFIG.read().pages.config_content.clone()),
273
+ ROUTE_TOKENS_PATH => Some(APP_CONFIG.read().pages.tokeninfo_content.clone()),
274
+ ROUTE_SHARED_STYLES_PATH => Some(APP_CONFIG.read().pages.shared_styles_content.clone()),
275
+ ROUTE_SHARED_JS_PATH => Some(APP_CONFIG.read().pages.shared_js_content.clone()),
276
+ ROUTE_ABOUT_PATH => Some(APP_CONFIG.read().pages.about_content.clone()),
277
+ ROUTE_README_PATH => Some(APP_CONFIG.read().pages.readme_content.clone()),
278
+ ROUTE_API_PATH => Some(APP_CONFIG.read().pages.api_content.clone()),
279
+ ROUTE_BUILD_KEY_PATH => Some(APP_CONFIG.read().pages.build_key_content.clone()),
280
+ _ => None,
281
+ }
282
+ }
283
+
284
+ pub fn update_page_content(path: &str, content: PageContent) -> Result<(), &'static str> {
285
+ let mut config = APP_CONFIG.write();
286
+ match path {
287
+ ROUTE_ROOT_PATH => config.pages.root_content = content,
288
+ ROUTE_LOGS_PATH => config.pages.logs_content = content,
289
+ ROUTE_CONFIG_PATH => config.pages.config_content = content,
290
+ ROUTE_TOKENS_PATH => config.pages.tokeninfo_content = content,
291
+ ROUTE_SHARED_STYLES_PATH => config.pages.shared_styles_content = content,
292
+ ROUTE_SHARED_JS_PATH => config.pages.shared_js_content = content,
293
+ ROUTE_ABOUT_PATH => config.pages.about_content = content,
294
+ ROUTE_README_PATH => config.pages.readme_content = content,
295
+ ROUTE_API_PATH => config.pages.api_content = content,
296
+ ROUTE_BUILD_KEY_PATH => config.pages.build_key_content = content,
297
+ _ => return Err(ERR_INVALID_PATH),
298
+ }
299
+ Ok(())
300
+ }
301
+
302
+ pub fn reset_page_content(path: &str) -> Result<(), &'static str> {
303
+ let mut config = APP_CONFIG.write();
304
+ match path {
305
+ ROUTE_ROOT_PATH => config.pages.root_content = PageContent::default(),
306
+ ROUTE_LOGS_PATH => config.pages.logs_content = PageContent::default(),
307
+ ROUTE_CONFIG_PATH => config.pages.config_content = PageContent::default(),
308
+ ROUTE_TOKENS_PATH => config.pages.tokeninfo_content = PageContent::default(),
309
+ ROUTE_SHARED_STYLES_PATH => config.pages.shared_styles_content = PageContent::default(),
310
+ ROUTE_SHARED_JS_PATH => config.pages.shared_js_content = PageContent::default(),
311
+ ROUTE_ABOUT_PATH => config.pages.about_content = PageContent::default(),
312
+ ROUTE_README_PATH => config.pages.readme_content = PageContent::default(),
313
+ ROUTE_API_PATH => config.pages.api_content = PageContent::default(),
314
+ ROUTE_BUILD_KEY_PATH => config.pages.build_key_content = PageContent::default(),
315
+ _ => return Err(ERR_INVALID_PATH),
316
+ }
317
+ Ok(())
318
+ }
319
+
320
+ pub fn is_share() -> bool {
321
+ APP_CONFIG.read().is_share
322
+ }
323
+ }
324
+
325
+ impl AppState {
326
+ pub fn new(token_infos: Vec<TokenInfo>) -> Self {
327
+ // 尝试加载保存的日志
328
+ let request_logs = tokio::task::block_in_place(|| {
329
+ tokio::runtime::Handle::current()
330
+ .block_on(async { Self::load_saved_logs().await.unwrap_or_default() })
331
+ });
332
+
333
+ Self {
334
+ total_requests: request_logs.len() as u64,
335
+ active_requests: 0,
336
+ error_requests: request_logs
337
+ .iter()
338
+ .filter(|log| matches!(log.status, LogStatus::Failed))
339
+ .count() as u64,
340
+ request_logs,
341
+ token_infos,
342
+ }
343
+ }
344
+
345
+ pub fn update_checksum(&mut self) {
346
+ for token_info in self.token_infos.iter_mut() {
347
+ token_info.checksum = generate_checksum_with_repair(&token_info.checksum);
348
+ }
349
+ }
350
+ }
351
+
352
+ #[derive(Clone, Archive, RkyvDeserialize, RkyvSerialize)]
353
+ pub enum LogStatus {
354
+ Pending,
355
+ Success,
356
+ Failed,
357
+ }
358
+
359
+ impl Serialize for LogStatus {
360
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
361
+ where
362
+ S: serde::Serializer,
363
+ {
364
+ serializer.serialize_str(self.as_str_name())
365
+ }
366
+ }
367
+
368
+ impl LogStatus {
369
+ pub fn as_str_name(&self) -> &'static str {
370
+ match self {
371
+ Self::Pending => STATUS_PENDING,
372
+ Self::Success => STATUS_SUCCESS,
373
+ Self::Failed => STATUS_FAILED,
374
+ }
375
+ }
376
+
377
+ pub fn from_str_name(s: &str) -> Option<Self> {
378
+ match s {
379
+ STATUS_PENDING => Some(Self::Pending),
380
+ STATUS_SUCCESS => Some(Self::Success),
381
+ STATUS_FAILED => Some(Self::Failed),
382
+ _ => None,
383
+ }
384
+ }
385
+ }
386
+
387
+ // 请求日志
388
+ #[derive(Serialize, Clone, Archive, RkyvDeserialize, RkyvSerialize)]
389
+ pub struct RequestLog {
390
+ pub id: u64,
391
+ pub timestamp: chrono::DateTime<chrono::Local>,
392
+ pub model: String,
393
+ pub token_info: TokenInfo,
394
+ #[serde(skip_serializing_if = "Option::is_none")]
395
+ pub prompt: Option<String>,
396
+ pub timing: TimingInfo,
397
+ pub stream: bool,
398
+ pub status: LogStatus,
399
+ #[serde(skip_serializing_if = "Option::is_none")]
400
+ pub error: Option<String>,
401
+ }
402
+
403
+ #[derive(Serialize, Clone, Archive, RkyvDeserialize, RkyvSerialize)]
404
+ pub struct TimingInfo {
405
+ pub total: f64, // 总用时(秒)
406
+ #[serde(skip_serializing_if = "Option::is_none")]
407
+ pub first: Option<f64>, // 首字时间(秒)
408
+ }
409
+
410
+ // 聊天请求
411
+ #[derive(Deserialize)]
412
+ pub struct ChatRequest {
413
+ pub model: String,
414
+ pub messages: Vec<Message>,
415
+ #[serde(default)]
416
+ pub stream: bool,
417
+ }
418
+
419
+ // 用于存储 token 信息
420
+ #[derive(Serialize, Clone, Archive, RkyvDeserialize, RkyvSerialize)]
421
+ pub struct TokenInfo {
422
+ pub token: String,
423
+ pub checksum: String,
424
+ #[serde(skip_serializing_if = "Option::is_none")]
425
+ pub profile: Option<TokenProfile>,
426
+ }
427
+
428
+ // TokenUpdateRequest 结构体
429
+ #[derive(Deserialize)]
430
+ pub struct TokenUpdateRequest {
431
+ pub tokens: String,
432
+ }
433
+
434
+ #[derive(Deserialize)]
435
+ pub struct TokenAddRequestTokenInfo {
436
+ pub token: String,
437
+ #[serde(default)]
438
+ pub checksum: Option<String>,
439
+ }
440
+
441
+ // TokensDeleteRequest 结构体
442
+ #[derive(Deserialize)]
443
+ pub struct TokensDeleteRequest {
444
+ #[serde(default)]
445
+ pub tokens: Vec<String>,
446
+ #[serde(default)]
447
+ pub expectation: TokensDeleteResponseExpectation,
448
+ }
449
+
450
+ #[derive(Deserialize, Default)]
451
+ #[serde(rename_all = "snake_case")]
452
+ pub enum TokensDeleteResponseExpectation {
453
+ #[default]
454
+ Simple,
455
+ UpdatedTokens,
456
+ FailedTokens,
457
+ Detailed,
458
+ }
459
+
460
+ impl TokensDeleteResponseExpectation {
461
+ pub fn needs_updated_tokens(&self) -> bool {
462
+ matches!(
463
+ self,
464
+ TokensDeleteResponseExpectation::UpdatedTokens
465
+ | TokensDeleteResponseExpectation::Detailed
466
+ )
467
+ }
468
+
469
+ pub fn needs_failed_tokens(&self) -> bool {
470
+ matches!(
471
+ self,
472
+ TokensDeleteResponseExpectation::FailedTokens
473
+ | TokensDeleteResponseExpectation::Detailed
474
+ )
475
+ }
476
+ }
477
+
478
+ // TokensDeleteResponse 结构体
479
+ #[derive(Serialize)]
480
+ pub struct TokensDeleteResponse {
481
+ pub status: ApiStatus,
482
+ #[serde(skip_serializing_if = "Option::is_none")]
483
+ pub updated_tokens: Option<Vec<String>>,
484
+ #[serde(skip_serializing_if = "Option::is_none")]
485
+ pub failed_tokens: Option<Vec<String>>,
486
+ }
src/app/model/build_key.rs ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use serde::{Deserialize, Serialize};
2
+
3
+ use crate::{app::constant::COMMA, chat::constant::AVAILABLE_MODELS};
4
+
5
+ #[derive(Deserialize)]
6
+ pub struct BuildKeyRequest {
7
+ pub auth_token: String,
8
+ #[serde(default)]
9
+ pub disable_vision: Option<bool>,
10
+ #[serde(default)]
11
+ pub enable_slow_pool: Option<bool>,
12
+ #[serde(default)]
13
+ pub usage_check_models: Option<UsageCheckModelConfig>,
14
+ #[serde(default)]
15
+ pub include_web_references: Option<bool>,
16
+ }
17
+ pub struct UsageCheckModelConfig {
18
+ pub model_type: UsageCheckModelType,
19
+ pub model_ids: Vec<&'static str>,
20
+ }
21
+
22
+ impl<'de> Deserialize<'de> for UsageCheckModelConfig {
23
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
24
+ where
25
+ D: serde::Deserializer<'de>,
26
+ {
27
+ #[derive(Deserialize)]
28
+ struct Helper {
29
+ #[serde(rename = "type")]
30
+ model_type: UsageCheckModelType,
31
+ #[serde(default)]
32
+ model_ids: String,
33
+ }
34
+
35
+ let helper = Helper::deserialize(deserializer)?;
36
+
37
+ let model_ids = if helper.model_ids.is_empty() {
38
+ Vec::new()
39
+ } else {
40
+ helper
41
+ .model_ids
42
+ .split(COMMA)
43
+ .filter_map(|model| {
44
+ let model = model.trim();
45
+ AVAILABLE_MODELS
46
+ .iter()
47
+ .find(|m| m.id == model)
48
+ .map(|m| m.id)
49
+ })
50
+ .collect()
51
+ };
52
+
53
+ Ok(UsageCheckModelConfig {
54
+ model_type: helper.model_type,
55
+ model_ids,
56
+ })
57
+ }
58
+ }
59
+
60
+ #[derive(Deserialize)]
61
+ #[serde(rename_all = "lowercase")]
62
+ pub enum UsageCheckModelType {
63
+ Default,
64
+ Disabled,
65
+ All,
66
+ Custom,
67
+ }
68
+
69
+ #[derive(Serialize)]
70
+ #[serde(rename_all = "lowercase")]
71
+ pub enum BuildKeyResponse {
72
+ Key(String),
73
+ Error(String),
74
+ }
src/app/model/config.rs ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use memmap2::{MmapMut, MmapOptions};
2
+ use rkyv::{archived_root, Deserialize as _};
3
+ use std::fs::OpenOptions;
4
+
5
+ use crate::app::lazy::{LOGS_FILE_PATH, PAGES_FILE_PATH};
6
+
7
+ use super::{AppConfig, AppState, Pages, RequestLog, APP_CONFIG};
8
+
9
+ impl AppState {
10
+ // 保存日志的方法
11
+ pub(crate) async fn save_logs(&self) -> Result<(), Box<dyn std::error::Error>> {
12
+ // 序列化日志
13
+ let bytes = rkyv::to_bytes::<_, 256>(&self.request_logs)?;
14
+
15
+ // 创建或打开文件
16
+ let file = OpenOptions::new()
17
+ .read(true)
18
+ .write(true)
19
+ .create(true)
20
+ .open(LOGS_FILE_PATH.as_str())?;
21
+
22
+ // 添加大小检查
23
+ if bytes.len() > usize::MAX / 2 {
24
+ return Err("日志数据过大".into());
25
+ }
26
+
27
+ // 设置文件大小
28
+ file.set_len(bytes.len() as u64)?;
29
+
30
+ // 创建可写入的内存映射
31
+ let mut mmap = unsafe { MmapMut::map_mut(&file)? };
32
+
33
+ // 写入数据
34
+ mmap.copy_from_slice(&bytes);
35
+
36
+ // 同步到磁盘
37
+ mmap.flush()?;
38
+
39
+ Ok(())
40
+ }
41
+
42
+ // 加载日志的方法
43
+ pub(super) async fn load_saved_logs() -> Result<Vec<RequestLog>, Box<dyn std::error::Error>> {
44
+ let file = match OpenOptions::new().read(true).open(LOGS_FILE_PATH.as_str()) {
45
+ Ok(file) => file,
46
+ Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
47
+ return Ok(Vec::new());
48
+ }
49
+ Err(e) => return Err(Box::new(e)),
50
+ };
51
+
52
+ // 添加文件大小检查
53
+ if file.metadata()?.len() > usize::MAX as u64 {
54
+ return Err("日志文件过大".into());
55
+ }
56
+
57
+ // 创建只读内存映射
58
+ let mmap = unsafe { MmapOptions::new().map(&file)? };
59
+
60
+ // 验证并反序列化数据
61
+ let archived = unsafe { archived_root::<Vec<RequestLog>>(&mmap) };
62
+ Ok(archived.deserialize(&mut rkyv::Infallible)?)
63
+ }
64
+ }
65
+
66
+ impl AppConfig {
67
+ pub fn save_config() -> Result<(), Box<dyn std::error::Error>> {
68
+ let pages = APP_CONFIG.read().pages.clone();
69
+ let bytes = rkyv::to_bytes::<_, 256>(&pages)?;
70
+
71
+ let file = OpenOptions::new()
72
+ .read(true)
73
+ .write(true)
74
+ .create(true)
75
+ .open(PAGES_FILE_PATH.as_str())?;
76
+
77
+ // 添加大小检查
78
+ if bytes.len() > usize::MAX / 2 {
79
+ return Err("配置数据过大".into());
80
+ }
81
+
82
+ file.set_len(bytes.len() as u64)?;
83
+
84
+ let mut mmap = unsafe { MmapMut::map_mut(&file)? };
85
+ mmap.copy_from_slice(&bytes);
86
+ mmap.flush()?;
87
+
88
+ Ok(())
89
+ }
90
+
91
+ pub fn load_saved_config() -> Result<(), Box<dyn std::error::Error>> {
92
+ let file = match OpenOptions::new().read(true).open(PAGES_FILE_PATH.as_str()) {
93
+ Ok(file) => file,
94
+ Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
95
+ return Ok(());
96
+ }
97
+ Err(e) => return Err(Box::new(e)),
98
+ };
99
+
100
+ // 添加文件大小检查
101
+ if file.metadata()?.len() > usize::MAX as u64 {
102
+ return Err("配置文件过大".into());
103
+ }
104
+
105
+ let mmap = unsafe { MmapOptions::new().map(&file)? };
106
+
107
+ let archived = unsafe { archived_root::<Pages>(&mmap) };
108
+ let pages = archived.deserialize(&mut rkyv::Infallible)?;
109
+ let mut config = APP_CONFIG.write();
110
+ config.pages = pages;
111
+
112
+ Ok(())
113
+ }
114
+ }
src/app/model/proxies.rs ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use reqwest::{Client, Proxy};
2
+ use serde::{Serialize, Serializer};
3
+ use serde::{Deserialize, Deserializer};
4
+ // use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
5
+
6
+ use crate::app::constant::COMMA_STRING;
7
+
8
+ #[derive(Clone, Default, PartialEq)]
9
+ pub enum Proxies {
10
+ No,
11
+ #[default]
12
+ System,
13
+ List(Vec<String>),
14
+ }
15
+
16
+ impl Serialize for Proxies {
17
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
18
+ where
19
+ S: Serializer,
20
+ {
21
+ match self {
22
+ Proxies::No => serializer.serialize_str(""),
23
+ Proxies::System => serializer.serialize_str("system"),
24
+ Proxies::List(urls) => serializer.serialize_str(&urls.join(COMMA_STRING)),
25
+ }
26
+ }
27
+ }
28
+
29
+ impl<'de> Deserialize<'de> for Proxies {
30
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
31
+ where
32
+ D: Deserializer<'de>,
33
+ {
34
+ let s = <String as serde::Deserialize>::deserialize(deserializer)?;
35
+ Ok(Proxies::from_str(&s))
36
+ }
37
+ }
38
+
39
+ impl Proxies {
40
+ /// 从字符串创建 Proxies
41
+ ///
42
+ /// # Arguments
43
+ /// * `s` - 代理字符串:
44
+ /// - "" 或 "no": 不使用代理
45
+ /// - "system": 使用系统代理
46
+ /// - 其他: 尝试解析为代理列表,无效则返回 System
47
+ pub fn from_str(s: &str) -> Self {
48
+ match s.trim() {
49
+ "" | "no" => Self::No,
50
+ "system" => Self::System,
51
+ urls => {
52
+ let valid_proxies: Vec<String> = urls
53
+ .split(',')
54
+ .filter_map(|url| {
55
+ let trimmed = url.trim();
56
+ (!trimmed.is_empty() && Proxy::all(trimmed).is_ok())
57
+ .then(|| trimmed.to_string())
58
+ })
59
+ .collect();
60
+
61
+ if valid_proxies.is_empty() {
62
+ Self::default()
63
+ } else {
64
+ Self::List(valid_proxies)
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ pub fn get_client(&self) -> Client {
71
+ match self {
72
+ Proxies::No => Client::builder().no_proxy().build().unwrap(),
73
+ Proxies::System => Client::new(),
74
+ Proxies::List(list) => {
75
+ // 使用第一个代理(已经确保是有效的)
76
+ let proxy = Proxy::all(list[0].clone()).unwrap();
77
+ Client::builder().proxy(proxy).build().unwrap()
78
+ }
79
+ }
80
+ }
81
+ }
src/app/model/usage_check.rs ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use crate::{
2
+ app::constant::{COMMA, COMMA_STRING},
3
+ chat::{config::key_config, constant::AVAILABLE_MODELS},
4
+ };
5
+ use serde::{Deserialize, Serialize};
6
+ // use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
7
+
8
+ #[derive(Clone, PartialEq)]
9
+ pub enum UsageCheck {
10
+ None,
11
+ Default,
12
+ All,
13
+ Custom(Vec<&'static str>),
14
+ }
15
+
16
+ impl UsageCheck {
17
+ pub fn from_proto(model: Option<&key_config::UsageCheckModel>) -> Option<Self> {
18
+ model.map(|model| {
19
+ use key_config::usage_check_model::Type;
20
+ match Type::try_from(model.r#type).unwrap_or(Type::Default) {
21
+ Type::Default | Type::Disabled => Self::None,
22
+ Type::All => Self::All,
23
+ Type::Custom => {
24
+ let models: Vec<&'static str> = model
25
+ .model_ids
26
+ .iter()
27
+ .filter_map(|id| AVAILABLE_MODELS.iter().find(|m| m.id == id).map(|m| m.id))
28
+ .collect();
29
+ if models.is_empty() {
30
+ Self::None
31
+ } else {
32
+ Self::Custom(models)
33
+ }
34
+ }
35
+ }
36
+ })
37
+ }
38
+
39
+ // pub fn to_proto(&self) -> key_config::UsageCheckModel {
40
+ // use key_config::usage_check_model::Type;
41
+ // match self {
42
+ // Self::None => key_config::UsageCheckModel {
43
+ // r#type: Type::Disabled.into(),
44
+ // model_ids: vec![],
45
+ // },
46
+ // Self::Default => key_config::UsageCheckModel {
47
+ // r#type: Type::Default.into(),
48
+ // model_ids: vec![],
49
+ // },
50
+ // Self::All => key_config::UsageCheckModel {
51
+ // r#type: Type::All.into(),
52
+ // model_ids: vec![],
53
+ // },
54
+ // Self::Custom(models) => key_config::UsageCheckModel {
55
+ // r#type: Type::Custom.into(),
56
+ // model_ids: models.iter().map(|&s| s.to_string()).collect(),
57
+ // },
58
+ // }
59
+ // }
60
+ }
61
+
62
+ impl Default for UsageCheck {
63
+ fn default() -> Self {
64
+ Self::Default
65
+ }
66
+ }
67
+
68
+ impl Serialize for UsageCheck {
69
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
70
+ where
71
+ S: serde::Serializer,
72
+ {
73
+ use serde::ser::SerializeStruct;
74
+ let mut state = serializer.serialize_struct("UsageCheck", 1)?;
75
+ match self {
76
+ UsageCheck::None => {
77
+ state.serialize_field("type", "none")?;
78
+ }
79
+ UsageCheck::Default => {
80
+ state.serialize_field("type", "default")?;
81
+ }
82
+ UsageCheck::All => {
83
+ state.serialize_field("type", "all")?;
84
+ }
85
+ UsageCheck::Custom(models) => {
86
+ state.serialize_field("type", "list")?;
87
+ state.serialize_field("content", &models.join(COMMA_STRING))?;
88
+ }
89
+ }
90
+ state.end()
91
+ }
92
+ }
93
+
94
+ impl<'de> Deserialize<'de> for UsageCheck {
95
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
96
+ where
97
+ D: serde::Deserializer<'de>,
98
+ {
99
+ #[derive(Deserialize)]
100
+ #[serde(tag = "type", content = "content")]
101
+ enum UsageCheckHelper {
102
+ #[serde(rename = "none")]
103
+ None,
104
+ #[serde(rename = "default")]
105
+ Default,
106
+ #[serde(rename = "all")]
107
+ All,
108
+ #[serde(rename = "list")]
109
+ Custom(String),
110
+ }
111
+
112
+ let helper = <UsageCheckHelper as serde::Deserialize>::deserialize(deserializer)?;
113
+ Ok(match helper {
114
+ UsageCheckHelper::None => UsageCheck::None,
115
+ UsageCheckHelper::Default => UsageCheck::Default,
116
+ UsageCheckHelper::All => UsageCheck::All,
117
+ UsageCheckHelper::Custom(list) => {
118
+ if list.is_empty() {
119
+ return Ok(UsageCheck::None);
120
+ }
121
+
122
+ let models: Vec<&'static str> = list
123
+ .split(COMMA)
124
+ .filter_map(|model| {
125
+ let model = model.trim();
126
+ AVAILABLE_MODELS
127
+ .iter()
128
+ .find(|m| m.id == model)
129
+ .map(|m| m.id)
130
+ })
131
+ .collect();
132
+
133
+ if models.is_empty() {
134
+ UsageCheck::None
135
+ } else {
136
+ UsageCheck::Custom(models)
137
+ }
138
+ }
139
+ })
140
+ }
141
+ }
142
+
143
+ impl UsageCheck {
144
+ pub fn from_str(s: &str) -> Self {
145
+ match s.trim().to_lowercase().as_str() {
146
+ "none" | "disabled" => Self::None,
147
+ "default" => Self::Default,
148
+ "all" | "everything" => Self::All,
149
+ list => {
150
+ if list.is_empty() {
151
+ return Self::default();
152
+ }
153
+ let models: Vec<&'static str> = list
154
+ .split(COMMA)
155
+ .filter_map(|model| {
156
+ let model = model.trim();
157
+ AVAILABLE_MODELS
158
+ .iter()
159
+ .find(|m| m.id == model)
160
+ .map(|m| m.id)
161
+ })
162
+ .collect();
163
+
164
+ if models.is_empty() {
165
+ Self::default()
166
+ } else {
167
+ Self::Custom(models)
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
src/chat.rs ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ pub mod adapter;
2
+ pub mod aiserver;
3
+ pub mod config;
4
+ pub mod constant;
5
+ pub mod error;
6
+ // pub mod middleware;
7
+ pub mod model;
8
+ pub mod route;
9
+ pub mod service;
10
+ pub mod stream;
src/chat/adapter.rs ADDED
@@ -0,0 +1,473 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
2
+ use image::guess_format;
3
+ use prost::Message as _;
4
+ use reqwest::Client;
5
+ use uuid::Uuid;
6
+
7
+ use crate::{
8
+ app::{
9
+ constant::EMPTY_STRING,
10
+ lazy::DEFAULT_INSTRUCTIONS,
11
+ model::{AppConfig, VisionAbility},
12
+ },
13
+ common::client::HTTP_CLIENT,
14
+ };
15
+
16
+ use super::{
17
+ aiserver::v1::{
18
+ conversation_message, image_proto, AzureState, ChatExternalLink, ConversationMessage, ExplicitContext, GetChatRequest, ImageProto, ModelDetails
19
+ },
20
+ constant::{ERR_UNSUPPORTED_GIF, ERR_UNSUPPORTED_IMAGE_FORMAT, LONG_CONTEXT_MODELS},
21
+ model::{Message, MessageContent, Role},
22
+ };
23
+
24
+ async fn process_chat_inputs(
25
+ inputs: Vec<Message>,
26
+ disable_vision: bool,
27
+ ) -> (String, Vec<ConversationMessage>, Vec<String>) {
28
+ // 收集 system 指令
29
+ let instructions = inputs
30
+ .iter()
31
+ .filter(|input| input.role == Role::System)
32
+ .map(|input| match &input.content {
33
+ MessageContent::Text(text) => text.clone(),
34
+ MessageContent::Vision(contents) => contents
35
+ .iter()
36
+ .filter_map(|content| {
37
+ if content.content_type == "text" {
38
+ content.text.clone()
39
+ } else {
40
+ None
41
+ }
42
+ })
43
+ .collect::<Vec<String>>()
44
+ .join("\n"),
45
+ })
46
+ .collect::<Vec<String>>()
47
+ .join("\n\n");
48
+
49
+ // 使用默认指令或收集到的指令
50
+ let instructions = if instructions.is_empty() {
51
+ DEFAULT_INSTRUCTIONS.clone()
52
+ } else {
53
+ instructions
54
+ };
55
+
56
+ // 过滤出 user 和 assistant 对话
57
+ let mut chat_inputs: Vec<Message> = inputs
58
+ .into_iter()
59
+ .filter(|input| input.role == Role::User || input.role == Role::Assistant)
60
+ .collect();
61
+
62
+ // 处理空对话情况
63
+ if chat_inputs.is_empty() {
64
+ return (
65
+ instructions,
66
+ vec![ConversationMessage {
67
+ text: EMPTY_STRING.into(),
68
+ r#type: conversation_message::MessageType::Human as i32,
69
+ attached_code_chunks: vec![],
70
+ codebase_context_chunks: vec![],
71
+ commits: vec![],
72
+ pull_requests: vec![],
73
+ git_diffs: vec![],
74
+ assistant_suggested_diffs: vec![],
75
+ interpreter_results: vec![],
76
+ images: vec![],
77
+ attached_folders: vec![],
78
+ approximate_lint_errors: vec![],
79
+ bubble_id: Uuid::new_v4().to_string(),
80
+ server_bubble_id: None,
81
+ attached_folders_new: vec![],
82
+ lints: vec![],
83
+ user_responses_to_suggested_code_blocks: vec![],
84
+ relevant_files: vec![],
85
+ tool_results: vec![],
86
+ notepads: vec![],
87
+ is_capability_iteration: Some(false),
88
+ capabilities: vec![],
89
+ edit_trail_contexts: vec![],
90
+ suggested_code_blocks: vec![],
91
+ diffs_for_compressing_files: vec![],
92
+ multi_file_linter_errors: vec![],
93
+ diff_histories: vec![],
94
+ recently_viewed_files: vec![],
95
+ recent_locations_history: vec![],
96
+ is_agentic: false,
97
+ file_diff_trajectories: vec![],
98
+ conversation_summary: None,
99
+ }],
100
+ vec![],
101
+ );
102
+ }
103
+
104
+ // 处理 WebReferences 开头的 assistant 消息
105
+ chat_inputs = chat_inputs
106
+ .into_iter()
107
+ .map(|mut input| {
108
+ if let (Role::Assistant, MessageContent::Text(text)) = (&input.role, &input.content) {
109
+ if text.starts_with("WebReferences:") {
110
+ if let Some(pos) = text.find("\n\n") {
111
+ input.content = MessageContent::Text(text[pos + 2..].to_owned());
112
+ }
113
+ }
114
+ }
115
+ input
116
+ })
117
+ .collect();
118
+
119
+ // 如果第一条是 assistant,插入空的 user 消息
120
+ if chat_inputs
121
+ .first()
122
+ .map_or(false, |input| input.role == Role::Assistant)
123
+ {
124
+ chat_inputs.insert(
125
+ 0,
126
+ Message {
127
+ role: Role::User,
128
+ content: MessageContent::Text(EMPTY_STRING.into()),
129
+ },
130
+ );
131
+ }
132
+
133
+ // 处理连续相同角色的情况
134
+ let mut i = 1;
135
+ while i < chat_inputs.len() {
136
+ if chat_inputs[i].role == chat_inputs[i - 1].role {
137
+ let insert_role = if chat_inputs[i].role == Role::User {
138
+ Role::Assistant
139
+ } else {
140
+ Role::User
141
+ };
142
+ chat_inputs.insert(
143
+ i,
144
+ Message {
145
+ role: insert_role,
146
+ content: MessageContent::Text(EMPTY_STRING.into()),
147
+ },
148
+ );
149
+ }
150
+ i += 1;
151
+ }
152
+
153
+ // 确保最后一条是 user
154
+ if chat_inputs
155
+ .last()
156
+ .map_or(false, |input| input.role == Role::Assistant)
157
+ {
158
+ chat_inputs.push(Message {
159
+ role: Role::User,
160
+ content: MessageContent::Text(EMPTY_STRING.into()),
161
+ });
162
+ }
163
+
164
+ // 转换为 proto messages
165
+ let mut messages = Vec::new();
166
+ for input in chat_inputs {
167
+ let (text, images) = match input.content {
168
+ MessageContent::Text(text) => (text, vec![]),
169
+ MessageContent::Vision(contents) => {
170
+ let mut text_parts = Vec::new();
171
+ let mut images = Vec::new();
172
+
173
+ for content in contents {
174
+ match content.content_type.as_str() {
175
+ "text" => {
176
+ if let Some(text) = content.text {
177
+ text_parts.push(text);
178
+ }
179
+ }
180
+ "image_url" => {
181
+ if !disable_vision {
182
+ if let Some(image_url) = &content.image_url {
183
+ let url = image_url.url.clone();
184
+ let client = HTTP_CLIENT.read().clone();
185
+ let result = tokio::spawn(async move {
186
+ fetch_image_data(&url, client).await
187
+ });
188
+ if let Ok(Ok((image_data, dimensions))) = result.await {
189
+ images.push(ImageProto {
190
+ data: image_data,
191
+ dimension: dimensions,
192
+ });
193
+ }
194
+ }
195
+ }
196
+ }
197
+ _ => {}
198
+ }
199
+ }
200
+ (text_parts.join("\n"), images)
201
+ }
202
+ };
203
+
204
+ messages.push(ConversationMessage {
205
+ text,
206
+ r#type: if input.role == Role::User {
207
+ conversation_message::MessageType::Human as i32
208
+ } else {
209
+ conversation_message::MessageType::Ai as i32
210
+ },
211
+ attached_code_chunks: vec![],
212
+ codebase_context_chunks: vec![],
213
+ commits: vec![],
214
+ pull_requests: vec![],
215
+ git_diffs: vec![],
216
+ assistant_suggested_diffs: vec![],
217
+ interpreter_results: vec![],
218
+ images,
219
+ attached_folders: vec![],
220
+ approximate_lint_errors: vec![],
221
+ bubble_id: Uuid::new_v4().to_string(),
222
+ server_bubble_id: None,
223
+ attached_folders_new: vec![],
224
+ lints: vec![],
225
+ user_responses_to_suggested_code_blocks: vec![],
226
+ relevant_files: vec![],
227
+ tool_results: vec![],
228
+ notepads: vec![],
229
+ is_capability_iteration: None,
230
+ capabilities: vec![],
231
+ edit_trail_contexts: vec![],
232
+ suggested_code_blocks: vec![],
233
+ diffs_for_compressing_files: vec![],
234
+ multi_file_linter_errors: vec![],
235
+ diff_histories: vec![],
236
+ recently_viewed_files: vec![],
237
+ recent_locations_history: vec![],
238
+ is_agentic: false,
239
+ file_diff_trajectories: vec![],
240
+ conversation_summary: None,
241
+ });
242
+ }
243
+
244
+ let mut urls = Vec::new();
245
+ if let Some(last_msg) = messages.last() {
246
+ if last_msg.r#type == conversation_message::MessageType::Human as i32 {
247
+ let text = &last_msg.text;
248
+ let mut chars = text.chars().peekable();
249
+
250
+ while let Some(c) = chars.next() {
251
+ if c == '@' {
252
+ let mut url = String::new();
253
+ while let Some(&next_char) = chars.peek() {
254
+ if next_char.is_whitespace() {
255
+ break;
256
+ }
257
+ url.push(chars.next().unwrap());
258
+ }
259
+ if let Ok(parsed_url) = url::Url::parse(&url) {
260
+ if parsed_url.scheme() == "http" || parsed_url.scheme() == "https" {
261
+ urls.push(url);
262
+ }
263
+ }
264
+ }
265
+ }
266
+ }
267
+ }
268
+
269
+ (instructions, messages, urls)
270
+ }
271
+
272
+ async fn fetch_image_data(
273
+ url: &str,
274
+ client: Client,
275
+ ) -> Result<(Vec<u8>, Option<image_proto::Dimension>), Box<dyn std::error::Error + Send + Sync>> {
276
+ // 在进入异步操作前获取并释放锁
277
+ let vision_ability = AppConfig::get_vision_ability();
278
+
279
+ match vision_ability {
280
+ VisionAbility::None => Err("图片功能已禁用".into()),
281
+
282
+ VisionAbility::Base64 => {
283
+ if !url.starts_with("data:image/") {
284
+ return Err("仅支持 base64 编码的图片".into());
285
+ }
286
+ process_base64_image(url)
287
+ }
288
+
289
+ VisionAbility::All => {
290
+ if url.starts_with("data:image/") {
291
+ process_base64_image(url)
292
+ } else {
293
+ process_http_image(url, client).await
294
+ }
295
+ }
296
+ }
297
+ }
298
+
299
+ // 处理 base64 编码的图片
300
+ fn process_base64_image(
301
+ url: &str,
302
+ ) -> Result<(Vec<u8>, Option<image_proto::Dimension>), Box<dyn std::error::Error + Send + Sync>> {
303
+ let parts: Vec<&str> = url.split("base64,").collect();
304
+ if parts.len() != 2 {
305
+ return Err("无效的 base64 图片格式".into());
306
+ }
307
+
308
+ // 检查图片格式
309
+ let format = parts[0].to_lowercase();
310
+ if !format.contains("png")
311
+ && !format.contains("jpeg")
312
+ && !format.contains("jpg")
313
+ && !format.contains("webp")
314
+ && !format.contains("gif")
315
+ {
316
+ return Err(ERR_UNSUPPORTED_IMAGE_FORMAT.into());
317
+ }
318
+
319
+ let image_data = BASE64.decode(parts[1])?;
320
+
321
+ // 检查是否为动态 GIF
322
+ if format.contains("gif") {
323
+ if let Ok(frames) = gif::DecodeOptions::new().read_info(std::io::Cursor::new(&image_data)) {
324
+ if frames.into_iter().count() > 1 {
325
+ return Err(ERR_UNSUPPORTED_GIF.into());
326
+ }
327
+ }
328
+ }
329
+
330
+ // 获取图片尺寸
331
+ let dimensions = if let Ok(img) = image::load_from_memory(&image_data) {
332
+ Some(image_proto::Dimension {
333
+ width: img.width() as i32,
334
+ height: img.height() as i32,
335
+ })
336
+ } else {
337
+ None
338
+ };
339
+
340
+ Ok((image_data, dimensions))
341
+ }
342
+
343
+ // 处理 HTTP 图片 URL
344
+ async fn process_http_image(
345
+ url: &str,
346
+ client: Client,
347
+ ) -> Result<(Vec<u8>, Option<image_proto::Dimension>), Box<dyn std::error::Error + Send + Sync>> {
348
+ let response = client.get(url).send().await?;
349
+ let image_data = response.bytes().await?.to_vec();
350
+ let format = guess_format(&image_data)?;
351
+
352
+ // 检查图片格式
353
+ match format {
354
+ image::ImageFormat::Png | image::ImageFormat::Jpeg | image::ImageFormat::WebP => {
355
+ // 这些格式都支持
356
+ }
357
+ image::ImageFormat::Gif => {
358
+ if let Ok(frames) =
359
+ gif::DecodeOptions::new().read_info(std::io::Cursor::new(&image_data))
360
+ {
361
+ if frames.into_iter().count() > 1 {
362
+ return Err(ERR_UNSUPPORTED_GIF.into());
363
+ }
364
+ }
365
+ }
366
+ _ => return Err(ERR_UNSUPPORTED_IMAGE_FORMAT.into()),
367
+ }
368
+
369
+ // 获取图片尺寸
370
+ let dimensions = if let Ok(img) = image::load_from_memory_with_format(&image_data, format) {
371
+ Some(image_proto::Dimension {
372
+ width: img.width() as i32,
373
+ height: img.height() as i32,
374
+ })
375
+ } else {
376
+ None
377
+ };
378
+
379
+ Ok((image_data, dimensions))
380
+ }
381
+
382
+ pub async fn encode_chat_message(
383
+ inputs: Vec<Message>,
384
+ model_name: &str,
385
+ disable_vision: bool,
386
+ enable_slow_pool: bool,
387
+ is_search: bool,
388
+ ) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
389
+ // 在进入异步操作前获取并释放锁
390
+ let enable_slow_pool = {
391
+ if enable_slow_pool {
392
+ Some(true)
393
+ } else {
394
+ None
395
+ }
396
+ };
397
+
398
+ let (instructions, messages, urls) = process_chat_inputs(inputs, disable_vision).await;
399
+
400
+ let explicit_context = if !instructions.trim().is_empty() {
401
+ Some(ExplicitContext {
402
+ context: instructions,
403
+ repo_context: None,
404
+ })
405
+ } else {
406
+ None
407
+ };
408
+
409
+ let base_uuid = rand::random::<u16>();
410
+ let external_links = urls.into_iter().enumerate().map(|(i, url)| {
411
+ let uuid = base_uuid.wrapping_add(i as u16);
412
+ ChatExternalLink {
413
+ url,
414
+ uuid: uuid.to_string(),
415
+ }
416
+ }).collect();
417
+
418
+ let chat = GetChatRequest {
419
+ current_file: None,
420
+ conversation: messages,
421
+ repositories: vec![],
422
+ explicit_context,
423
+ workspace_root_path: None,
424
+ code_blocks: vec![],
425
+ model_details: Some(ModelDetails {
426
+ model_name: Some(model_name.to_string()),
427
+ api_key: None,
428
+ enable_ghost_mode: None,
429
+ azure_state: Some(AzureState {
430
+ api_key: String::new(),
431
+ base_url: String::new(),
432
+ deployment: String::new(),
433
+ use_azure: false,
434
+ }),
435
+ enable_slow_pool,
436
+ openai_api_base_url: None,
437
+ }),
438
+ documentation_identifiers: vec![],
439
+ request_id: Uuid::new_v4().to_string(),
440
+ linter_errors: None,
441
+ summary: None,
442
+ summary_up_until_index: None,
443
+ allow_long_file_scan: Some(false),
444
+ is_bash: Some(false),
445
+ conversation_id: Uuid::new_v4().to_string(),
446
+ can_handle_filenames_after_language_ids: Some(true),
447
+ use_web: if is_search {
448
+ Some("full_search".to_string())
449
+ } else {
450
+ None
451
+ },
452
+ quotes: vec![],
453
+ debug_info: None,
454
+ workspace_id: None,
455
+ external_links,
456
+ commit_notes: vec![],
457
+ long_context_mode: Some(LONG_CONTEXT_MODELS.contains(&model_name)),
458
+ is_eval: Some(false),
459
+ desired_max_tokens: None,
460
+ context_ast: None,
461
+ is_composer: None,
462
+ runnable_code_blocks: Some(false),
463
+ should_cache: Some(false),
464
+ };
465
+
466
+ let mut encoded = Vec::new();
467
+ chat.encode(&mut encoded)?;
468
+
469
+ let len_prefix = format!("{:010x}", encoded.len()).to_uppercase();
470
+ let content = hex::encode_upper(&encoded);
471
+
472
+ Ok(hex::decode(len_prefix + &content)?)
473
+ }
src/chat/aiserver.rs ADDED
@@ -0,0 +1 @@
 
 
1
+ pub mod v1;
src/chat/aiserver/v1.rs ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ include!(concat!(env!("OUT_DIR"), "/aiserver.v1.rs"));
2
+ use error_details::Error;
3
+
4
+ impl ErrorDetails {
5
+ pub fn status_code(&self) -> u16 {
6
+ match Error::try_from(self.error) {
7
+ Ok(error) => match error {
8
+ Error::Unspecified => 500,
9
+ Error::BadApiKey
10
+ | Error::InvalidAuthId
11
+ | Error::AuthTokenNotFound
12
+ | Error::AuthTokenExpired
13
+ | Error::Unauthorized => 401,
14
+ Error::NotLoggedIn
15
+ | Error::NotHighEnoughPermissions
16
+ | Error::AgentRequiresLogin
17
+ | Error::ProUserOnly
18
+ | Error::TaskNoPermissions => 403,
19
+ Error::NotFound
20
+ | Error::UserNotFound
21
+ | Error::TaskUuidNotFound
22
+ | Error::AgentEngineNotFound
23
+ | Error::GitgraphNotFound
24
+ | Error::FileNotFound => 404,
25
+ Error::FreeUserRateLimitExceeded
26
+ | Error::ProUserRateLimitExceeded
27
+ | Error::OpenaiRateLimitExceeded
28
+ | Error::OpenaiAccountLimitExceeded
29
+ | Error::GenericRateLimitExceeded
30
+ | Error::Gpt4VisionPreviewRateLimit
31
+ | Error::ApiKeyRateLimit => 429,
32
+ Error::BadRequest
33
+ | Error::BadModelName
34
+ | Error::SlashEditFileTooLong
35
+ | Error::FileUnsupported
36
+ | Error::ClaudeImageTooLarge => 400,
37
+ Error::Deprecated
38
+ | Error::FreeUserUsageLimit
39
+ | Error::ProUserUsageLimit
40
+ | Error::ResourceExhausted
41
+ | Error::Openai
42
+ | Error::MaxTokens
43
+ | Error::ApiKeyNotSupported
44
+ | Error::UserAbortedRequest
45
+ | Error::CustomMessage
46
+ | Error::OutdatedClient
47
+ | Error::Debounced
48
+ | Error::RepositoryServiceRepositoryIsNotInitialized => 500,
49
+ },
50
+ Err(_) => 500,
51
+ }
52
+ }
53
+
54
+ // pub fn is_expected(&self) -> bool {
55
+ // self.is_expected.unwrap_or_default()
56
+ // }
57
+ }
src/chat/aiserver/v1/lite.proto ADDED
@@ -0,0 +1,1156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ syntax = "proto3";
2
+ package aiserver.v1;
3
+ enum ClientSideToolV2 { // aiserver.v1.ClientSideToolV2
4
+ CLIENT_SIDE_TOOL_V2_UNSPECIFIED = 0;
5
+ CLIENT_SIDE_TOOL_V2_READ_SEMSEARCH_FILES = 1;
6
+ CLIENT_SIDE_TOOL_V2_READ_FILE_FOR_IMPORTS = 2;
7
+ CLIENT_SIDE_TOOL_V2_RIPGREP_SEARCH = 3;
8
+ CLIENT_SIDE_TOOL_V2_RUN_TERMINAL_COMMAND = 4;
9
+ CLIENT_SIDE_TOOL_V2_READ_FILE = 5;
10
+ CLIENT_SIDE_TOOL_V2_LIST_DIR = 6;
11
+ CLIENT_SIDE_TOOL_V2_EDIT_FILE = 7;
12
+ CLIENT_SIDE_TOOL_V2_FILE_SEARCH = 8;
13
+ CLIENT_SIDE_TOOL_V2_SEMANTIC_SEARCH_FULL = 9;
14
+ CLIENT_SIDE_TOOL_V2_CREATE_FILE = 10;
15
+ CLIENT_SIDE_TOOL_V2_DELETE_FILE = 11;
16
+ }
17
+ enum EmbeddingModel { // aiserver.v1.EmbeddingModel
18
+ EMBEDDING_MODEL_UNSPECIFIED = 0;
19
+ EMBEDDING_MODEL_VOYAGE_CODE_2 = 1;
20
+ EMBEDDING_MODEL_TEXT_EMBEDDINGS_LARGE_3 = 2;
21
+ EMBEDDING_MODEL_QWEN_1_5B_CUSTOM = 3;
22
+ }
23
+ enum ChunkType { // aiserver.v1.ChunkType
24
+ CHUNK_TYPE_UNSPECIFIED = 0;
25
+ CHUNK_TYPE_CODEBASE = 1;
26
+ CHUNK_TYPE_LONG_FILE = 2;
27
+ CHUNK_TYPE_DOCS = 3;
28
+ }
29
+ enum FastApplySource { // aiserver.v1.FastApplySource
30
+ FAST_APPLY_SOURCE_UNSPECIFIED = 0;
31
+ FAST_APPLY_SOURCE_COMPOSER = 1;
32
+ FAST_APPLY_SOURCE_CLICKED_APPLY = 2;
33
+ FAST_APPLY_SOURCE_CACHED_APPLY = 3;
34
+ }
35
+ enum BuiltinTool { // aiserver.v1.BuiltinTool
36
+ BUILTIN_TOOL_UNSPECIFIED = 0;
37
+ BUILTIN_TOOL_SEARCH = 1;
38
+ BUILTIN_TOOL_READ_CHUNK = 2;
39
+ BUILTIN_TOOL_GOTODEF = 3;
40
+ BUILTIN_TOOL_EDIT = 4;
41
+ BUILTIN_TOOL_UNDO_EDIT = 5;
42
+ BUILTIN_TOOL_END = 6;
43
+ BUILTIN_TOOL_NEW_FILE = 7;
44
+ BUILTIN_TOOL_ADD_TEST = 8;
45
+ BUILTIN_TOOL_RUN_TEST = 9;
46
+ BUILTIN_TOOL_DELETE_TEST = 10;
47
+ BUILTIN_TOOL_SAVE_FILE = 11;
48
+ BUILTIN_TOOL_GET_TESTS = 12;
49
+ BUILTIN_TOOL_GET_SYMBOLS = 13;
50
+ BUILTIN_TOOL_SEMANTIC_SEARCH = 14;
51
+ BUILTIN_TOOL_GET_PROJECT_STRUCTURE = 15;
52
+ BUILTIN_TOOL_CREATE_RM_FILES = 16;
53
+ BUILTIN_TOOL_RUN_TERMINAL_COMMANDS = 17;
54
+ BUILTIN_TOOL_NEW_EDIT = 18;
55
+ BUILTIN_TOOL_READ_WITH_LINTER = 19;
56
+ }
57
+ enum FeatureType { // aiserver.v1.FeatureType
58
+ FEATURE_TYPE_UNSPECIFIED = 0;
59
+ FEATURE_TYPE_EDIT = 1;
60
+ FEATURE_TYPE_GENERATE = 2;
61
+ FEATURE_TYPE_INLINE_LONG_COMPLETION = 3;
62
+ }
63
+ enum TaskStatus { // aiserver.v1.TaskStatus
64
+ TASK_STATUS_UNSPECIFIED = 0;
65
+ TASK_STATUS_RUNNING = 1;
66
+ TASK_STATUS_PAUSED = 2;
67
+ TASK_STATUS_DONE = 3;
68
+ TASK_STATUS_NOT_STARTED = 4;
69
+ }
70
+ enum RerankerAlgorithm { // aiserver.v1.RerankerAlgorithm
71
+ RERANKER_ALGORITHM_UNSPECIFIED = 0;
72
+ RERANKER_ALGORITHM_LULEA = 1;
73
+ RERANKER_ALGORITHM_UMEA = 2;
74
+ RERANKER_ALGORITHM_NONE = 3;
75
+ RERANKER_ALGORITHM_LLAMA = 4;
76
+ RERANKER_ALGORITHM_STARCODER_V1 = 5;
77
+ RERANKER_ALGORITHM_GPT_3_5_LOGPROBS = 6;
78
+ RERANKER_ALGORITHM_LULEA_HAIKU = 7;
79
+ RERANKER_ALGORITHM_COHERE = 8;
80
+ RERANKER_ALGORITHM_VOYAGE = 9;
81
+ RERANKER_ALGORITHM_VOYAGE_EMBEDS = 10;
82
+ RERANKER_ALGORITHM_IDENTITY = 11;
83
+ RERANKER_ALGORITHM_ADA_EMBEDS = 12;
84
+ }
85
+ enum RechunkerChoice { // aiserver.v1.RechunkerChoice
86
+ RECHUNKER_CHOICE_UNSPECIFIED = 0;
87
+ RECHUNKER_CHOICE_IDENTITY = 1;
88
+ RECHUNKER_CHOICE_600_TOKS = 2;
89
+ RECHUNKER_CHOICE_2400_TOKS = 3;
90
+ RECHUNKER_CHOICE_4000_TOKS = 4;
91
+ }
92
+ enum LintGenerator { // aiserver.v1.LintGenerator
93
+ LINT_GENERATOR_UNSPECIFIED = 0;
94
+ LINT_GENERATOR_NAIVE = 1;
95
+ LINT_GENERATOR_COMMENT_PIPELINE = 2;
96
+ LINT_GENERATOR_SIMPLE_BUG = 3;
97
+ LINT_GENERATOR_SIMPLE_LINT_RULES = 4;
98
+ }
99
+ enum LintDiscriminator { // aiserver.v1.LintDiscriminator
100
+ LINT_DISCRIMINATOR_UNSPECIFIED = 0;
101
+ LINT_DISCRIMINATOR_SPECIFIC_RULES = 1;
102
+ LINT_DISCRIMINATOR_COMPILE_ERRORS = 2;
103
+ LINT_DISCRIMINATOR_CHANGE_BEHAVIOR = 3;
104
+ LINT_DISCRIMINATOR_RELEVANCE = 4;
105
+ LINT_DISCRIMINATOR_USER_AWARENESS = 5;
106
+ LINT_DISCRIMINATOR_CORRECTNESS = 6;
107
+ LINT_DISCRIMINATOR_CHUNKING = 7;
108
+ LINT_DISCRIMINATOR_TYPO = 8;
109
+ LINT_DISCRIMINATOR_CONFIDENCE = 9;
110
+ LINT_DISCRIMINATOR_DISMISSED_BUGS = 10;
111
+ }
112
+ enum CppSource { // aiserver.v1.CppSource
113
+ CPP_SOURCE_UNSPECIFIED = 0;
114
+ CPP_SOURCE_LINE_CHANGE = 1;
115
+ CPP_SOURCE_TYPING = 2;
116
+ CPP_SOURCE_OPTION_HOLD = 3;
117
+ CPP_SOURCE_LINTER_ERRORS = 4;
118
+ CPP_SOURCE_PARAMETER_HINTS = 5;
119
+ CPP_SOURCE_CURSOR_PREDICTION = 6;
120
+ CPP_SOURCE_MANUAL_TRIGGER = 7;
121
+ CPP_SOURCE_EDITOR_CHANGE = 8;
122
+ }
123
+ enum ChunkingStrategy { // aiserver.v1.ChunkingStrategy
124
+ CHUNKING_STRATEGY_UNSPECIFIED = 0;
125
+ CHUNKING_STRATEGY_DEFAULT = 1;
126
+ }
127
+ message ErrorDetails { // aiserver.v1.ErrorDetails
128
+ enum Error { // aiserver.v1.ErrorDetails.Error
129
+ ERROR_UNSPECIFIED = 0;
130
+ ERROR_BAD_API_KEY = 1;
131
+ ERROR_NOT_LOGGED_IN = 2;
132
+ ERROR_INVALID_AUTH_ID = 3;
133
+ ERROR_NOT_HIGH_ENOUGH_PERMISSIONS = 4;
134
+ ERROR_BAD_MODEL_NAME = 5;
135
+ ERROR_USER_NOT_FOUND = 6;
136
+ ERROR_FREE_USER_RATE_LIMIT_EXCEEDED = 7;
137
+ ERROR_PRO_USER_RATE_LIMIT_EXCEEDED = 8;
138
+ ERROR_FREE_USER_USAGE_LIMIT = 9;
139
+ ERROR_PRO_USER_USAGE_LIMIT = 10;
140
+ ERROR_AUTH_TOKEN_NOT_FOUND = 11;
141
+ ERROR_AUTH_TOKEN_EXPIRED = 12;
142
+ ERROR_OPENAI = 13;
143
+ ERROR_OPENAI_RATE_LIMIT_EXCEEDED = 14;
144
+ ERROR_OPENAI_ACCOUNT_LIMIT_EXCEEDED = 15;
145
+ ERROR_TASK_UUID_NOT_FOUND = 16;
146
+ ERROR_TASK_NO_PERMISSIONS = 17;
147
+ ERROR_AGENT_REQUIRES_LOGIN = 18;
148
+ ERROR_AGENT_ENGINE_NOT_FOUND = 19;
149
+ ERROR_MAX_TOKENS = 20;
150
+ ERROR_USER_ABORTED_REQUEST = 21;
151
+ ERROR_GENERIC_RATE_LIMIT_EXCEEDED = 22;
152
+ ERROR_PRO_USER_ONLY = 23;
153
+ ERROR_API_KEY_NOT_SUPPORTED = 24;
154
+ ERROR_SLASH_EDIT_FILE_TOO_LONG = 26;
155
+ ERROR_FILE_UNSUPPORTED = 27;
156
+ ERROR_GPT_4_VISION_PREVIEW_RATE_LIMIT = 28;
157
+ ERROR_CUSTOM_MESSAGE = 29;
158
+ ERROR_OUTDATED_CLIENT = 30;
159
+ ERROR_CLAUDE_IMAGE_TOO_LARGE = 31;
160
+ ERROR_GITGRAPH_NOT_FOUND = 32;
161
+ ERROR_FILE_NOT_FOUND = 33;
162
+ ERROR_API_KEY_RATE_LIMIT = 34;
163
+ ERROR_DEBOUNCED = 35;
164
+ ERROR_BAD_REQUEST = 36;
165
+ ERROR_REPOSITORY_SERVICE_REPOSITORY_IS_NOT_INITIALIZED = 37;
166
+ ERROR_UNAUTHORIZED = 38;
167
+ ERROR_NOT_FOUND = 39;
168
+ ERROR_DEPRECATED = 40;
169
+ ERROR_RESOURCE_EXHAUSTED = 41;
170
+ }
171
+ Error error = 1;
172
+ CustomErrorDetails details = 2;
173
+ optional bool is_expected = 3;
174
+ }
175
+
176
+ message CustomErrorDetails { // aiserver.v1.CustomErrorDetails
177
+ string title = 1;
178
+ string detail = 2;
179
+ optional bool allow_command_links_potentially_unsafe_please_only_use_for_handwritten_trusted_markdown = 3;
180
+ optional bool is_retryable = 4;
181
+ optional bool show_request_id = 5;
182
+ }
183
+
184
+ message GetChatRequest { // aiserver.v1.GetChatRequest
185
+ CurrentFileInfo current_file = 1;
186
+ repeated ConversationMessage conversation = 2;
187
+ repeated RepositoryInfo repositories = 3;
188
+ ExplicitContext explicit_context = 4;
189
+ optional string workspace_root_path = 5;
190
+ repeated CodeBlock code_blocks = 6;
191
+ ModelDetails model_details = 7;
192
+ repeated string documentation_identifiers = 8;
193
+ string request_id = 9;
194
+ LinterErrors linter_errors = 10;
195
+ optional string summary = 11;
196
+ optional int32 summary_up_until_index = 12;
197
+ optional bool allow_long_file_scan = 13;
198
+ optional bool is_bash = 14;
199
+ string conversation_id = 15;
200
+ optional bool can_handle_filenames_after_language_ids = 16;
201
+ optional string use_web = 17;
202
+ repeated ChatQuote quotes = 18;
203
+ optional DebugInfo debug_info = 19;
204
+ optional string workspace_id = 20;
205
+ repeated ChatExternalLink external_links = 21;
206
+ repeated CommitNote commit_notes = 23;
207
+ optional bool long_context_mode = 22;
208
+ optional bool is_eval = 24;
209
+ optional int32 desired_max_tokens = 26;
210
+ ContextAST context_ast = 25;
211
+ optional bool is_composer = 27;
212
+ optional bool runnable_code_blocks = 28;
213
+ optional bool should_cache = 29;
214
+ }
215
+ message CurrentFileInfo { // aiserver.v1.CurrentFileInfo
216
+ message NotebookCell { // aiserver.v1.CurrentFileInfo.NotebookCell
217
+ }
218
+ string relative_workspace_path = 1;
219
+ string contents = 2;
220
+ bool rely_on_filesync = 18;
221
+ optional string sha_256_hash = 17;
222
+ repeated NotebookCell cells = 16;
223
+ repeated BM25Chunk top_chunks = 10;
224
+ int32 contents_start_at_line = 9;
225
+ CursorPosition cursor_position = 3;
226
+ repeated DataframeInfo dataframes = 4;
227
+ int32 total_number_of_lines = 8;
228
+ string language_id = 5;
229
+ CursorRange selection = 6;
230
+ optional int32 alternative_version_id = 11;
231
+ repeated Diagnostic diagnostics = 7;
232
+ optional int32 file_version = 14;
233
+ repeated int32 cell_start_lines = 15;
234
+ string workspace_root_path = 19;
235
+ }
236
+ message BM25Chunk { // aiserver.v1.BM25Chunk
237
+ string content = 1;
238
+ SimplestRange range = 2;
239
+ int32 score = 3;
240
+ string relative_path = 4;
241
+ }
242
+ message SimplestRange { // aiserver.v1.SimplestRange
243
+ int32 start_line = 1;
244
+ int32 end_line_inclusive = 2;
245
+ }
246
+ message CursorPosition { // aiserver.v1.CursorPosition
247
+ int32 line = 1;
248
+ int32 column = 2;
249
+ }
250
+ message DataframeInfo { // aiserver.v1.DataframeInfo
251
+ message Column { // aiserver.v1.DataframeInfo.Column
252
+ string key = 1;
253
+ string type = 2;
254
+ }
255
+ string name = 1;
256
+ string shape = 2;
257
+ int32 data_dimensionality = 3;
258
+ repeated Column columns = 6;
259
+ int32 row_count = 7;
260
+ string index_column = 8;
261
+ }
262
+ message CursorRange { // aiserver.v1.CursorRange
263
+ CursorPosition start_position = 1;
264
+ CursorPosition end_position = 2;
265
+ }
266
+ message Diagnostic { // aiserver.v1.Diagnostic
267
+ enum DiagnosticSeverity { // aiserver.v1.Diagnostic.DiagnosticSeverity
268
+ DIAGNOSTIC_SEVERITY_UNSPECIFIED = 0;
269
+ DIAGNOSTIC_SEVERITY_ERROR = 1;
270
+ DIAGNOSTIC_SEVERITY_WARNING = 2;
271
+ DIAGNOSTIC_SEVERITY_INFORMATION = 3;
272
+ DIAGNOSTIC_SEVERITY_HINT = 4;
273
+ }
274
+ message RelatedInformation { // aiserver.v1.Diagnostic.RelatedInformation
275
+ string message = 1;
276
+ CursorRange range = 2;
277
+ }
278
+ string message = 1;
279
+ CursorRange range = 2;
280
+ DiagnosticSeverity severity = 3;
281
+ repeated RelatedInformation related_information = 4;
282
+ }
283
+ message ConversationMessage { // aiserver.v1.ConversationMessage
284
+ enum MessageType { // aiserver.v1.ConversationMessage.MessageType
285
+ MESSAGE_TYPE_UNSPECIFIED = 0;
286
+ MESSAGE_TYPE_HUMAN = 1;
287
+ MESSAGE_TYPE_AI = 2;
288
+ }
289
+ message CodeChunk { // aiserver.v1.ConversationMessage.CodeChunk
290
+ enum SummarizationStrategy { // aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy
291
+ SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0;
292
+ SUMMARIZATION_STRATEGY_SUMMARIZED = 1;
293
+ SUMMARIZATION_STRATEGY_EMBEDDED = 2;
294
+ }
295
+ enum Intent { // aiserver.v1.ConversationMessage.CodeChunk.Intent
296
+ INTENT_UNSPECIFIED = 0;
297
+ INTENT_COMPOSER_FILE = 1;
298
+ INTENT_COMPRESSED_COMPOSER_FILE = 2;
299
+ INTENT_RECENTLY_VIEWED_FILE = 3;
300
+ INTENT_OUTLINE = 4;
301
+ INTENT_MENTIONED_FILE = 5;
302
+ }
303
+ string relative_workspace_path = 1;
304
+ int32 start_line_number = 2;
305
+ repeated string lines = 3;
306
+ optional SummarizationStrategy summarization_strategy = 4;
307
+ string language_identifier = 5;
308
+ optional Intent intent = 6;
309
+ optional bool is_final_version = 7;
310
+ optional bool is_first_version = 8;
311
+ optional bool contents_are_missing = 9;
312
+ }
313
+ message ApproximateLintError { // aiserver.v1.ConversationMessage.ApproximateLintError
314
+ string message = 1;
315
+ string value = 2;
316
+ int32 start_line = 3;
317
+ int32 end_line = 4;
318
+ int32 start_column = 5;
319
+ int32 end_column = 6;
320
+ }
321
+ message Lints { // aiserver.v1.ConversationMessage.Lints
322
+ GetLintsForChangeResponse lints = 1;
323
+ string chat_codeblock_model_value = 2;
324
+ }
325
+ message ToolResult { // aiserver.v1.ConversationMessage.ToolResult
326
+ message CodeChunk { // aiserver.v1.ConversationMessage.CodeChunk
327
+ enum SummarizationStrategy { // aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy
328
+ SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0;
329
+ SUMMARIZATION_STRATEGY_SUMMARIZED = 1;
330
+ SUMMARIZATION_STRATEGY_EMBEDDED = 2;
331
+ }
332
+ enum Intent { // aiserver.v1.ConversationMessage.CodeChunk.Intent
333
+ INTENT_UNSPECIFIED = 0;
334
+ INTENT_COMPOSER_FILE = 1;
335
+ INTENT_COMPRESSED_COMPOSER_FILE = 2;
336
+ INTENT_RECENTLY_VIEWED_FILE = 3;
337
+ INTENT_OUTLINE = 4;
338
+ INTENT_MENTIONED_FILE = 5;
339
+ }
340
+ string relative_workspace_path = 1;
341
+ int32 start_line_number = 2;
342
+ repeated string lines = 3;
343
+ optional SummarizationStrategy summarization_strategy = 4;
344
+ string language_identifier = 5;
345
+ optional Intent intent = 6;
346
+ optional bool is_final_version = 7;
347
+ optional bool is_first_version = 8;
348
+ optional bool contents_are_missing = 9;
349
+ }
350
+ string tool_call_id = 1;
351
+ string tool_name = 2;
352
+ uint32 tool_index = 3;
353
+ string args = 4;
354
+ string raw_args = 5;
355
+ repeated CodeChunk attached_code_chunks = 6;
356
+ optional string content = 7;
357
+ ClientSideToolV2Result result = 8;
358
+ optional ToolResultError error = 9;
359
+ }
360
+ message NotepadContext { // aiserver.v1.ConversationMessage.NotepadContext
361
+ message CodeChunk { // aiserver.v1.ConversationMessage.CodeChunk
362
+ enum SummarizationStrategy { // aiserver.v1.ConversationMessage.CodeChunk.SummarizationStrategy
363
+ SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0;
364
+ SUMMARIZATION_STRATEGY_SUMMARIZED = 1;
365
+ SUMMARIZATION_STRATEGY_EMBEDDED = 2;
366
+ }
367
+ enum Intent { // aiserver.v1.ConversationMessage.CodeChunk.Intent
368
+ INTENT_UNSPECIFIED = 0;
369
+ INTENT_COMPOSER_FILE = 1;
370
+ INTENT_COMPRESSED_COMPOSER_FILE = 2;
371
+ INTENT_RECENTLY_VIEWED_FILE = 3;
372
+ INTENT_OUTLINE = 4;
373
+ INTENT_MENTIONED_FILE = 5;
374
+ }
375
+ string relative_workspace_path = 1;
376
+ int32 start_line_number = 2;
377
+ repeated string lines = 3;
378
+ optional SummarizationStrategy summarization_strategy = 4;
379
+ string language_identifier = 5;
380
+ optional Intent intent = 6;
381
+ optional bool is_final_version = 7;
382
+ optional bool is_first_version = 8;
383
+ optional bool contents_are_missing = 9;
384
+ }
385
+ string name = 1;
386
+ string text = 2;
387
+ repeated CodeChunk attached_code_chunks = 3;
388
+ repeated string attached_folders = 4;
389
+ repeated Commit commits = 5;
390
+ repeated PullRequest pull_requests = 6;
391
+ repeated GitDiff git_diffs = 7;
392
+ repeated ImageProto images = 8;
393
+ }
394
+ message EditTrailContext { // aiserver.v1.ConversationMessage.EditTrailContext
395
+ message EditLocation { // aiserver.v1.ConversationMessage.EditLocation
396
+ string relative_workspace_path = 1;
397
+ SimplestRange range = 3;
398
+ SimplestRange initial_range = 4;
399
+ string context_lines = 5;
400
+ string text = 6;
401
+ SimplestRange text_range = 7;
402
+ }
403
+ string unique_id = 1;
404
+ repeated EditLocation edit_trail_sorted = 2;
405
+ }
406
+ message RecentLocation { // aiserver.v1.ConversationMessage.RecentLocation
407
+ string relative_workspace_path = 1;
408
+ int32 line_number = 2;
409
+ }
410
+ string text = 1;
411
+ MessageType type = 2;
412
+ repeated CodeChunk attached_code_chunks = 3;
413
+ repeated CodeBlock codebase_context_chunks = 4;
414
+ repeated Commit commits = 5;
415
+ repeated PullRequest pull_requests = 6;
416
+ repeated GitDiff git_diffs = 7;
417
+ repeated SimpleFileDiff assistant_suggested_diffs = 8;
418
+ repeated InterpreterResult interpreter_results = 9;
419
+ repeated ImageProto images = 10;
420
+ repeated string attached_folders = 11;
421
+ repeated ApproximateLintError approximate_lint_errors = 12;
422
+ string bubble_id = 13;
423
+ optional string server_bubble_id = 32;
424
+ repeated FolderInfo attached_folders_new = 14;
425
+ repeated Lints lints = 15;
426
+ repeated UserResponseToSuggestedCodeBlock user_responses_to_suggested_code_blocks = 16;
427
+ repeated string relevant_files = 17;
428
+ repeated ToolResult tool_results = 18;
429
+ repeated NotepadContext notepads = 19;
430
+ optional bool is_capability_iteration = 20;
431
+ repeated ComposerCapabilityRequest capabilities = 21;
432
+ repeated EditTrailContext edit_trail_contexts = 22;
433
+ repeated SuggestedCodeBlock suggested_code_blocks = 23;
434
+ repeated RedDiff diffs_for_compressing_files = 24;
435
+ repeated LinterErrorsWithoutFileContents multi_file_linter_errors = 25;
436
+ repeated DiffHistoryData diff_histories = 26;
437
+ repeated CodeChunk recently_viewed_files = 27;
438
+ repeated RecentLocation recent_locations_history = 28;
439
+ bool is_agentic = 29;
440
+ repeated ComposerFileDiffHistory file_diff_trajectories = 30;
441
+ optional ConversationSummary conversation_summary = 31;
442
+ }
443
+ message CodeBlock { // aiserver.v1.CodeBlock
444
+ message Signatures { // aiserver.v1.CodeBlock.Signatures
445
+ repeated CursorRange ranges = 1;
446
+ }
447
+ string relative_workspace_path = 1;
448
+ optional string file_contents = 2;
449
+ CursorRange range = 3;
450
+ string contents = 4;
451
+ Signatures signatures = 5;
452
+ optional string override_contents = 6;
453
+ optional string original_contents = 7;
454
+ repeated DetailedLine detailed_lines = 8;
455
+ }
456
+ message DetailedLine { // aiserver.v1.DetailedLine
457
+ string text = 1;
458
+ float line_number = 2;
459
+ bool is_signature = 3;
460
+ }
461
+ message Commit { // aiserver.v1.Commit
462
+ string sha = 1;
463
+ string message = 2;
464
+ string description = 3;
465
+ repeated FileDiff diff = 4;
466
+ string author = 5;
467
+ string date = 6;
468
+ }
469
+ message FileDiff { // aiserver.v1.FileDiff
470
+ message Chunk { // aiserver.v1.FileDiff.Chunk
471
+ string content = 1;
472
+ repeated string lines = 2;
473
+ int32 old_start = 3;
474
+ int32 old_lines = 4;
475
+ int32 new_start = 5;
476
+ int32 new_lines = 6;
477
+ }
478
+ string from = 1;
479
+ string to = 2;
480
+ repeated Chunk chunks = 3;
481
+ }
482
+ message PullRequest { // aiserver.v1.PullRequest
483
+ string title = 1;
484
+ string body = 2;
485
+ repeated FileDiff diff = 3;
486
+ }
487
+ message GitDiff { // aiserver.v1.GitDiff
488
+ enum DiffType { // aiserver.v1.GitDiff.DiffType
489
+ DIFF_TYPE_UNSPECIFIED = 0;
490
+ DIFF_TYPE_DIFF_TO_HEAD = 1;
491
+ DIFF_TYPE_DIFF_FROM_BRANCH_TO_MAIN = 2;
492
+ }
493
+ repeated FileDiff diffs = 1;
494
+ DiffType diff_type = 2;
495
+ }
496
+ message SimpleFileDiff { // aiserver.v1.SimpleFileDiff
497
+ message Chunk { // aiserver.v1.SimpleFileDiff.Chunk
498
+ repeated string old_lines = 1;
499
+ repeated string new_lines = 2;
500
+ LineRange old_range = 3;
501
+ LineRange new_range = 4;
502
+ }
503
+ string relative_workspace_path = 1;
504
+ repeated Chunk chunks = 3;
505
+ }
506
+ message LineRange { // aiserver.v1.LineRange
507
+ int32 start_line_number = 1;
508
+ int32 end_line_number_inclusive = 2;
509
+ }
510
+ message InterpreterResult { // aiserver.v1.InterpreterResult
511
+ string output = 1;
512
+ bool success = 2;
513
+ }
514
+ message ImageProto { // aiserver.v1.ImageProto
515
+ message Dimension { // aiserver.v1.ImageProto.Dimension
516
+ int32 width = 1;
517
+ int32 height = 2;
518
+ }
519
+ bytes data = 1;
520
+ Dimension dimension = 2;
521
+ }
522
+ message FolderInfo { // aiserver.v1.FolderInfo
523
+ string relative_path = 1;
524
+ repeated FolderFileInfo files = 2;
525
+ }
526
+ message FolderFileInfo { // aiserver.v1.FolderFileInfo
527
+ string relative_path = 1;
528
+ string content = 2;
529
+ bool truncated = 3;
530
+ float score = 4;
531
+ }
532
+ message GetLintsForChangeResponse { // aiserver.v1.GetLintsForChangeResponse
533
+ message Lint { // aiserver.v1.GetLintsForChangeResponse.Lint
534
+ message QuickFix { // aiserver.v1.GetLintsForChangeResponse.Lint.QuickFix
535
+ message Edit { // aiserver.v1.GetLintsForChangeResponse.Lint.QuickFix.Edit
536
+ string relative_workspace_path = 1;
537
+ string text = 2;
538
+ int32 start_line_number_one_indexed = 3;
539
+ int32 start_column_one_indexed = 4;
540
+ int32 end_line_number_inclusive_one_indexed = 5;
541
+ int32 end_column_one_indexed = 6;
542
+ }
543
+ string message = 1;
544
+ string kind = 2;
545
+ bool is_preferred = 3;
546
+ repeated Edit edits = 4;
547
+ }
548
+ string message = 1;
549
+ string severity = 2;
550
+ string relative_workspace_path = 3;
551
+ int32 start_line_number_one_indexed = 4;
552
+ int32 start_column_one_indexed = 5;
553
+ int32 end_line_number_inclusive_one_indexed = 6;
554
+ int32 end_column_one_indexed = 7;
555
+ repeated QuickFix quick_fixes = 9;
556
+ }
557
+ repeated Lint lints = 1;
558
+ }
559
+ message UserResponseToSuggestedCodeBlock { // aiserver.v1.UserResponseToSuggestedCodeBlock
560
+ enum UserResponseType { // aiserver.v1.UserResponseToSuggestedCodeBlock.UserResponseType
561
+ USER_RESPONSE_TYPE_UNSPECIFIED = 0;
562
+ USER_RESPONSE_TYPE_ACCEPT = 1;
563
+ USER_RESPONSE_TYPE_REJECT = 2;
564
+ USER_RESPONSE_TYPE_MODIFY = 3;
565
+ }
566
+ UserResponseType user_response_type = 1;
567
+ string file_path = 2;
568
+ optional FileDiff user_modifications_to_suggested_code_blocks = 3;
569
+ }
570
+ message ClientSideToolV2Result { // aiserver.v1.ClientSideToolV2Result
571
+ ClientSideToolV2 tool = 1;
572
+ ReadSemsearchFilesResult read_semsearch_files_result = 2;
573
+ ReadFileForImportsResult read_file_for_imports_result = 3;
574
+ RipgrepSearchResult ripgrep_search_result = 4;
575
+ RunTerminalCommandResult run_terminal_command_result = 5;
576
+ ReadFileResult read_file_result = 6;
577
+ ListDirResult list_dir_result = 9;
578
+ EditFileResult edit_file_result = 10;
579
+ ToolCallFileSearchResult file_search_result = 11;
580
+ SemanticSearchFullResult semantic_search_full_result = 18;
581
+ CreateFileResult create_file_result = 19;
582
+ DeleteFileResult delete_file_result = 20;
583
+ optional ToolResultError error = 8;
584
+ }
585
+ message ReadSemsearchFilesResult { // aiserver.v1.ReadSemsearchFilesResult
586
+ repeated CodeResult code_results = 1;
587
+ }
588
+ message CodeResult { // aiserver.v1.CodeResult
589
+ CodeBlock code_block = 1;
590
+ float score = 2;
591
+ }
592
+ message ReadFileForImportsResult { // aiserver.v1.ReadFileForImportsResult
593
+ string contents = 1;
594
+ }
595
+ message RipgrepSearchResult { // aiserver.v1.RipgrepSearchResult
596
+ RipgrepSearchResultInternal internal = 1;
597
+ }
598
+ message RipgrepSearchResultInternal { // aiserver.v1.RipgrepSearchResultInternal
599
+ message IFileMatch { // aiserver.v1.RipgrepSearchResultInternal.IFileMatch
600
+ message ITextSearchResult { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchResult
601
+ message ITextSearchMatch { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchMatch
602
+ message ISearchRangeSetPairing { // aiserver.v1.RipgrepSearchResultInternal.ISearchRangeSetPairing
603
+ message ISearchRange { // aiserver.v1.RipgrepSearchResultInternal.ISearchRange
604
+ int32 start_line_number = 1;
605
+ int32 start_column = 2;
606
+ int32 end_line_number = 3;
607
+ int32 end_column = 4;
608
+ }
609
+ ISearchRange source = 1;
610
+ ISearchRange preview = 2;
611
+ }
612
+ optional string uri = 1;
613
+ repeated ISearchRangeSetPairing range_locations = 2;
614
+ string preview_text = 3;
615
+ optional int32 webview_index = 4;
616
+ optional string cell_fragment = 5;
617
+ }
618
+ message ITextSearchContext { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchContext
619
+ optional string uri = 1;
620
+ string text = 2;
621
+ int32 line_number = 3;
622
+ }
623
+ ITextSearchMatch match = 1;
624
+ ITextSearchContext context = 2;
625
+ }
626
+ string resource = 1;
627
+ repeated ITextSearchResult results = 2;
628
+ }
629
+ enum SearchCompletionExitCode { // aiserver.v1.RipgrepSearchResultInternal.SearchCompletionExitCode
630
+ SEARCH_COMPLETION_EXIT_CODE_UNSPECIFIED = 0;
631
+ SEARCH_COMPLETION_EXIT_CODE_NORMAL = 1;
632
+ SEARCH_COMPLETION_EXIT_CODE_NEW_SEARCH_STARTED = 2;
633
+ }
634
+ message ITextSearchCompleteMessage { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchCompleteMessage
635
+ enum TextSearchCompleteMessageType { // aiserver.v1.RipgrepSearchResultInternal.TextSearchCompleteMessageType
636
+ TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_UNSPECIFIED = 0;
637
+ TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_INFORMATION = 1;
638
+ TEXT_SEARCH_COMPLETE_MESSAGE_TYPE_WARNING = 2;
639
+ }
640
+ string text = 1;
641
+ TextSearchCompleteMessageType type = 2;
642
+ optional bool trusted = 3;
643
+ }
644
+ message IFileSearchStats { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchStats
645
+ message ISearchEngineStats { // aiserver.v1.RipgrepSearchResultInternal.ISearchEngineStats
646
+ int32 file_walk_time = 1;
647
+ int32 directories_walked = 2;
648
+ int32 files_walked = 3;
649
+ int32 cmd_time = 4;
650
+ optional int32 cmd_result_count = 5;
651
+ }
652
+ message ICachedSearchStats { // aiserver.v1.RipgrepSearchResultInternal.ICachedSearchStats
653
+ bool cache_was_resolved = 1;
654
+ int32 cache_lookup_time = 2;
655
+ int32 cache_filter_time = 3;
656
+ int32 cache_entry_count = 4;
657
+ }
658
+ message IFileSearchProviderStats { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchProviderStats
659
+ int32 provider_time = 1;
660
+ int32 post_process_time = 2;
661
+ }
662
+ enum FileSearchProviderType { // aiserver.v1.RipgrepSearchResultInternal.IFileSearchStats.FileSearchProviderType
663
+ FILE_SEARCH_PROVIDER_TYPE_UNSPECIFIED = 0;
664
+ FILE_SEARCH_PROVIDER_TYPE_FILE_SEARCH_PROVIDER = 1;
665
+ FILE_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS = 2;
666
+ }
667
+ bool from_cache = 1;
668
+ ISearchEngineStats search_engine_stats = 2;
669
+ ICachedSearchStats cached_search_stats = 3;
670
+ IFileSearchProviderStats file_search_provider_stats = 4;
671
+ int32 result_count = 5;
672
+ FileSearchProviderType type = 6;
673
+ optional int32 sorting_time = 7;
674
+ }
675
+ message ITextSearchStats { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchStats
676
+ enum TextSearchProviderType { // aiserver.v1.RipgrepSearchResultInternal.ITextSearchStats.TextSearchProviderType
677
+ TEXT_SEARCH_PROVIDER_TYPE_UNSPECIFIED = 0;
678
+ TEXT_SEARCH_PROVIDER_TYPE_TEXT_SEARCH_PROVIDER = 1;
679
+ TEXT_SEARCH_PROVIDER_TYPE_SEARCH_PROCESS = 2;
680
+ TEXT_SEARCH_PROVIDER_TYPE_AI_TEXT_SEARCH_PROVIDER = 3;
681
+ }
682
+ TextSearchProviderType type = 1;
683
+ }
684
+ repeated IFileMatch results = 1;
685
+ optional SearchCompletionExitCode exit = 2;
686
+ optional bool limit_hit = 3;
687
+ repeated ITextSearchCompleteMessage messages = 4;
688
+ IFileSearchStats file_search_stats = 5;
689
+ ITextSearchStats text_search_stats = 6;
690
+ }
691
+ message RunTerminalCommandResult { // aiserver.v1.RunTerminalCommandResult
692
+ string output = 1;
693
+ int32 exit_code = 2;
694
+ optional bool rejected = 3;
695
+ bool popped_out_into_background = 4;
696
+ }
697
+ message ReadFileResult { // aiserver.v1.ReadFileResult
698
+ string contents = 1;
699
+ bool did_downgrade_to_line_range = 2;
700
+ bool did_shorten_line_range = 3;
701
+ bool did_set_default_line_range = 4;
702
+ optional string full_file_contents = 5;
703
+ optional string outline = 6;
704
+ optional int32 start_line_one_indexed = 7;
705
+ optional int32 end_line_one_indexed_inclusive = 8;
706
+ string relative_workspace_path = 9;
707
+ bool did_shorten_char_range = 10;
708
+ }
709
+ message ListDirResult { // aiserver.v1.ListDirResult
710
+ message File { // aiserver.v1.ListDirResult.File
711
+ message Timestamp { // google.protobuf.Timestamp
712
+ int64 seconds = 1;
713
+ int32 nanos = 2;
714
+ }
715
+ string name = 1;
716
+ bool is_directory = 2;
717
+ optional int64 size = 3;
718
+ optional Timestamp last_modified = 4;
719
+ optional int32 num_children = 5;
720
+ optional int32 num_lines = 6;
721
+ }
722
+ repeated File files = 1;
723
+ string directory_relative_workspace_path = 2;
724
+ }
725
+ message EditFileResult { // aiserver.v1.EditFileResult
726
+ message FileDiff { // aiserver.v1.EditFileResult.FileDiff
727
+ message ChunkDiff { // aiserver.v1.EditFileResult.FileDiff.ChunkDiff
728
+ string diff_string = 1;
729
+ int32 old_start = 2;
730
+ int32 new_start = 3;
731
+ int32 old_lines = 4;
732
+ int32 new_lines = 5;
733
+ int32 lines_removed = 6;
734
+ int32 lines_added = 7;
735
+ }
736
+ enum Editor { // aiserver.v1.EditFileResult.FileDiff.Editor
737
+ EDITOR_UNSPECIFIED = 0;
738
+ EDITOR_AI = 1;
739
+ EDITOR_HUMAN = 2;
740
+ }
741
+ repeated ChunkDiff chunks = 1;
742
+ Editor editor = 2;
743
+ bool hit_timeout = 3;
744
+ }
745
+ FileDiff diff = 1;
746
+ bool is_applied = 2;
747
+ bool apply_failed = 3;
748
+ }
749
+ message ToolCallFileSearchResult { // aiserver.v1.ToolCallFileSearchResult
750
+ message File { // aiserver.v1.ToolCallFileSearchResult.File
751
+ string uri = 1;
752
+ }
753
+ repeated File files = 1;
754
+ optional bool limit_hit = 2;
755
+ int32 num_results = 3;
756
+ }
757
+ message SemanticSearchFullResult { // aiserver.v1.SemanticSearchFullResult
758
+ repeated CodeResult code_results = 1;
759
+ }
760
+ message CreateFileResult { // aiserver.v1.CreateFileResult
761
+ bool file_created_successfully = 1;
762
+ bool file_already_exists = 2;
763
+ }
764
+ message DeleteFileResult { // aiserver.v1.DeleteFileResult
765
+ bool rejected = 1;
766
+ bool file_non_existent = 2;
767
+ bool file_deleted_successfully = 3;
768
+ }
769
+ message ToolResultError { // aiserver.v1.ToolResultError
770
+ string client_visible_error_message = 1;
771
+ string model_visible_error_message = 2;
772
+ }
773
+ message ComposerCapabilityRequest { // aiserver.v1.ComposerCapabilityRequest
774
+ enum ComposerCapabilityType { // aiserver.v1.ComposerCapabilityRequest.ComposerCapabilityType
775
+ COMPOSER_CAPABILITY_TYPE_UNSPECIFIED = 0;
776
+ COMPOSER_CAPABILITY_TYPE_LOOP_ON_LINTS = 1;
777
+ COMPOSER_CAPABILITY_TYPE_LOOP_ON_TESTS = 2;
778
+ COMPOSER_CAPABILITY_TYPE_MEGA_PLANNER = 3;
779
+ COMPOSER_CAPABILITY_TYPE_LOOP_ON_COMMAND = 4;
780
+ COMPOSER_CAPABILITY_TYPE_TOOL_CALL = 5;
781
+ COMPOSER_CAPABILITY_TYPE_DIFF_REVIEW = 6;
782
+ COMPOSER_CAPABILITY_TYPE_CONTEXT_PICKING = 7;
783
+ COMPOSER_CAPABILITY_TYPE_EDIT_TRAIL = 8;
784
+ COMPOSER_CAPABILITY_TYPE_AUTO_CONTEXT = 9;
785
+ COMPOSER_CAPABILITY_TYPE_CONTEXT_PLANNER = 10;
786
+ COMPOSER_CAPABILITY_TYPE_DIFF_HISTORY = 11;
787
+ COMPOSER_CAPABILITY_TYPE_REMEMBER_THIS = 12;
788
+ COMPOSER_CAPABILITY_TYPE_DECOMPOSER = 13;
789
+ COMPOSER_CAPABILITY_TYPE_USES_CODEBASE = 14;
790
+ COMPOSER_CAPABILITY_TYPE_TOOL_FORMER = 15;
791
+ }
792
+ message LoopOnLintsCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnLintsCapability
793
+ repeated LinterErrors linter_errors = 1;
794
+ optional string custom_instructions = 2;
795
+ }
796
+ message LoopOnTestsCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnTestsCapability
797
+ repeated string test_names = 1;
798
+ optional string custom_instructions = 2;
799
+ }
800
+ message MegaPlannerCapability { // aiserver.v1.ComposerCapabilityRequest.MegaPlannerCapability
801
+ optional string custom_instructions = 1;
802
+ }
803
+ message LoopOnCommandCapability { // aiserver.v1.ComposerCapabilityRequest.LoopOnCommandCapability
804
+ string command = 1;
805
+ optional string custom_instructions = 2;
806
+ optional string output = 3;
807
+ optional int32 exit_code = 4;
808
+ }
809
+ message ToolCallCapability { // aiserver.v1.ComposerCapabilityRequest.ToolCallCapability
810
+ message ToolSchema { // aiserver.v1.ComposerCapabilityRequest.ToolSchema
811
+ enum ToolType { // aiserver.v1.ComposerCapabilityRequest.ToolType
812
+ TOOL_TYPE_UNSPECIFIED = 0;
813
+ TOOL_TYPE_ADD_FILE_TO_CONTEXT = 1;
814
+ TOOL_TYPE_RUN_TERMINAL_COMMAND = 2;
815
+ TOOL_TYPE_ITERATE = 3;
816
+ TOOL_TYPE_REMOVE_FILE_FROM_CONTEXT = 4;
817
+ TOOL_TYPE_SEMANTIC_SEARCH_CODEBASE = 5;
818
+ }
819
+ ToolType type = 1;
820
+ string name = 2;
821
+ repeated string required = 4;
822
+ }
823
+ optional string custom_instructions = 1;
824
+ repeated ToolSchema tool_schemas = 2;
825
+ repeated string relevant_files = 3;
826
+ repeated string files_in_context = 4;
827
+ repeated string semantic_search_files = 5;
828
+ }
829
+ message DiffReviewCapability { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability
830
+ message SimpleFileDiff { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability.SimpleFileDiff
831
+ message Chunk { // aiserver.v1.ComposerCapabilityRequest.DiffReviewCapability.SimpleFileDiff.Chunk
832
+ repeated string old_lines = 1;
833
+ repeated string new_lines = 2;
834
+ LineRange old_range = 3;
835
+ LineRange new_range = 4;
836
+ }
837
+ string relative_workspace_path = 1;
838
+ repeated Chunk chunks = 3;
839
+ }
840
+ optional string custom_instructions = 1;
841
+ repeated SimpleFileDiff diffs = 2;
842
+ }
843
+ message ContextPickingCapability { // aiserver.v1.ComposerCapabilityRequest.ContextPickingCapability
844
+ optional string custom_instructions = 1;
845
+ repeated string potential_context_files = 2;
846
+ repeated CodeChunk potential_context_code_chunks = 3;
847
+ repeated string files_in_context = 4;
848
+ }
849
+ message EditTrailCapability { // aiserver.v1.ComposerCapabilityRequest.EditTrailCapability
850
+ optional string custom_instructions = 1;
851
+ }
852
+ message AutoContextCapability { // aiserver.v1.ComposerCapabilityRequest.AutoContextCapability
853
+ optional string custom_instructions = 1;
854
+ repeated string additional_files = 2;
855
+ }
856
+ message ContextPlannerCapability { // aiserver.v1.ComposerCapabilityRequest.ContextPlannerCapability
857
+ optional string custom_instructions = 1;
858
+ repeated CodeChunk attached_code_chunks = 2;
859
+ }
860
+ message RememberThisCapability { // aiserver.v1.ComposerCapabilityRequest.RememberThisCapability
861
+ optional string custom_instructions = 1;
862
+ string memory = 2;
863
+ }
864
+ message DecomposerCapability { // aiserver.v1.ComposerCapabilityRequest.DecomposerCapability
865
+ optional string custom_instructions = 1;
866
+ }
867
+ ComposerCapabilityType type = 1;
868
+ LoopOnLintsCapability loop_on_lints = 2;
869
+ LoopOnTestsCapability loop_on_tests = 3;
870
+ MegaPlannerCapability mega_planner = 4;
871
+ LoopOnCommandCapability loop_on_command = 5;
872
+ ToolCallCapability tool_call = 6;
873
+ DiffReviewCapability diff_review = 7;
874
+ ContextPickingCapability context_picking = 8;
875
+ EditTrailCapability edit_trail = 9;
876
+ AutoContextCapability auto_context = 10;
877
+ ContextPlannerCapability context_planner = 11;
878
+ RememberThisCapability remember_this = 12;
879
+ DecomposerCapability decomposer = 13;
880
+ }
881
+ message LinterErrors { // aiserver.v1.LinterErrors
882
+ string relative_workspace_path = 1;
883
+ repeated LinterError errors = 2;
884
+ string file_contents = 3;
885
+ }
886
+ message LinterError { // aiserver.v1.LinterError
887
+ message RelatedInformation { // aiserver.v1.Diagnostic.RelatedInformation
888
+ string message = 1;
889
+ CursorRange range = 2;
890
+ }
891
+ enum DiagnosticSeverity { // aiserver.v1.Diagnostic.DiagnosticSeverity
892
+ DIAGNOSTIC_SEVERITY_UNSPECIFIED = 0;
893
+ DIAGNOSTIC_SEVERITY_ERROR = 1;
894
+ DIAGNOSTIC_SEVERITY_WARNING = 2;
895
+ DIAGNOSTIC_SEVERITY_INFORMATION = 3;
896
+ DIAGNOSTIC_SEVERITY_HINT = 4;
897
+ }
898
+ string message = 1;
899
+ CursorRange range = 2;
900
+ optional string source = 3;
901
+ repeated RelatedInformation related_information = 4;
902
+ optional DiagnosticSeverity severity = 5;
903
+ }
904
+ message CodeChunk { // aiserver.v1.CodeChunk
905
+ enum SummarizationStrategy { // aiserver.v1.CodeChunk.SummarizationStrategy
906
+ SUMMARIZATION_STRATEGY_NONE_UNSPECIFIED = 0;
907
+ SUMMARIZATION_STRATEGY_SUMMARIZED = 1;
908
+ SUMMARIZATION_STRATEGY_EMBEDDED = 2;
909
+ }
910
+ enum Intent { // aiserver.v1.CodeChunk.Intent
911
+ INTENT_UNSPECIFIED = 0;
912
+ INTENT_COMPOSER_FILE = 1;
913
+ INTENT_COMPRESSED_COMPOSER_FILE = 2;
914
+ }
915
+ string relative_workspace_path = 1;
916
+ int32 start_line_number = 2;
917
+ repeated string lines = 3;
918
+ optional SummarizationStrategy summarization_strategy = 4;
919
+ string language_identifier = 5;
920
+ optional Intent intent = 6;
921
+ optional bool is_final_version = 7;
922
+ optional bool is_first_version = 8;
923
+ }
924
+ message SuggestedCodeBlock { // aiserver.v1.SuggestedCodeBlock
925
+ string relative_workspace_path = 1;
926
+ }
927
+ message RedDiff { // aiserver.v1.RedDiff
928
+ string relative_workspace_path = 1;
929
+ repeated SimplestRange red_ranges = 2;
930
+ repeated SimplestRange red_ranges_reversed = 3;
931
+ string start_hash = 4;
932
+ string end_hash = 5;
933
+ }
934
+ message LinterErrorsWithoutFileContents { // aiserver.v1.LinterErrorsWithoutFileContents
935
+ string relative_workspace_path = 1;
936
+ repeated LinterError errors = 2;
937
+ }
938
+ message DiffHistoryData { // aiserver.v1.DiffHistoryData
939
+ string relative_workspace_path = 1;
940
+ repeated ComposerFileDiff diffs = 2;
941
+ double timestamp = 3;
942
+ string unique_id = 4;
943
+ ComposerFileDiff start_to_end_diff = 5;
944
+ }
945
+ message ComposerFileDiff { // aiserver.v1.ComposerFileDiff
946
+ message ChunkDiff { // aiserver.v1.ComposerFileDiff.ChunkDiff
947
+ string diff_string = 1;
948
+ int32 old_start = 2;
949
+ int32 new_start = 3;
950
+ int32 old_lines = 4;
951
+ int32 new_lines = 5;
952
+ int32 lines_removed = 6;
953
+ int32 lines_added = 7;
954
+ }
955
+ enum Editor { // aiserver.v1.ComposerFileDiff.Editor
956
+ EDITOR_UNSPECIFIED = 0;
957
+ EDITOR_AI = 1;
958
+ EDITOR_HUMAN = 2;
959
+ }
960
+ repeated ChunkDiff chunks = 1;
961
+ Editor editor = 2;
962
+ bool hit_timeout = 3;
963
+ }
964
+ message ComposerFileDiffHistory { // aiserver.v1.ComposerFileDiffHistory
965
+ string file_name = 1;
966
+ repeated string diff_history = 2;
967
+ repeated double diff_history_timestamps = 3;
968
+ }
969
+ message ConversationSummary { // aiserver.v1.ConversationSummary
970
+ string summary = 1;
971
+ string truncation_last_bubble_id_inclusive = 2;
972
+ string client_should_start_sending_from_inclusive_bubble_id = 3;
973
+ }
974
+ message RepositoryInfo { // aiserver.v1.RepositoryInfo
975
+ string relative_workspace_path = 1;
976
+ repeated string remote_urls = 2;
977
+ repeated string remote_names = 3;
978
+ string repo_name = 4;
979
+ string repo_owner = 5;
980
+ bool is_tracked = 6;
981
+ bool is_local = 7;
982
+ optional int32 num_files = 8;
983
+ optional double orthogonal_transform_seed = 9;
984
+ optional EmbeddingModel preferred_embedding_model = 10;
985
+ }
986
+ message ExplicitContext { // aiserver.v1.ExplicitContext
987
+ string context = 1;
988
+ optional string repo_context = 2;
989
+ }
990
+ message ModelDetails { // aiserver.v1.ModelDetails
991
+ optional string model_name = 1;
992
+ optional string api_key = 2;
993
+ optional bool enable_ghost_mode = 3;
994
+ optional AzureState azure_state = 4;
995
+ optional bool enable_slow_pool = 5;
996
+ optional string openai_api_base_url = 6;
997
+ }
998
+ message AzureState { // aiserver.v1.AzureState
999
+ string api_key = 1;
1000
+ string base_url = 2;
1001
+ string deployment = 3;
1002
+ bool use_azure = 4;
1003
+ }
1004
+ message ChatQuote { // aiserver.v1.ChatQuote
1005
+ string markdown = 1;
1006
+ string bubble_id = 2;
1007
+ int32 section_index = 3;
1008
+ }
1009
+ message DebugInfo { // aiserver.v1.DebugInfo
1010
+ message Breakpoint { // aiserver.v1.DebugInfo.Breakpoint
1011
+ string relative_workspace_path = 1;
1012
+ int32 line_number = 2;
1013
+ repeated string lines_before_breakpoint = 3;
1014
+ repeated string lines_after_breakpoint = 4;
1015
+ optional string exception_info = 5;
1016
+ }
1017
+ message CallStackFrame { // aiserver.v1.DebugInfo.CallStackFrame
1018
+ message Scope { // aiserver.v1.DebugInfo.Scope
1019
+ message Variable { // aiserver.v1.DebugInfo.Variable
1020
+ string name = 1;
1021
+ string value = 2;
1022
+ optional string type = 3;
1023
+ }
1024
+ string name = 1;
1025
+ repeated Variable variables = 2;
1026
+ }
1027
+ string relative_workspace_path = 1;
1028
+ int32 line_number = 2;
1029
+ string function_name = 3;
1030
+ repeated Scope scopes = 4;
1031
+ }
1032
+ Breakpoint breakpoint = 1;
1033
+ repeated CallStackFrame call_stack = 2;
1034
+ repeated CodeBlock history = 3;
1035
+ }
1036
+ message ChatExternalLink { // aiserver.v1.ChatExternalLink
1037
+ string url = 1;
1038
+ string uuid = 2;
1039
+ }
1040
+ message CommitNote { // aiserver.v1.CommitNote
1041
+ string note = 1;
1042
+ string commit_hash = 2;
1043
+ }
1044
+ message ContextAST { // aiserver.v1.ContextAST
1045
+ repeated ContainerTree files = 1;
1046
+ }
1047
+ message ContainerTree { // aiserver.v1.ContainerTree
1048
+ string relative_workspace_path = 1;
1049
+ repeated ContainerTreeNode nodes = 2;
1050
+ }
1051
+ message ContainerTreeNode { // aiserver.v1.ContainerTreeNode
1052
+ message Container { // aiserver.v1.ContainerTreeNode.Container
1053
+ message Reference { // aiserver.v1.ContainerTreeNode.Reference
1054
+ string value = 1;
1055
+ string relative_workspace_path = 2;
1056
+ }
1057
+ string doc_string = 1;
1058
+ string header = 2;
1059
+ string trailer = 3;
1060
+ repeated ContainerTreeNode children = 5;
1061
+ repeated Reference references = 6;
1062
+ double score = 7;
1063
+ }
1064
+ message Blob { // aiserver.v1.ContainerTreeNode.Blob
1065
+ optional string value = 1;
1066
+ }
1067
+ message Symbol { // aiserver.v1.ContainerTreeNode.Symbol
1068
+ message Reference { // aiserver.v1.ContainerTreeNode.Reference
1069
+ string value = 1;
1070
+ string relative_workspace_path = 2;
1071
+ }
1072
+ string doc_string = 1;
1073
+ string value = 2;
1074
+ repeated Reference references = 6;
1075
+ double score = 7;
1076
+ }
1077
+ Container container = 1;
1078
+ Blob blob = 2;
1079
+ Symbol symbol = 3;
1080
+ }
1081
+ message StreamChatResponse { // aiserver.v1.StreamChatResponse
1082
+ message ChunkIdentity { // aiserver.v1.StreamChatResponse.ChunkIdentity
1083
+ string file_name = 1;
1084
+ int32 start_line = 2;
1085
+ int32 end_line = 3;
1086
+ string text = 4;
1087
+ ChunkType chunk_type = 5;
1088
+ }
1089
+ string text = 1;
1090
+ optional string server_bubble_id = 22;
1091
+ optional string debugging_only_chat_prompt = 2;
1092
+ optional int32 debugging_only_token_count = 3;
1093
+ DocumentationCitation document_citation = 4;
1094
+ optional string filled_prompt = 5;
1095
+ optional bool is_big_file = 6;
1096
+ optional string intermediate_text = 7;
1097
+ optional bool is_using_slow_request = 10;
1098
+ optional ChunkIdentity chunk_identity = 8;
1099
+ optional DocsReference docs_reference = 9;
1100
+ optional WebCitation web_citation = 11;
1101
+ optional StatusUpdates status_updates = 12;
1102
+ optional ServerTimingInfo timing_info = 13;
1103
+ optional SymbolLink symbol_link = 14;
1104
+ optional FileLink file_link = 15;
1105
+ optional ConversationSummary conversation_summary = 16;
1106
+ optional ServiceStatusUpdate service_status_update = 17;
1107
+ }
1108
+ message DocumentationCitation { // aiserver.v1.DocumentationCitation
1109
+ repeated DocumentationChunk chunks = 1;
1110
+ }
1111
+ message DocumentationChunk { // aiserver.v1.DocumentationChunk
1112
+ string doc_name = 1;
1113
+ string page_url = 2;
1114
+ string documentation_chunk = 3;
1115
+ float score = 4;
1116
+ string page_title = 5;
1117
+ }
1118
+ message DocsReference { // aiserver.v1.DocsReference
1119
+ string title = 1;
1120
+ string url = 2;
1121
+ }
1122
+ message WebCitation { // aiserver.v1.WebCitation
1123
+ repeated WebReference references = 1;
1124
+ }
1125
+ message WebReference { // aiserver.v1.WebReference
1126
+ string title = 2;
1127
+ string url = 1;
1128
+ }
1129
+ message StatusUpdates { // aiserver.v1.StatusUpdates
1130
+ repeated StatusUpdate updates = 1;
1131
+ }
1132
+ message StatusUpdate { // aiserver.v1.StatusUpdate
1133
+ string message = 1;
1134
+ optional string metadata = 2;
1135
+ }
1136
+ message ServerTimingInfo { // aiserver.v1.ServerTimingInfo
1137
+ double server_start_time = 1;
1138
+ double server_first_token_time = 2;
1139
+ double server_request_sent_time = 3;
1140
+ double server_end_time = 4;
1141
+ }
1142
+ message SymbolLink { // aiserver.v1.SymbolLink
1143
+ string symbol_name = 1;
1144
+ string symbol_search_string = 2;
1145
+ string relative_workspace_path = 3;
1146
+ int32 rough_line_number = 4;
1147
+ }
1148
+ message FileLink { // aiserver.v1.FileLink
1149
+ string display_name = 1;
1150
+ string relative_workspace_path = 2;
1151
+ }
1152
+ message ServiceStatusUpdate { // aiserver.v1.ServiceStatusUpdate
1153
+ string message = 1;
1154
+ string codicon = 2;
1155
+ optional bool allow_command_links_potentially_unsafe_please_only_use_for_handwritten_trusted_markdown = 3;
1156
+ }
src/chat/config.rs ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use crate::AppConfig;
2
+
3
+ include!(concat!(env!("OUT_DIR"), "/key.rs"));
4
+
5
+ impl KeyConfig {
6
+ pub fn new_with_global() -> Self {
7
+ Self {
8
+ auth_token: None,
9
+ disable_vision: Some(AppConfig::get_vision_ability().is_none()),
10
+ enable_slow_pool: Some(AppConfig::get_slow_pool()),
11
+ usage_check_models: None,
12
+ include_web_references: Some(AppConfig::get_web_refs()),
13
+ }
14
+ }
15
+
16
+ pub fn copy_without_auth_token(&self, config: &mut Self) {
17
+ if self.disable_vision.is_some() {
18
+ config.disable_vision = self.disable_vision;
19
+ }
20
+ if self.enable_slow_pool.is_some() {
21
+ config.enable_slow_pool = self.enable_slow_pool;
22
+ }
23
+ if self.usage_check_models.is_some() {
24
+ config.usage_check_models = self.usage_check_models.clone();
25
+ }
26
+ if self.include_web_references.is_some() {
27
+ config.include_web_references = self.include_web_references;
28
+ }
29
+ }
30
+ }
src/chat/config/key.proto ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ syntax = "proto3";
2
+
3
+ package key;
4
+
5
+ // 动态配置的 API KEY
6
+ message KeyConfig {
7
+ // 认证令牌信息
8
+ message TokenInfo {
9
+ string sub = 1; // 用户标识符
10
+ int64 exp = 2; // 过期时间(Unix 时间戳)
11
+ string randomness = 3; // 随机字符串
12
+ string signature = 4; // 签名
13
+ bytes machine_id = 5; // 机器ID的SHA256哈希值
14
+ bytes mac_id = 6; // MAC地址的SHA256哈希值
15
+ }
16
+
17
+ // 认证令牌(必需)
18
+ TokenInfo auth_token = 1;
19
+
20
+ // 是否禁用图片处理能力
21
+ optional bool disable_vision = 4;
22
+
23
+ // 是否启用慢速池
24
+ optional bool enable_slow_pool = 5;
25
+
26
+ // 使用量检查模型规则
27
+ message UsageCheckModel {
28
+ // 检查类型
29
+ enum Type {
30
+ TYPE_DEFAULT = 0; // 未指定
31
+ TYPE_DISABLED = 1; // 禁用
32
+ TYPE_ALL = 2; // 全部
33
+ TYPE_CUSTOM = 3; // 自定义列表
34
+ }
35
+ Type type = 1; // 检查类型
36
+ repeated string model_ids = 2; // 模型 ID 列表,当 type 为 TYPE_CUSTOM 时生效
37
+ }
38
+ // 使用量检查模型规则
39
+ optional UsageCheckModel usage_check_models = 6;
40
+
41
+ // 包含网络引用
42
+ optional bool include_web_references = 7;
43
+
44
+ // 密码SHA256哈希值
45
+ // bytes secret = 2;
46
+ }
src/chat/constant.rs ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use super::model::Model;
2
+
3
+ macro_rules! def_pub_const {
4
+ ($name:ident, $value:expr) => {
5
+ pub const $name: &'static str = $value;
6
+ };
7
+ }
8
+ def_pub_const!(ERR_UNSUPPORTED_GIF, "不支持动态 GIF");
9
+ def_pub_const!(
10
+ ERR_UNSUPPORTED_IMAGE_FORMAT,
11
+ "不支持的图片格式,仅支持 PNG、JPEG、WEBP 和非动态 GIF"
12
+ );
13
+ def_pub_const!(ERR_NODATA, "No data");
14
+
15
+ const MODEL_OBJECT: &str = "model";
16
+ const CREATED: &i64 = &1706659200;
17
+
18
+ def_pub_const!(ANTHROPIC, "anthropic");
19
+ def_pub_const!(CURSOR, "cursor");
20
+ def_pub_const!(GOOGLE, "google");
21
+ def_pub_const!(OPENAI, "openai");
22
+ def_pub_const!(DEEPSEEK, "deepseek");
23
+
24
+ def_pub_const!(CLAUDE_3_5_SONNET, "claude-3.5-sonnet");
25
+ def_pub_const!(GPT_4, "gpt-4");
26
+ def_pub_const!(GPT_4O, "gpt-4o");
27
+ def_pub_const!(CLAUDE_3_OPUS, "claude-3-opus");
28
+ def_pub_const!(CURSOR_FAST, "cursor-fast");
29
+ def_pub_const!(CURSOR_SMALL, "cursor-small");
30
+ def_pub_const!(GPT_3_5_TURBO, "gpt-3.5-turbo");
31
+ def_pub_const!(GPT_4_TURBO_2024_04_09, "gpt-4-turbo-2024-04-09");
32
+ def_pub_const!(GPT_4O_128K, "gpt-4o-128k");
33
+ def_pub_const!(GEMINI_1_5_FLASH_500K, "gemini-1.5-flash-500k");
34
+ def_pub_const!(CLAUDE_3_HAIKU_200K, "claude-3-haiku-200k");
35
+ def_pub_const!(CLAUDE_3_5_SONNET_200K, "claude-3-5-sonnet-200k");
36
+ def_pub_const!(CLAUDE_3_5_SONNET_20241022, "claude-3-5-sonnet-20241022");
37
+ def_pub_const!(GPT_4O_MINI, "gpt-4o-mini");
38
+ def_pub_const!(O1_MINI, "o1-mini");
39
+ def_pub_const!(O1_PREVIEW, "o1-preview");
40
+ def_pub_const!(O1, "o1");
41
+ def_pub_const!(CLAUDE_3_5_HAIKU, "claude-3.5-haiku");
42
+ def_pub_const!(GEMINI_EXP_1206, "gemini-exp-1206");
43
+ def_pub_const!(
44
+ GEMINI_2_0_FLASH_THINKING_EXP,
45
+ "gemini-2.0-flash-thinking-exp"
46
+ );
47
+ def_pub_const!(GEMINI_2_0_FLASH_EXP, "gemini-2.0-flash-exp");
48
+ def_pub_const!(DEEPSEEK_V3, "deepseek-v3");
49
+ def_pub_const!(DEEPSEEK_R1, "deepseek-r1");
50
+
51
+ // #[derive(Clone, PartialEq, rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)]
52
+ // pub enum ModelType {
53
+ // Claude35Sonnet,
54
+ // Gpt4,
55
+ // Gpt4o,
56
+ // Claude3Opus,
57
+ // CursorFast,
58
+ // CursorSmall,
59
+ // Gpt35Turbo,
60
+ // Gpt4Turbo202404,
61
+ // Gpt4o128k,
62
+ // Gemini15Flash500k,
63
+ // Claude3Haiku200k,
64
+ // Claude35Sonnet200k,
65
+ // Claude35Sonnet20241022,
66
+ // Gpt4oMini,
67
+ // O1Mini,
68
+ // O1Preview,
69
+ // O1,
70
+ // Claude35Haiku,
71
+ // GeminiExp1206,
72
+ // Gemini20FlashThinkingExp,
73
+ // Gemini20FlashExp,
74
+ // DeepseekV3,
75
+ // DeepseekR1,
76
+ // }
77
+
78
+ macro_rules! create_model {
79
+ ($($id:expr, $owner:expr),* $(,)?) => {
80
+ pub const AVAILABLE_MODELS: [Model; count!($( ($id, $owner) )*)] = [
81
+ $(
82
+ Model {
83
+ id: $id,
84
+ created: CREATED,
85
+ object: MODEL_OBJECT,
86
+ owned_by: $owner,
87
+ },
88
+ )*
89
+ ];
90
+ };
91
+ }
92
+
93
+ macro_rules! count {
94
+ () => (0);
95
+ (($id:expr, $owner:expr) $( ($id2:expr, $owner2:expr) )*) => (1 + count!($( ($id2, $owner2) )*));
96
+ }
97
+
98
+ // impl ModelType {
99
+ // pub fn as_str_name(&self) -> &'static str {
100
+ // match self {
101
+ // ModelType::Claude35Sonnet => CLAUDE_3_5_SONNET,
102
+ // ModelType::Gpt4 => GPT_4,
103
+ // ModelType::Gpt4o => GPT_4O,
104
+ // ModelType::Claude3Opus => CLAUDE_3_OPUS,
105
+ // ModelType::CursorFast => CURSOR_FAST,
106
+ // ModelType::CursorSmall => CURSOR_SMALL,
107
+ // ModelType::Gpt35Turbo => GPT_3_5_TURBO,
108
+ // ModelType::Gpt4Turbo202404 => GPT_4_TURBO_2024_04_09,
109
+ // ModelType::Gpt4o128k => GPT_4O_128K,
110
+ // ModelType::Gemini15Flash500k => GEMINI_1_5_FLASH_500K,
111
+ // ModelType::Claude3Haiku200k => CLAUDE_3_HAIKU_200K,
112
+ // ModelType::Claude35Sonnet200k => CLAUDE_3_5_SONNET_200K,
113
+ // ModelType::Claude35Sonnet20241022 => CLAUDE_3_5_SONNET_20241022,
114
+ // ModelType::Gpt4oMini => GPT_4O_MINI,
115
+ // ModelType::O1Mini => O1_MINI,
116
+ // ModelType::O1Preview => O1_PREVIEW,
117
+ // ModelType::O1 => O1,
118
+ // ModelType::Claude35Haiku => CLAUDE_3_5_HAIKU,
119
+ // ModelType::GeminiExp1206 => GEMINI_EXP_1206,
120
+ // ModelType::Gemini20FlashThinkingExp => GEMINI_2_0_FLASH_THINKING_EXP,
121
+ // ModelType::Gemini20FlashExp => GEMINI_2_0_FLASH_EXP,
122
+ // ModelType::DeepseekV3 => DEEPSEEK_V3,
123
+ // ModelType::DeepseekR1 => DEEPSEEK_R1,
124
+ // }
125
+ // }
126
+
127
+ // pub fn from_str_name(id :&str) -> Option<ModelType> {
128
+ // match id {
129
+ // CLAUDE_3_5_SONNET => Some(ModelType::Claude35Sonnet),
130
+ // GPT_4 => Some(ModelType::Gpt4),
131
+ // GPT_4O => Some(ModelType::Gpt4o),
132
+ // CLAUDE_3_OPUS => Some(ModelType::Claude3Opus),
133
+ // CURSOR_FAST => Some(ModelType::CursorFast),
134
+ // CURSOR_SMALL => Some(ModelType::CursorSmall),
135
+ // GPT_3_5_TURBO => Some(ModelType::Gpt35Turbo),
136
+ // GPT_4_TURBO_2024_04_09 => Some(ModelType::Gpt4Turbo202404),
137
+ // GPT_4O_128K => Some(ModelType::Gpt4o128k),
138
+ // GEMINI_1_5_FLASH_500K => Some(ModelType::Gemini15Flash500k),
139
+ // CLAUDE_3_HAIKU_200K => Some(ModelType::Claude3Haiku200k),
140
+ // CLAUDE_3_5_SONNET_200K => Some(ModelType::Claude35Sonnet200k),
141
+ // CLAUDE_3_5_SONNET_20241022 => Some(ModelType::Claude35Sonnet20241022),
142
+ // GPT_4O_MINI => Some(ModelType::Gpt4oMini),
143
+ // O1_MINI => Some(ModelType::O1Mini),
144
+ // O1_PREVIEW => Some(ModelType::O1Preview),
145
+ // O1 => Some(ModelType::O1),
146
+ // CLAUDE_3_5_HAIKU => Some(ModelType::Claude35Haiku),
147
+ // GEMINI_EXP_1206 => Some(ModelType::GeminiExp1206),
148
+ // GEMINI_2_0_FLASH_THINKING_EXP => Some(ModelType::Gemini20FlashThinkingExp),
149
+ // GEMINI_2_0_FLASH_EXP => Some(ModelType::Gemini20FlashExp),
150
+ // DEEPSEEK_V3 => Some(ModelType::DeepseekV3),
151
+ // DEEPSEEK_R1 => Some(ModelType::DeepseekR1),
152
+ // _ => None,
153
+ // }
154
+ // }
155
+ // }
156
+
157
+ create_model!(
158
+ CLAUDE_3_5_SONNET, ANTHROPIC,
159
+ GPT_4, OPENAI,
160
+ GPT_4O, OPENAI,
161
+ CLAUDE_3_OPUS, ANTHROPIC,
162
+ CURSOR_FAST, CURSOR,
163
+ CURSOR_SMALL, CURSOR,
164
+ GPT_3_5_TURBO, OPENAI,
165
+ GPT_4_TURBO_2024_04_09, OPENAI,
166
+ GPT_4O_128K, OPENAI,
167
+ GEMINI_1_5_FLASH_500K, GOOGLE,
168
+ CLAUDE_3_HAIKU_200K, ANTHROPIC,
169
+ CLAUDE_3_5_SONNET_200K, ANTHROPIC,
170
+ CLAUDE_3_5_SONNET_20241022, ANTHROPIC,
171
+ GPT_4O_MINI, OPENAI,
172
+ O1_MINI, OPENAI,
173
+ O1_PREVIEW, OPENAI,
174
+ O1, OPENAI,
175
+ CLAUDE_3_5_HAIKU, ANTHROPIC,
176
+ GEMINI_EXP_1206, GOOGLE,
177
+ GEMINI_2_0_FLASH_THINKING_EXP, GOOGLE,
178
+ GEMINI_2_0_FLASH_EXP, GOOGLE,
179
+ DEEPSEEK_V3, DEEPSEEK,
180
+ DEEPSEEK_R1, DEEPSEEK,
181
+ );
182
+
183
+ pub const USAGE_CHECK_MODELS: [&str; 11] = [
184
+ CLAUDE_3_5_SONNET_20241022,
185
+ CLAUDE_3_5_SONNET,
186
+ GEMINI_EXP_1206,
187
+ GPT_4,
188
+ GPT_4_TURBO_2024_04_09,
189
+ GPT_4O,
190
+ CLAUDE_3_5_HAIKU,
191
+ GPT_4O_128K,
192
+ GEMINI_1_5_FLASH_500K,
193
+ CLAUDE_3_HAIKU_200K,
194
+ CLAUDE_3_5_SONNET_200K,
195
+ ];
196
+
197
+ pub const LONG_CONTEXT_MODELS: [&str; 4] = [
198
+ GPT_4O_128K,
199
+ GEMINI_1_5_FLASH_500K,
200
+ CLAUDE_3_HAIKU_200K,
201
+ CLAUDE_3_5_SONNET_200K,
202
+ ];
203
+
204
+ // include!("constant/models.rs");
src/chat/constant/models.rs ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ pub struct DefaultModel {
2
+ pub default_on: bool,
3
+ pub is_long_context_only: Option<bool>,
4
+ pub name: &'static str,
5
+ }
6
+
7
+ pub const AVAILABLE_MODELS2: [DefaultModel; 22] = [
8
+ DefaultModel {
9
+ default_on: true,
10
+ is_long_context_only: Some(false),
11
+ name: CLAUDE_3_5_SONNET,
12
+ },
13
+ DefaultModel {
14
+ default_on: false,
15
+ is_long_context_only: None,
16
+ name: GPT_4,
17
+ },
18
+ DefaultModel {
19
+ default_on: true,
20
+ is_long_context_only: None,
21
+ name: GPT_4O,
22
+ },
23
+ DefaultModel {
24
+ default_on: false,
25
+ is_long_context_only: None,
26
+ name: CLAUDE_3_OPUS,
27
+ },
28
+ DefaultModel {
29
+ default_on: false,
30
+ is_long_context_only: None,
31
+ name: CURSOR_FAST,
32
+ },
33
+ DefaultModel {
34
+ default_on: false,
35
+ is_long_context_only: None,
36
+ name: CURSOR_SMALL,
37
+ },
38
+ DefaultModel {
39
+ default_on: false,
40
+ is_long_context_only: None,
41
+ name: GPT_3_5_TURBO,
42
+ },
43
+ DefaultModel {
44
+ default_on: false,
45
+ is_long_context_only: None,
46
+ name: GPT_4_TURBO_2024_04_09,
47
+ },
48
+ DefaultModel {
49
+ default_on: true,
50
+ is_long_context_only: Some(true),
51
+ name: GPT_4O_128K,
52
+ },
53
+ DefaultModel {
54
+ default_on: true,
55
+ is_long_context_only: Some(true),
56
+ name: GEMINI_1_5_FLASH_500K,
57
+ },
58
+ DefaultModel {
59
+ default_on: true,
60
+ is_long_context_only: Some(true),
61
+ name: CLAUDE_3_HAIKU_200K,
62
+ },
63
+ DefaultModel {
64
+ default_on: true,
65
+ is_long_context_only: Some(true),
66
+ name: CLAUDE_3_5_SONNET_200K,
67
+ },
68
+ DefaultModel {
69
+ default_on: false,
70
+ is_long_context_only: Some(false),
71
+ name: CLAUDE_3_5_SONNET_20241022,
72
+ },
73
+ DefaultModel {
74
+ default_on: true,
75
+ is_long_context_only: Some(false),
76
+ name: GPT_4O_MINI,
77
+ },
78
+ DefaultModel {
79
+ default_on: true,
80
+ is_long_context_only: Some(false),
81
+ name: O1_MINI,
82
+ },
83
+ DefaultModel {
84
+ default_on: true,
85
+ is_long_context_only: Some(false),
86
+ name: O1_PREVIEW,
87
+ },
88
+ DefaultModel {
89
+ default_on: true,
90
+ is_long_context_only: Some(false),
91
+ name: O1,
92
+ },
93
+ DefaultModel {
94
+ default_on: false,
95
+ is_long_context_only: Some(false),
96
+ name: CLAUDE_3_5_HAIKU,
97
+ },
98
+ DefaultModel {
99
+ default_on: false,
100
+ is_long_context_only: None,
101
+ name: GEMINI_EXP_1206,
102
+ },
103
+ DefaultModel {
104
+ default_on: false,
105
+ is_long_context_only: None,
106
+ name: GEMINI_2_0_FLASH_THINKING_EXP,
107
+ },
108
+ DefaultModel {
109
+ default_on: false,
110
+ is_long_context_only: None,
111
+ name: GEMINI_2_0_FLASH_EXP,
112
+ },
113
+ DefaultModel {
114
+ default_on: false,
115
+ is_long_context_only: None,
116
+ name: DEEPSEEK_V3,
117
+ },
118
+ ];
src/chat/error.rs ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use super::aiserver::v1::ErrorDetails;
2
+ use crate::common::model::{ApiStatus, ErrorResponse as CommonErrorResponse};
3
+ use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _};
4
+ use prost::Message as _;
5
+ use reqwest::StatusCode;
6
+ use serde::{Deserialize, Serialize};
7
+
8
+ #[derive(Deserialize)]
9
+ pub struct ChatError {
10
+ error: ErrorBody,
11
+ }
12
+
13
+ #[derive(Deserialize)]
14
+ pub struct ErrorBody {
15
+ code: String,
16
+ // message: String, always: Error
17
+ details: Vec<ErrorDetail>,
18
+ }
19
+
20
+ #[derive(Deserialize)]
21
+ pub struct ErrorDetail {
22
+ // #[serde(rename = "type")]
23
+ // error_type: String, always: aiserver.v1.ErrorDetails
24
+ // debug: ErrorDebug,
25
+ value: String,
26
+ }
27
+
28
+ // #[derive(Deserialize)]
29
+ // pub struct ErrorDebug {
30
+ // error: String,
31
+ // details: ErrorDetails,
32
+ // // #[serde(rename = "isExpected")]
33
+ // // is_expected: Option<bool>,
34
+ // }
35
+
36
+ // #[derive(Deserialize)]
37
+ // pub struct ErrorDetails {
38
+ // title: String,
39
+ // detail: String,
40
+ // // #[serde(rename = "isRetryable")]
41
+ // // is_retryable: Option<bool>,
42
+ // }
43
+
44
+ impl ChatError {
45
+ pub fn to_error_response(self) -> ErrorResponse {
46
+ if self.error.details.is_empty() {
47
+ return ErrorResponse {
48
+ status: 500,
49
+ code: "unknown".to_string(),
50
+ error: None,
51
+ };
52
+ }
53
+
54
+ let error_details = self.error.details.first().and_then(|detail| {
55
+ STANDARD_NO_PAD
56
+ .decode(&detail.value)
57
+ .ok()
58
+ .map(bytes::Bytes::from)
59
+ .and_then(|buf| ErrorDetails::decode(buf).ok())
60
+ });
61
+
62
+ let status = error_details
63
+ .as_ref()
64
+ .map(|details| details.status_code())
65
+ .unwrap_or(500);
66
+
67
+ ErrorResponse {
68
+ status,
69
+ code: self.error.code,
70
+ error: error_details
71
+ .and_then(|details| details.details)
72
+ .map(|custom_details| Error {
73
+ message: custom_details.title,
74
+ details: custom_details.detail,
75
+ }),
76
+ }
77
+ }
78
+ }
79
+
80
+ #[derive(Serialize)]
81
+ pub struct ErrorResponse {
82
+ pub status: u16,
83
+ pub code: String,
84
+ #[serde(skip_serializing_if = "Option::is_none")]
85
+ pub error: Option<Error>,
86
+ }
87
+
88
+ #[derive(Serialize)]
89
+ pub struct Error {
90
+ pub message: String,
91
+ pub details: String,
92
+ // pub value: String,
93
+ }
94
+
95
+ impl ErrorResponse {
96
+ // pub fn to_json(&self) -> serde_json::Value {
97
+ // serde_json::to_value(self).unwrap()
98
+ // }
99
+
100
+ pub fn status_code(&self) -> StatusCode {
101
+ StatusCode::from_u16(self.status).unwrap()
102
+ }
103
+
104
+ pub fn native_code(&self) -> String {
105
+ self.error.as_ref().map_or_else(
106
+ || self.code.replace("_", " "),
107
+ |error| error.message.clone(),
108
+ )
109
+ }
110
+
111
+ pub fn to_common(self) -> CommonErrorResponse {
112
+ CommonErrorResponse {
113
+ status: ApiStatus::Error,
114
+ code: Some(self.status),
115
+ error: self
116
+ .error
117
+ .as_ref()
118
+ .map(|error| error.message.clone())
119
+ .or(Some(self.code.clone())),
120
+ message: self.error.as_ref().map(|error| error.details.clone()),
121
+ }
122
+ }
123
+ }
124
+
125
+ pub enum StreamError {
126
+ ChatError(ChatError),
127
+ DataLengthLessThan5,
128
+ EmptyStream,
129
+ }
130
+
131
+ impl std::fmt::Display for StreamError {
132
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133
+ match self {
134
+ StreamError::ChatError(error) => write!(f, "{}", error.error.code),
135
+ StreamError::DataLengthLessThan5 => write!(f, "data length less than 5"),
136
+ StreamError::EmptyStream => write!(f, "empty stream"),
137
+ }
138
+ }
139
+ }
src/chat/middleware.rs ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ mod auth;
2
+ pub use auth::*;
src/chat/middleware/auth.rs ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use crate::app::{constant::AUTHORIZATION_BEARER_PREFIX, lazy::AUTH_TOKEN};
2
+ use axum::{
3
+ body::Body,
4
+ http::{header::AUTHORIZATION, Request, StatusCode},
5
+ middleware::Next,
6
+ response::Response,
7
+ };
8
+
9
+ // 认证中间件函数
10
+ pub async fn auth_middleware(request: Request<Body>, next: Next) -> Result<Response, StatusCode> {
11
+ let auth_header = request
12
+ .headers()
13
+ .get(AUTHORIZATION)
14
+ .and_then(|h| h.to_str().ok())
15
+ .and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
16
+ .ok_or(StatusCode::UNAUTHORIZED)?;
17
+
18
+ if auth_header != AUTH_TOKEN.as_str() {
19
+ return Err(StatusCode::UNAUTHORIZED);
20
+ }
21
+
22
+ Ok(next.run(request).await)
23
+ }
src/chat/model.rs ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use serde::{Deserialize, Serialize};
2
+
3
+ #[derive(Serialize, Deserialize)]
4
+ #[serde(untagged)]
5
+ pub enum MessageContent {
6
+ Text(String),
7
+ Vision(Vec<VisionMessageContent>),
8
+ }
9
+
10
+ #[derive(Serialize, Deserialize)]
11
+ pub struct VisionMessageContent {
12
+ #[serde(rename = "type")]
13
+ pub content_type: String,
14
+ #[serde(skip_serializing_if = "Option::is_none")]
15
+ pub text: Option<String>,
16
+ #[serde(skip_serializing_if = "Option::is_none")]
17
+ pub image_url: Option<ImageUrl>,
18
+ }
19
+
20
+ #[derive(Serialize, Deserialize)]
21
+ pub struct ImageUrl {
22
+ pub url: String,
23
+ #[serde(skip_serializing_if = "Option::is_none")]
24
+ pub detail: Option<String>,
25
+ }
26
+
27
+ #[derive(Serialize, Deserialize)]
28
+ pub struct Message {
29
+ pub role: Role,
30
+ pub content: MessageContent,
31
+ }
32
+
33
+ #[derive(Serialize, Deserialize, PartialEq)]
34
+ pub enum Role {
35
+ #[serde(rename = "system", alias = "developer")]
36
+ System,
37
+ #[serde(rename = "user", alias = "human")]
38
+ User,
39
+ #[serde(rename = "assistant", alias = "ai")]
40
+ Assistant,
41
+ }
42
+
43
+ #[derive(Serialize)]
44
+ pub struct ChatResponse {
45
+ pub id: String,
46
+ pub object: String,
47
+ pub created: i64,
48
+ #[serde(skip_serializing_if = "Option::is_none")]
49
+ pub model: Option<String>,
50
+ pub choices: Vec<Choice>,
51
+ #[serde(skip_serializing_if = "Option::is_none")]
52
+ pub usage: Option<Usage>,
53
+ }
54
+
55
+ #[derive(Serialize)]
56
+ pub struct Choice {
57
+ pub index: i32,
58
+ #[serde(skip_serializing_if = "Option::is_none")]
59
+ pub message: Option<Message>,
60
+ #[serde(skip_serializing_if = "Option::is_none")]
61
+ pub delta: Option<Delta>,
62
+ pub finish_reason: Option<String>,
63
+ }
64
+
65
+ #[derive(Serialize)]
66
+ pub struct Delta {
67
+ #[serde(skip_serializing_if = "Option::is_none")]
68
+ pub role: Option<Role>,
69
+ #[serde(skip_serializing_if = "Option::is_none")]
70
+ pub content: Option<String>,
71
+ }
72
+
73
+ #[derive(Serialize)]
74
+ pub struct Usage {
75
+ pub prompt_tokens: u32,
76
+ pub completion_tokens: u32,
77
+ pub total_tokens: u32,
78
+ }
79
+
80
+ // 模型定义
81
+ #[derive(Serialize, Clone)]
82
+ pub struct Model {
83
+ pub id: &'static str,
84
+ pub created: &'static i64,
85
+ pub object: &'static str,
86
+ pub owned_by: &'static str,
87
+ }
88
+
89
+ use super::constant::USAGE_CHECK_MODELS;
90
+ use crate::app::model::{AppConfig, UsageCheck};
91
+
92
+ impl Model {
93
+ pub fn is_usage_check(&self, usage_check: Option<UsageCheck>) -> bool {
94
+ match usage_check.unwrap_or(AppConfig::get_usage_check()) {
95
+ UsageCheck::None => false,
96
+ UsageCheck::Default => USAGE_CHECK_MODELS.contains(&self.id),
97
+ UsageCheck::All => true,
98
+ UsageCheck::Custom(models) => models.contains(&self.id),
99
+ }
100
+ }
101
+ }
102
+
103
+ #[derive(Serialize)]
104
+ pub struct ModelsResponse {
105
+ pub object: &'static str,
106
+ pub data: &'static [Model],
107
+ }
src/chat/route.rs ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ mod logs;
2
+ pub use logs::{handle_logs, handle_logs_post};
3
+ mod health;
4
+ pub use health::{handle_health, handle_root};
5
+ mod tokens;
6
+ pub use tokens::{
7
+ handle_add_tokens, handle_basic_calibration, handle_delete_tokens, handle_get_checksum,
8
+ handle_get_hash, handle_get_timestamp_header, handle_get_tokens, handle_reload_tokens,
9
+ handle_tokens_page, handle_update_tokens,
10
+ };
11
+ mod profile;
12
+ pub use profile::handle_user_info;
13
+ mod config;
14
+ pub use config::{
15
+ handle_about, handle_build_key, handle_build_key_page, handle_config_page, handle_env_example,
16
+ handle_readme, handle_static,
17
+ };
18
+ mod api;
19
+ pub use api::handle_api_page;
src/chat/route/api.rs ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use axum::response::{IntoResponse, Response};
2
+ use reqwest::header::CONTENT_TYPE;
3
+
4
+ use crate::{
5
+ app::constant::{
6
+ CONTENT_TYPE_TEXT_HTML_WITH_UTF8, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_API_PATH,
7
+ },
8
+ AppConfig, PageContent,
9
+ };
10
+
11
+ pub async fn handle_api_page() -> impl IntoResponse {
12
+ match AppConfig::get_page_content(ROUTE_API_PATH).unwrap_or_default() {
13
+ PageContent::Default => Response::builder()
14
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
15
+ .body(include_str!("../../../static/api.min.html").to_string())
16
+ .unwrap(),
17
+ PageContent::Text(content) => Response::builder()
18
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
19
+ .body(content.clone())
20
+ .unwrap(),
21
+ PageContent::Html(content) => Response::builder()
22
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
23
+ .body(content.clone())
24
+ .unwrap(),
25
+ }
26
+ }
src/chat/route/config.rs ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use crate::{
2
+ app::{
3
+ constant::{
4
+ AUTHORIZATION_BEARER_PREFIX, CONTENT_TYPE_TEXT_CSS_WITH_UTF8, CONTENT_TYPE_TEXT_HTML_WITH_UTF8, CONTENT_TYPE_TEXT_JS_WITH_UTF8, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, ROUTE_ABOUT_PATH, ROUTE_BUILD_KEY_PATH, ROUTE_CONFIG_PATH, ROUTE_README_PATH, ROUTE_SHARED_JS_PATH, ROUTE_SHARED_STYLES_PATH
5
+ },
6
+ lazy::{AUTH_TOKEN, KEY_PREFIX},
7
+ model::{AppConfig, BuildKeyRequest, BuildKeyResponse, PageContent, UsageCheckModelType},
8
+ },
9
+ chat::config::{key_config, KeyConfig},
10
+ common::utils::{to_base64, token_to_tokeninfo},
11
+ };
12
+ use axum::{
13
+ body::Body,
14
+ extract::Path,
15
+ http::{
16
+ header::{AUTHORIZATION, CONTENT_TYPE, LOCATION},
17
+ HeaderMap, StatusCode,
18
+ },
19
+ response::{IntoResponse, Response},
20
+ Json,
21
+ };
22
+ use prost::Message as _;
23
+
24
+ pub async fn handle_env_example() -> impl IntoResponse {
25
+ Response::builder()
26
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
27
+ .body(include_str!("../../../.env.example").to_string())
28
+ .unwrap()
29
+ }
30
+
31
+ // 配置页面处理函数
32
+ pub async fn handle_config_page() -> impl IntoResponse {
33
+ match AppConfig::get_page_content(ROUTE_CONFIG_PATH).unwrap_or_default() {
34
+ PageContent::Default => Response::builder()
35
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
36
+ .body(include_str!("../../../static/config.min.html").to_string())
37
+ .unwrap(),
38
+ PageContent::Text(content) => Response::builder()
39
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
40
+ .body(content.clone())
41
+ .unwrap(),
42
+ PageContent::Html(content) => Response::builder()
43
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
44
+ .body(content.clone())
45
+ .unwrap(),
46
+ }
47
+ }
48
+
49
+ pub async fn handle_static(Path(path): Path<String>) -> impl IntoResponse {
50
+ match path.as_str() {
51
+ "shared-styles.css" => {
52
+ match AppConfig::get_page_content(ROUTE_SHARED_STYLES_PATH).unwrap_or_default() {
53
+ PageContent::Default => Response::builder()
54
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_CSS_WITH_UTF8)
55
+ .body(include_str!("../../../static/shared-styles.min.css").to_string())
56
+ .unwrap(),
57
+ PageContent::Text(content) | PageContent::Html(content) => Response::builder()
58
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_CSS_WITH_UTF8)
59
+ .body(content.clone())
60
+ .unwrap(),
61
+ }
62
+ }
63
+ "shared.js" => {
64
+ match AppConfig::get_page_content(ROUTE_SHARED_JS_PATH).unwrap_or_default() {
65
+ PageContent::Default => Response::builder()
66
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_JS_WITH_UTF8)
67
+ .body(include_str!("../../../static/shared.min.js").to_string())
68
+ .unwrap(),
69
+ PageContent::Text(content) | PageContent::Html(content) => Response::builder()
70
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_JS_WITH_UTF8)
71
+ .body(content.clone())
72
+ .unwrap(),
73
+ }
74
+ }
75
+ _ => Response::builder()
76
+ .status(StatusCode::NOT_FOUND)
77
+ .body("Not found".to_string())
78
+ .unwrap(),
79
+ }
80
+ }
81
+
82
+ pub async fn handle_readme() -> impl IntoResponse {
83
+ match AppConfig::get_page_content(ROUTE_README_PATH).unwrap_or_default() {
84
+ PageContent::Default => Response::builder()
85
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
86
+ .body(include_str!("../../../static/readme.min.html").to_string())
87
+ .unwrap(),
88
+ PageContent::Text(content) => Response::builder()
89
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
90
+ .body(content.clone())
91
+ .unwrap(),
92
+ PageContent::Html(content) => Response::builder()
93
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
94
+ .body(content.clone())
95
+ .unwrap(),
96
+ }
97
+ }
98
+
99
+ pub async fn handle_about() -> impl IntoResponse {
100
+ match AppConfig::get_page_content(ROUTE_ABOUT_PATH).unwrap_or_default() {
101
+ PageContent::Default => Response::builder()
102
+ .status(StatusCode::TEMPORARY_REDIRECT)
103
+ .header(LOCATION, ROUTE_README_PATH)
104
+ .body(Body::empty())
105
+ .unwrap(),
106
+ PageContent::Text(content) => Response::builder()
107
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
108
+ .body(Body::from(content.clone()))
109
+ .unwrap(),
110
+ PageContent::Html(content) => Response::builder()
111
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
112
+ .body(Body::from(content.clone()))
113
+ .unwrap(),
114
+ }
115
+ }
116
+
117
+ pub async fn handle_build_key_page() -> impl IntoResponse {
118
+ match AppConfig::get_page_content(ROUTE_BUILD_KEY_PATH).unwrap_or_default() {
119
+ PageContent::Default => Response::builder()
120
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
121
+ .body(include_str!("../../../static/build_key.min.html").to_string())
122
+ .unwrap(),
123
+ PageContent::Text(content) => Response::builder()
124
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
125
+ .body(content.clone())
126
+ .unwrap(),
127
+ PageContent::Html(content) => Response::builder()
128
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
129
+ .body(content.clone())
130
+ .unwrap(),
131
+ }
132
+ }
133
+
134
+ pub async fn handle_build_key(
135
+ headers: HeaderMap,
136
+ Json(request): Json<BuildKeyRequest>,
137
+ ) -> (StatusCode, Json<BuildKeyResponse>) {
138
+ // 验证认证令牌
139
+ if AppConfig::is_share() {
140
+ let auth_header = headers
141
+ .get(AUTHORIZATION)
142
+ .and_then(|h| h.to_str().ok())
143
+ .and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX));
144
+
145
+ if auth_header.map_or(true, |h| h != AppConfig::get_share_token().as_str() && h != AUTH_TOKEN.as_str()) {
146
+ return (
147
+ StatusCode::UNAUTHORIZED,
148
+ Json(BuildKeyResponse::Error("Unauthorized".to_owned())),
149
+ );
150
+ }
151
+ }
152
+
153
+ // 验证并解析 auth_token
154
+ let token_info = match token_to_tokeninfo(&request.auth_token) {
155
+ Some(info) => info,
156
+ None => {
157
+ return (
158
+ StatusCode::BAD_REQUEST,
159
+ Json(BuildKeyResponse::Error("Invalid auth token".to_owned())),
160
+ )
161
+ }
162
+ };
163
+
164
+ // 构建 proto 消息
165
+ let mut key_config = KeyConfig {
166
+ auth_token: Some(token_info),
167
+ disable_vision: request.disable_vision,
168
+ enable_slow_pool: request.enable_slow_pool,
169
+ usage_check_models: None,
170
+ include_web_references: request.include_web_references,
171
+ };
172
+
173
+ if let Some(usage_check_models) = request.usage_check_models {
174
+ let usage_check = key_config::UsageCheckModel {
175
+ r#type: match usage_check_models.model_type {
176
+ UsageCheckModelType::Default => {
177
+ key_config::usage_check_model::Type::Default as i32
178
+ }
179
+ UsageCheckModelType::Disabled => {
180
+ key_config::usage_check_model::Type::Disabled as i32
181
+ }
182
+ UsageCheckModelType::All => key_config::usage_check_model::Type::All as i32,
183
+ UsageCheckModelType::Custom => key_config::usage_check_model::Type::Custom as i32,
184
+ },
185
+ model_ids: if matches!(usage_check_models.model_type, UsageCheckModelType::Custom) {
186
+ usage_check_models
187
+ .model_ids
188
+ .iter()
189
+ .map(|s| s.to_string())
190
+ .collect()
191
+ } else {
192
+ Vec::new()
193
+ },
194
+ };
195
+ key_config.usage_check_models = Some(usage_check);
196
+ }
197
+
198
+ // 序列化
199
+ let encoded = key_config.encode_to_vec();
200
+
201
+ let key = format!("{}{}", *KEY_PREFIX, to_base64(&encoded));
202
+
203
+ (StatusCode::OK, Json(BuildKeyResponse::Key(key)))
204
+ }
src/chat/route/health.rs ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use crate::{
2
+ app::{
3
+ constant::{
4
+ AUTHORIZATION_BEARER_PREFIX, CONTENT_TYPE_TEXT_HTML_WITH_UTF8,
5
+ CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8, PKG_VERSION, ROUTE_ABOUT_PATH, ROUTE_API_PATH,
6
+ ROUTE_BASIC_CALIBRATION_PATH, ROUTE_BUILD_KEY_PATH, ROUTE_CONFIG_PATH,
7
+ ROUTE_ENV_EXAMPLE_PATH, ROUTE_GET_CHECKSUM, ROUTE_GET_HASH, ROUTE_GET_TIMESTAMP_HEADER,
8
+ ROUTE_HEALTH_PATH, ROUTE_LOGS_PATH, ROUTE_README_PATH, ROUTE_ROOT_PATH,
9
+ ROUTE_STATIC_PATH, ROUTE_TOKENS_ADD_PATH, ROUTE_TOKENS_DELETE_PATH,
10
+ ROUTE_TOKENS_GET_PATH, ROUTE_TOKENS_PATH, ROUTE_TOKENS_UPDATE_PATH,
11
+ ROUTE_USER_INFO_PATH,
12
+ },
13
+ lazy::{get_start_time, AUTH_TOKEN, ROUTE_CHAT_PATH, ROUTE_MODELS_PATH},
14
+ model::{AppConfig, AppState, PageContent},
15
+ },
16
+ chat::constant::AVAILABLE_MODELS,
17
+ common::model::{
18
+ health::{CpuInfo, HealthCheckResponse, MemoryInfo, SystemInfo, SystemStats},
19
+ ApiStatus,
20
+ },
21
+ };
22
+ use axum::{
23
+ body::Body,
24
+ extract::State,
25
+ http::{
26
+ header::{CONTENT_TYPE, LOCATION},
27
+ HeaderMap, StatusCode,
28
+ },
29
+ response::{IntoResponse, Response},
30
+ Json,
31
+ };
32
+ use chrono::Local;
33
+ use reqwest::header::AUTHORIZATION;
34
+ use std::sync::Arc;
35
+ use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System};
36
+ use tokio::sync::Mutex;
37
+
38
+ pub async fn handle_root() -> impl IntoResponse {
39
+ match AppConfig::get_page_content(ROUTE_ROOT_PATH).unwrap_or_default() {
40
+ PageContent::Default => Response::builder()
41
+ .status(StatusCode::TEMPORARY_REDIRECT)
42
+ .header(LOCATION, ROUTE_HEALTH_PATH)
43
+ .body(Body::empty())
44
+ .unwrap(),
45
+ PageContent::Text(content) => Response::builder()
46
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN_WITH_UTF8)
47
+ .body(Body::from(content.clone()))
48
+ .unwrap(),
49
+ PageContent::Html(content) => Response::builder()
50
+ .header(CONTENT_TYPE, CONTENT_TYPE_TEXT_HTML_WITH_UTF8)
51
+ .body(Body::from(content.clone()))
52
+ .unwrap(),
53
+ }
54
+ }
55
+
56
+ pub async fn handle_health(
57
+ State(state): State<Arc<Mutex<AppState>>>,
58
+ headers: HeaderMap,
59
+ ) -> Json<HealthCheckResponse> {
60
+ let start_time = get_start_time();
61
+ let uptime = (Local::now() - start_time).num_seconds();
62
+
63
+ // 先检查 headers 是否包含有效的认证信息
64
+ let stats = if headers
65
+ .get(AUTHORIZATION)
66
+ .and_then(|h| h.to_str().ok())
67
+ .and_then(|h| h.strip_prefix(AUTHORIZATION_BEARER_PREFIX))
68
+ .map_or(false, |token| token == AUTH_TOKEN.as_str())
69
+ {
70
+ // 只有在需要系统信息时才创建实例
71
+ let mut sys = System::new_with_specifics(
72
+ RefreshKind::nothing()
73
+ .with_memory(MemoryRefreshKind::everything())
74
+ .with_cpu(CpuRefreshKind::everything()),
75
+ );
76
+
77
+ std::thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
78
+
79
+ // 刷新 CPU 和内存信息
80
+ sys.refresh_memory();
81
+ sys.refresh_cpu_usage();
82
+
83
+ let pid = std::process::id() as usize;
84
+ let process = sys.process(pid.into());
85
+
86
+ // 获取内存信息
87
+ let memory = process.map(|p| p.memory()).unwrap_or(0);
88
+
89
+ // 获取 CPU 使用率
90
+ let cpu_usage = sys.global_cpu_usage();
91
+
92
+ let state = state.lock().await;
93
+
94
+ Some(SystemStats {
95
+ started: start_time.to_string(),
96
+ total_requests: state.total_requests,
97
+ active_requests: state.active_requests,
98
+ system: SystemInfo {
99
+ memory: MemoryInfo {
100
+ rss: memory, // 物理内存使用量(字节)
101
+ },
102
+ cpu: CpuInfo {
103
+ usage: cpu_usage, // CPU 使用率(百分比)
104
+ },
105
+ },
106
+ })
107
+ } else {
108
+ None
109
+ };
110
+
111
+ Json(HealthCheckResponse {
112
+ status: ApiStatus::Healthy,
113
+ version: PKG_VERSION,
114
+ uptime,
115
+ stats,
116
+ models: AVAILABLE_MODELS.iter().map(|m| m.id).collect::<Vec<_>>(),
117
+ endpoints: vec![
118
+ ROUTE_CHAT_PATH.as_str(),
119
+ ROUTE_MODELS_PATH.as_str(),
120
+ ROUTE_TOKENS_PATH,
121
+ ROUTE_TOKENS_GET_PATH,
122
+ ROUTE_TOKENS_UPDATE_PATH,
123
+ ROUTE_TOKENS_ADD_PATH,
124
+ ROUTE_TOKENS_DELETE_PATH,
125
+ ROUTE_LOGS_PATH,
126
+ ROUTE_ENV_EXAMPLE_PATH,
127
+ ROUTE_CONFIG_PATH,
128
+ ROUTE_STATIC_PATH,
129
+ ROUTE_ABOUT_PATH,
130
+ ROUTE_README_PATH,
131
+ ROUTE_API_PATH,
132
+ ROUTE_GET_HASH,
133
+ ROUTE_GET_CHECKSUM,
134
+ ROUTE_GET_TIMESTAMP_HEADER,
135
+ ROUTE_BASIC_CALIBRATION_PATH,
136
+ ROUTE_USER_INFO_PATH,
137
+ ROUTE_BUILD_KEY_PATH,
138
+ ],
139
+ })
140
+ }