@chenpu17/cc-gw 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/README.md +95 -2
  2. package/package.json +3 -2
  3. package/src/cli/dist/index.js +20 -2
  4. package/src/server/dist/index.js +150 -43
  5. package/src/web/dist/assets/{About-R4GuiAOC.js → About-BkGxQ3Pv.js} +2 -2
  6. package/src/web/dist/assets/{ApiKeys-CNNwIRTy.js → ApiKeys-GdImp523.js} +1 -1
  7. package/src/web/dist/assets/{Button-CgdNhVeu.js → Button-BkhovQFd.js} +1 -1
  8. package/src/web/dist/assets/{Dashboard-U1EC6qmF.js → Dashboard-DxSNZwZc.js} +1 -1
  9. package/src/web/dist/assets/{FormField-DJjnoGU0.js → FormField-C-bAE13W.js} +1 -1
  10. package/src/web/dist/assets/{Help-COAfsR6K.js → Help-Bj3HXV4H.js} +1 -1
  11. package/src/web/dist/assets/{Input-CkTJl-fF.js → Input-B6cOxhbI.js} +1 -1
  12. package/src/web/dist/assets/{Login-BbRStE3M.js → Login-Btsbo17f.js} +1 -1
  13. package/src/web/dist/assets/Logs-72HRZmFi.js +1 -0
  14. package/src/web/dist/assets/{ModelManagement-BWX0w6Wu.js → ModelManagement-cuAzyPgP.js} +1 -1
  15. package/src/web/dist/assets/{PageSection-BT-depe5.js → PageSection-Bq4tdag3.js} +1 -1
  16. package/src/web/dist/assets/Settings-DPvX1pD_.js +11 -0
  17. package/src/web/dist/assets/{StatusBadge-CgBAXD_S.js → StatusBadge-P00M_NBZ.js} +1 -1
  18. package/src/web/dist/assets/{copy-OD9YQ85H.js → copy-D321KBhI.js} +1 -1
  19. package/src/web/dist/assets/{index-DVBy-Kka.js → index-BLBh7aj6.js} +1 -1
  20. package/src/web/dist/assets/index-DEa23YLm.css +1 -0
  21. package/src/web/dist/assets/{index-3UByifOG.js → index-DP6DzFEd.js} +30 -30
  22. package/src/web/dist/assets/{info-DAG4KwRm.js → info-CADQNr0Q.js} +1 -1
  23. package/src/web/dist/assets/{useApiQuery-7KXAW9hB.js → useApiQuery-C5jmZPyb.js} +1 -1
  24. package/src/web/dist/index.html +2 -2
  25. package/src/web/dist/assets/Logs-C-7diPqf.js +0 -1
  26. package/src/web/dist/assets/Settings-CLM0Srx1.js +0 -1
  27. package/src/web/dist/assets/index-BFd07aus.css +0 -1
package/README.md CHANGED
@@ -166,7 +166,74 @@ export CC_GW_ANTHROPIC_BETA_ALL=claude-code-20250219,interleaved-thinking-2025-0
166
166
 
167
167
  然后运行 `direnv allow` 自动加载。
168
168
 
169
- ##### 6.3 自定义接入点(Custom Endpoints)
169
+ ##### 6.3 HTTPS 配置
170
+
171
+ cc-gw 支持同时启动 HTTP 和 HTTPS 服务。
172
+
173
+ **默认配置**:
174
+ - HTTP: 端口 `4100`(**默认启用**,推荐用于本地开发)
175
+ - HTTPS: 端口 `4443`(**默认关闭**)
176
+ - 两个协议可独立启用/禁用,但**至少要保持一个启用**
177
+
178
+ ⚠️ **重要提示:关于 HTTPS 证书**
179
+
180
+ - **自签名证书无效**:Claude Code 和大多数 AI 工具无法信任自签名证书,会导致 "Unable to connect to API" 错误
181
+ - **推荐方案**:本地开发环境建议使用 HTTP 协议(`127.0.0.1` 本地访问非常安全)
182
+ - **如需 HTTPS**:请使用受信任 CA(如 Let's Encrypt)签发的正式证书,或配置反向代理(如 Nginx/Caddy)处理 HTTPS
183
+
184
+ **手动配置 HTTPS(通过配置文件)**:
185
+
186
+ 编辑 `~/.cc-gw/config.json`:
187
+
188
+ ```json
189
+ {
190
+ "http": {
191
+ "enabled": true,
192
+ "port": 4100,
193
+ "host": "127.0.0.1"
194
+ },
195
+ "https": {
196
+ "enabled": true,
197
+ "port": 4443,
198
+ "host": "127.0.0.1",
199
+ "keyPath": "/path/to/your/ssl/key.pem",
200
+ "certPath": "/path/to/your/ssl/cert.pem",
201
+ "caPath": ""
202
+ }
203
+ }
204
+ ```
205
+
206
+ **使用 HTTPS 访问**:
207
+
208
+ ```bash
209
+ # 重启服务使配置生效
210
+ cc-gw restart --daemon
211
+
212
+ # 访问 HTTPS 端点
213
+ curl https://127.0.0.1:4443/health
214
+
215
+ # Web UI HTTPS 访问
216
+ open https://127.0.0.1:4443/ui
217
+ ```
218
+
219
+ **环境变量配置**:
220
+
221
+ ```bash
222
+ # 推荐:使用 HTTP(默认)
223
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:4100/anthropic
224
+ export OPENAI_BASE_URL=http://127.0.0.1:4100/openai/v1
225
+
226
+ # 如果使用 HTTPS(需要受信任的证书)
227
+ export ANTHROPIC_BASE_URL=https://127.0.0.1:4443/anthropic
228
+ export OPENAI_BASE_URL=https://127.0.0.1:4443/openai/v1
229
+ ```
230
+
231
+ **证书路径说明**:
232
+ - `keyPath`: 私钥文件路径(必需)
233
+ - `certPath`: 证书文件路径(必需)
234
+ - `caPath`: CA 证书路径(可选,用于证书链)
235
+
236
+ ##### 6.4 自定义接入点(Custom Endpoints)
170
237
 
171
238
  cc-gw 支持创建额外的自定义 API 端点,每个端点可以:
172
239
  - 使用不同的协议(Anthropic、OpenAI)
@@ -220,6 +287,12 @@ cc-gw 支持创建额外的自定义 API 端点,每个端点可以:
220
287
  - 检查模型路由配置
221
288
  - 确认上游服务模型名称正确
222
289
 
290
+ 4. **HTTPS 连接问题**:
291
+ - ⚠️ **Claude Code 无法连接**: 自签名证书会导致 "Unable to connect to API" 错误,建议使用 HTTP 协议(本地 127.0.0.1 访问安全)
292
+ - **证书路径错误**: 检查配置文件中的 `keyPath` 和 `certPath` 是否正确
293
+ - **需要受信任证书**: 如必须使用 HTTPS,请配置 Let's Encrypt 等受信任 CA 签发的证书
294
+ - **两个协议都禁用**: 至少要启用 HTTP 或 HTTPS 中的一个
295
+
223
296
  > ✅ 完成以上 6 个步骤后,你的 cc-gw 网关就完全配置好了!所有 AI 客户端都可以通过统一的网关访问不同的模型服务。
224
297
 
225
298
  ### 推荐方式:npm 全局安装
@@ -305,6 +378,19 @@ UI 支持中英文、深色/浅色主题以及移动端响应式布局,提供
305
378
 
306
379
  ```json
307
380
  {
381
+ "http": {
382
+ "enabled": true,
383
+ "port": 4100,
384
+ "host": "127.0.0.1"
385
+ },
386
+ "https": {
387
+ "enabled": true,
388
+ "port": 4443,
389
+ "host": "127.0.0.1",
390
+ "keyPath": "~/.cc-gw/certs/key.pem",
391
+ "certPath": "~/.cc-gw/certs/cert.pem",
392
+ "caPath": ""
393
+ },
308
394
  "host": "127.0.0.1",
309
395
  "port": 4100,
310
396
  "providers": [
@@ -348,8 +434,15 @@ UI 支持中英文、深色/浅色主题以及移动端响应式布局,提供
348
434
  }
349
435
  ```
350
436
 
351
- 字段要点(建议仍以 Web UI “系统设置 / 模型管理” 进行操作,下列仅便于理解结构):
437
+ 字段要点(建议仍以 Web UI "系统设置 / 模型管理" 进行操作,下列仅便于理解结构):
352
438
 
439
+ - `http` / `https`:协议配置,支持独立启用/禁用,但至少要保持一个启用。
440
+ - `enabled`:是否启用该协议
441
+ - `port`:监听端口
442
+ - `host`:监听地址
443
+ - `keyPath` / `certPath`(仅 HTTPS):SSL/TLS 证书路径
444
+ - `caPath`(可选):CA 证书链路径
445
+ - `host` / `port`:旧格式向后兼容字段,新配置会自动迁移到 `http` / `https` 格式。
353
446
  - `providers`:定义上游服务;`type` 支持 `openai | anthropic | kimi | deepseek | custom`。
354
447
  - 模型标识使用 `providerId:modelId` 形式供路由引用。
355
448
  - `modelRoutes`:将 Claude 发起的模型名映射到上游模型;支持在源模型名中使用 `*` 通配符匹配,匹配度更高(字符更多)的规则优先,同等情况下按配置顺序取第一条;若希望直接透传请求的模型名,可将目标写成 `providerId:*`,此时会将源请求中的模型名原样发送给对应 Provider。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenpu17/cc-gw",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -59,8 +59,8 @@
59
59
  "better-sqlite3": "^12.4.1",
60
60
  "colorette": "^2.0.20",
61
61
  "commander": "^12.0.0",
62
- "jszip": "^3.10.1",
63
62
  "fastify": "^4.26.2",
63
+ "jszip": "^3.10.1",
64
64
  "open": "^10.1.0",
65
65
  "tiktoken": "^1.0.21",
66
66
  "undici": "^6.11.1"
@@ -68,6 +68,7 @@
68
68
  "devDependencies": {
69
69
  "@eslint/js": "^9.5.0",
70
70
  "@playwright/test": "^1.55.1",
71
+ "@types/better-sqlite3": "^7.6.13",
71
72
  "@types/node": "^20.12.7",
72
73
  "eslint": "^8.57.0",
73
74
  "globals": "^15.0.0",
@@ -97,6 +97,20 @@ async function ensureConfigTemplate(port) {
97
97
  longContextThreshold: 6e4
98
98
  };
99
99
  const template = {
100
+ http: {
101
+ enabled: true,
102
+ port: selectedPort,
103
+ host: "127.0.0.1"
104
+ },
105
+ https: {
106
+ enabled: false,
107
+ port: 4443,
108
+ host: "127.0.0.1",
109
+ keyPath: "",
110
+ certPath: "",
111
+ caPath: ""
112
+ },
113
+ // 保留旧字段以兼容
100
114
  host: "127.0.0.1",
101
115
  port: selectedPort,
102
116
  providers: [],
@@ -218,10 +232,14 @@ async function handleStart(options) {
218
232
  }
219
233
  if (configCreated) {
220
234
  console.log(green(`\u5DF2\u5728 ${CONFIG_FILE} \u751F\u6210\u9ED8\u8BA4\u914D\u7F6E`));
221
- console.log(yellow(`\u9996\u6B21\u542F\u52A8\uFF1A\u5F85\u670D\u52A1\u5C31\u7EEA\u540E\uFF0C\u8BF7\u5728\u6D4F\u89C8\u5668\u8BBF\u95EE http://127.0.0.1:${effectivePort}/ui \u8FDB\u884C\u914D\u7F6E\u3002`));
235
+ console.log(yellow(`\u9996\u6B21\u542F\u52A8\uFF1A\u5F85\u670D\u52A1\u5C31\u7EEA\u540E\uFF0C\u8BF7\u8BBF\u95EE\u4EE5\u4E0B\u5730\u5740\u8FDB\u884C\u914D\u7F6E:`));
236
+ console.log(yellow(` HTTP: http://127.0.0.1:${effectivePort}/ui`));
237
+ console.log(yellow(` HTTPS: https://127.0.0.1:4443/ui (\u9700\u5148\u751F\u6210\u8BC1\u4E66)`));
222
238
  }
223
239
  if (daemonMode) {
224
- console.log(green(`Web UI \u5DF2\u5C31\u7EEA: http://127.0.0.1:${effectivePort}/ui`));
240
+ console.log(green(`\u670D\u52A1\u5DF2\u542F\u52A8:`));
241
+ console.log(green(` HTTP: http://127.0.0.1:${effectivePort}/ui`));
242
+ console.log(green(` HTTPS: https://127.0.0.1:4443/ui (\u5982\u5DF2\u542F\u7528)`));
225
243
  }
226
244
  if (!daemonMode) {
227
245
  const forwardSignal = (signal) => {
@@ -9921,6 +9921,7 @@ var LOG_LEVELS = /* @__PURE__ */ new Set([
9921
9921
  var HOME_OVERRIDE = process.env.CC_GW_HOME;
9922
9922
  var HOME_DIR = path.resolve(HOME_OVERRIDE ?? path.join(os.homedir(), ".cc-gw"));
9923
9923
  var CONFIG_PATH = path.join(HOME_DIR, "config.json");
9924
+ var CERTS_DIR = path.join(HOME_DIR, "certs");
9924
9925
  var TypedEmitter = class extends EventEmitter {
9925
9926
  on(event, listener) {
9926
9927
  return super.on(event, listener);
@@ -9973,6 +9974,42 @@ function sanitizeModelRoutes(input) {
9973
9974
  }
9974
9975
  return sanitized;
9975
9976
  }
9977
+ function migrateProtocolConfig(data) {
9978
+ if (data.http || data.https) {
9979
+ return;
9980
+ }
9981
+ const port = typeof data.port === "number" ? data.port : 4100;
9982
+ const host = typeof data.host === "string" ? data.host : "127.0.0.1";
9983
+ data.http = {
9984
+ enabled: true,
9985
+ port,
9986
+ host
9987
+ };
9988
+ const hasLegacyHttpsConfig = typeof data.httpsPort === "number" || typeof data.keyPath === "string" && data.keyPath || typeof data.certPath === "string" && data.certPath;
9989
+ data.https = {
9990
+ enabled: hasLegacyHttpsConfig ? true : false,
9991
+ port: typeof data.httpsPort === "number" ? data.httpsPort : 4443,
9992
+ host: typeof data.httpsHost === "string" ? data.httpsHost : host,
9993
+ keyPath: typeof data.keyPath === "string" ? data.keyPath : path.join(CERTS_DIR, "key.pem"),
9994
+ certPath: typeof data.certPath === "string" ? data.certPath : path.join(CERTS_DIR, "cert.pem"),
9995
+ caPath: typeof data.caPath === "string" ? data.caPath : ""
9996
+ };
9997
+ data.port = port;
9998
+ data.host = host;
9999
+ }
10000
+ function validateProtocolConfig(data) {
10001
+ const httpEnabled = data.http?.enabled === true;
10002
+ const httpsEnabled = data.https?.enabled === true;
10003
+ if (!httpEnabled && !httpsEnabled) {
10004
+ throw new Error("\u81F3\u5C11\u9700\u8981\u542F\u7528 HTTP \u6216 HTTPS \u534F\u8BAE");
10005
+ }
10006
+ if (httpsEnabled) {
10007
+ const https = data.https;
10008
+ if (!https.keyPath || !https.certPath) {
10009
+ throw new Error("HTTPS \u5DF2\u542F\u7528\u4F46\u7F3A\u5C11\u8BC1\u4E66\u8DEF\u5F84\u914D\u7F6E");
10010
+ }
10011
+ }
10012
+ }
9976
10013
  function sanitizeWebAuth(input) {
9977
10014
  if (!input || typeof input !== "object") {
9978
10015
  return {
@@ -10021,8 +10058,10 @@ function resolveEndpointRouting(source, fallback) {
10021
10058
  }
10022
10059
  function parseConfig(raw) {
10023
10060
  const data = JSON.parse(raw);
10061
+ migrateProtocolConfig(data);
10062
+ validateProtocolConfig(data);
10024
10063
  if (typeof data.port !== "number") {
10025
- throw new Error("\u914D\u7F6E\u6587\u4EF6\u7F3A\u5C11\u6216\u9519\u8BEF\u7684 port \u5B57\u6BB5");
10064
+ data.port = data.http?.port ?? 4100;
10026
10065
  }
10027
10066
  if (!Array.isArray(data.providers)) {
10028
10067
  data.providers = [];
@@ -12132,8 +12171,8 @@ async function registerMessagesRoute(app) {
12132
12171
  if (providerType === "anthropic") {
12133
12172
  providerBody = cloneOriginalPayload(payload);
12134
12173
  providerBody.model = target.modelId;
12135
- if (normalized.stream !== void 0) {
12136
- providerBody.stream = normalized.stream;
12174
+ if (Object.prototype.hasOwnProperty.call(payload, "stream")) {
12175
+ providerBody.stream = Boolean(payload.stream);
12137
12176
  }
12138
12177
  const collected = {};
12139
12178
  const skip = /* @__PURE__ */ new Set(["content-length", "host", "connection", "transfer-encoding"]);
@@ -13806,7 +13845,9 @@ async function registerOpenAiRoutes(app) {
13806
13845
  overrideTools: Array.isArray(payload.tools) ? payload.tools : void 0
13807
13846
  });
13808
13847
  providerBody.model = target.modelId;
13809
- providerBody.stream = normalized.stream;
13848
+ if (Object.prototype.hasOwnProperty.call(payload, "stream")) {
13849
+ providerBody.stream = Boolean(payload.stream);
13850
+ }
13810
13851
  const rawHeaders = request.raw?.headers ?? request.headers;
13811
13852
  const forwarded = collectAnthropicForwardHeaders(rawHeaders);
13812
13853
  providerHeaders = filterForwardedAnthropicHeaders(forwarded);
@@ -13845,7 +13886,9 @@ async function registerOpenAiRoutes(app) {
13845
13886
  } else {
13846
13887
  providerBody = { ...payload };
13847
13888
  providerBody.model = target.modelId;
13848
- providerBody.stream = normalized.stream;
13889
+ if (Object.prototype.hasOwnProperty.call(payload, "stream")) {
13890
+ providerBody.stream = Boolean(payload.stream);
13891
+ }
13849
13892
  if (providerBody.max_output_tokens == null && typeof providerBody.max_tokens === "number") {
13850
13893
  providerBody.max_output_tokens = providerBody.max_tokens;
13851
13894
  }
@@ -14371,8 +14414,8 @@ async function registerOpenAiRoutes(app) {
14371
14414
  inputTokens: finalPromptTokens,
14372
14415
  outputTokens: finalCompletionTokens,
14373
14416
  cachedTokens: finalCachedTokens ?? null,
14374
- cacheReadTokens: 0,
14375
- cacheCreationTokens: 0,
14417
+ cacheReadTokens: finalCachedResult.read,
14418
+ cacheCreationTokens: finalCachedResult.creation,
14376
14419
  ttftMs,
14377
14420
  tpotMs: computeTpot2(totalLatencyMs, finalCompletionTokens, {
14378
14421
  streaming: true,
@@ -14538,12 +14581,16 @@ async function registerOpenAiRoutes(app) {
14538
14581
  const textOutputTokens = usageCompletion ?? 0;
14539
14582
  const reasoningTokens = usageReasoning ?? 0;
14540
14583
  const outputTokens = textOutputTokens + reasoningTokens;
14584
+ const hasCacheStats = usageCached != null;
14585
+ const resolvedCachedTokens = hasCacheStats ? usageCached : null;
14586
+ const resolvedCacheRead = hasCacheStats ? usageCacheRead : null;
14587
+ const resolvedCacheCreation = hasCacheStats ? usageCacheCreation : null;
14541
14588
  await updateLogTokens(logId, {
14542
14589
  inputTokens,
14543
14590
  outputTokens,
14544
- cachedTokens: usageCached,
14545
- cacheReadTokens: 0,
14546
- cacheCreationTokens: 0,
14591
+ cachedTokens: resolvedCachedTokens,
14592
+ cacheReadTokens: resolvedCacheRead,
14593
+ cacheCreationTokens: resolvedCacheCreation,
14547
14594
  ttftMs: firstTokenAt ? firstTokenAt - requestStart : null,
14548
14595
  tpotMs: computeTpot2(latencyMs, outputTokens, {
14549
14596
  streaming: true,
@@ -14556,6 +14603,9 @@ async function registerOpenAiRoutes(app) {
14556
14603
  requests: 1,
14557
14604
  inputTokens,
14558
14605
  outputTokens,
14606
+ cachedTokens: resolvedCachedTokens ?? 0,
14607
+ cacheReadTokens: resolvedCacheRead ?? 0,
14608
+ cacheCreationTokens: resolvedCacheCreation ?? 0,
14559
14609
  latencyMs
14560
14610
  });
14561
14611
  await finalize(200, null);
@@ -14687,7 +14737,9 @@ async function registerOpenAiRoutes(app) {
14687
14737
  overrideTools
14688
14738
  });
14689
14739
  providerBody.model = target.modelId;
14690
- providerBody.stream = normalized.stream;
14740
+ if (Object.prototype.hasOwnProperty.call(payload, "stream")) {
14741
+ providerBody.stream = Boolean(payload.stream);
14742
+ }
14691
14743
  const rawHeaders = request.raw?.headers ?? request.headers;
14692
14744
  const forwarded = collectAnthropicForwardHeaders(rawHeaders);
14693
14745
  providerHeaders = filterForwardedAnthropicHeaders(forwarded);
@@ -14731,7 +14783,9 @@ async function registerOpenAiRoutes(app) {
14731
14783
  overrideTools
14732
14784
  });
14733
14785
  providerBody.model = target.modelId;
14734
- providerBody.stream = normalized.stream;
14786
+ if (Object.prototype.hasOwnProperty.call(payload, "stream")) {
14787
+ providerBody.stream = Boolean(payload.stream);
14788
+ }
14735
14789
  if (Array.isArray(payload.functions) && !providerBody.functions) {
14736
14790
  providerBody.functions = payload.functions;
14737
14791
  }
@@ -14807,7 +14861,7 @@ async function registerOpenAiRoutes(app) {
14807
14861
  await updateLogTokens(logId, {
14808
14862
  inputTokens: inputTokens3,
14809
14863
  outputTokens: outputTokens3,
14810
- cachedTokens: usageCached,
14864
+ cachedTokens: cachedTokens3,
14811
14865
  cacheReadTokens: cached3.read,
14812
14866
  cacheCreationTokens: cached3.creation,
14813
14867
  ttftMs: latencyMs3,
@@ -14818,9 +14872,9 @@ async function registerOpenAiRoutes(app) {
14818
14872
  requests: 1,
14819
14873
  inputTokens: inputTokens3,
14820
14874
  outputTokens: outputTokens3,
14821
- cachedTokens: usageCached,
14822
- cacheReadTokens: usageCacheRead,
14823
- cacheCreationTokens: usageCacheCreation,
14875
+ cachedTokens: cachedTokens3,
14876
+ cacheReadTokens: cached3.read,
14877
+ cacheCreationTokens: cached3.creation,
14824
14878
  latencyMs: latencyMs3
14825
14879
  });
14826
14880
  if (storeResponsePayloads) {
@@ -14856,7 +14910,7 @@ async function registerOpenAiRoutes(app) {
14856
14910
  await updateLogTokens(logId, {
14857
14911
  inputTokens: inputTokens2,
14858
14912
  outputTokens: outputTokens2,
14859
- cachedTokens: usageCached,
14913
+ cachedTokens: cachedTokens2,
14860
14914
  cacheReadTokens: cached2.read,
14861
14915
  cacheCreationTokens: cached2.creation,
14862
14916
  ttftMs: usagePayload?.first_token_latency_ms ?? latencyMs2,
@@ -15294,8 +15348,8 @@ async function registerOpenAiRoutes(app) {
15294
15348
  inputTokens: finalPromptTokens,
15295
15349
  outputTokens: finalCompletionTokens,
15296
15350
  cachedTokens: finalCachedTokens ?? null,
15297
- cacheReadTokens: 0,
15298
- cacheCreationTokens: 0,
15351
+ cacheReadTokens: finalCachedResult.read,
15352
+ cacheCreationTokens: finalCachedResult.creation,
15299
15353
  ttftMs,
15300
15354
  tpotMs: computeTpot2(totalLatencyMs, finalCompletionTokens, {
15301
15355
  streaming: true,
@@ -15436,12 +15490,16 @@ async function registerOpenAiRoutes(app) {
15436
15490
  const latencyMs = Date.now() - requestStart;
15437
15491
  const inputTokens = usagePrompt ?? usageCompletion ?? target.tokenEstimate ?? estimateTokens(normalized, target.modelId);
15438
15492
  const outputTokens = usageCompletion ?? 0;
15493
+ const hasCacheStats = usageCached != null;
15494
+ const resolvedCachedTokens = hasCacheStats ? usageCached : null;
15495
+ const resolvedCacheRead = hasCacheStats ? usageCacheRead : null;
15496
+ const resolvedCacheCreation = hasCacheStats ? usageCacheCreation : null;
15439
15497
  await updateLogTokens(logId, {
15440
15498
  inputTokens,
15441
15499
  outputTokens,
15442
- cachedTokens: usageCached,
15443
- cacheReadTokens: 0,
15444
- cacheCreationTokens: 0,
15500
+ cachedTokens: resolvedCachedTokens,
15501
+ cacheReadTokens: resolvedCacheRead,
15502
+ cacheCreationTokens: resolvedCacheCreation,
15445
15503
  ttftMs: firstTokenAt ? firstTokenAt - requestStart : null,
15446
15504
  tpotMs: computeTpot2(latencyMs, outputTokens, {
15447
15505
  streaming: true,
@@ -15453,6 +15511,9 @@ async function registerOpenAiRoutes(app) {
15453
15511
  requests: 1,
15454
15512
  inputTokens,
15455
15513
  outputTokens,
15514
+ cachedTokens: resolvedCachedTokens ?? 0,
15515
+ cacheReadTokens: resolvedCacheRead ?? 0,
15516
+ cacheCreationTokens: resolvedCacheCreation ?? 0,
15456
15517
  latencyMs
15457
15518
  });
15458
15519
  await finalize(200, null);
@@ -17544,8 +17605,8 @@ async function handleAnthropicProtocol(request, reply, endpoint, endpointId, app
17544
17605
  if (providerType === "anthropic") {
17545
17606
  providerBody = cloneOriginalPayload2(payload);
17546
17607
  providerBody.model = target.modelId;
17547
- if (normalized.stream !== void 0) {
17548
- providerBody.stream = normalized.stream;
17608
+ if (Object.prototype.hasOwnProperty.call(payload, "stream")) {
17609
+ providerBody.stream = Boolean(payload.stream);
17549
17610
  }
17550
17611
  const collected = {};
17551
17612
  const skip = /* @__PURE__ */ new Set(["content-length", "host", "connection", "transfer-encoding"]);
@@ -17874,7 +17935,9 @@ async function handleOpenAIChatProtocol(request, reply, endpoint, endpointId, ap
17874
17935
  });
17875
17936
  }
17876
17937
  providerBody.model = target.modelId;
17877
- providerBody.stream = normalized.stream;
17938
+ if (Object.prototype.hasOwnProperty.call(payload, "stream")) {
17939
+ providerBody.stream = Boolean(payload.stream);
17940
+ }
17878
17941
  const upstream = await connector.send({
17879
17942
  model: target.modelId,
17880
17943
  body: providerBody,
@@ -18165,7 +18228,9 @@ async function handleOpenAIResponsesProtocol(request, reply, endpoint, endpointI
18165
18228
  });
18166
18229
  }
18167
18230
  providerBody.model = target.modelId;
18168
- providerBody.stream = normalized.stream;
18231
+ if (Object.prototype.hasOwnProperty.call(payload, "stream")) {
18232
+ providerBody.stream = Boolean(payload.stream);
18233
+ }
18169
18234
  const upstream = await connector.send({
18170
18235
  model: target.modelId,
18171
18236
  body: providerBody,
@@ -18196,7 +18261,7 @@ async function handleOpenAIResponsesProtocol(request, reply, endpoint, endpointI
18196
18261
  await updateLogTokens(logId, {
18197
18262
  inputTokens: inputTokens2,
18198
18263
  outputTokens: outputTokens2,
18199
- cachedTokens: usageCached,
18264
+ cachedTokens: cachedTokens2,
18200
18265
  cacheReadTokens: cached2.read,
18201
18266
  cacheCreationTokens: cached2.creation,
18202
18267
  ttftMs: latencyMs2,
@@ -18339,11 +18404,11 @@ function startMaintenanceTimers() {
18339
18404
  scheduleCleanup();
18340
18405
  }
18341
18406
  function scheduleCleanup() {
18342
- const run2 = () => {
18407
+ const run2 = async () => {
18343
18408
  try {
18344
18409
  const retentionDays = getConfig().logRetentionDays ?? 30;
18345
18410
  const cutoff = Date.now() - retentionDays * DAY_MS;
18346
- const deleted = cleanupLogsBefore(cutoff);
18411
+ const deleted = await cleanupLogsBefore(cutoff);
18347
18412
  if (deleted > 0) {
18348
18413
  console.info(`[maintenance] cleaned ${deleted} old log entries`);
18349
18414
  }
@@ -18474,17 +18539,30 @@ async function syncCustomEndpoints(app, config) {
18474
18539
  );
18475
18540
  }
18476
18541
  }
18477
- async function createServer() {
18542
+ async function createServer(protocol = "http") {
18478
18543
  const config = cachedConfig2 ?? loadConfig();
18479
18544
  const requestLogEnabled = config.requestLogging !== false;
18480
18545
  const responseLogEnabled = config.responseLogging !== false;
18481
18546
  const bodyLimit = typeof config.bodyLimit === "number" && Number.isFinite(config.bodyLimit) && config.bodyLimit > 0 ? config.bodyLimit : 10 * 1024 * 1024;
18547
+ let httpsOptions;
18548
+ if (protocol === "https" && config.https?.enabled) {
18549
+ const { keyPath, certPath, caPath } = config.https;
18550
+ if (!fs4.existsSync(keyPath) || !fs4.existsSync(certPath)) {
18551
+ throw new Error(`HTTPS \u8BC1\u4E66\u6587\u4EF6\u4E0D\u5B58\u5728: ${keyPath}, ${certPath}`);
18552
+ }
18553
+ httpsOptions = {
18554
+ key: fs4.readFileSync(keyPath),
18555
+ cert: fs4.readFileSync(certPath),
18556
+ ca: caPath ? fs4.readFileSync(caPath) : void 0
18557
+ };
18558
+ }
18482
18559
  const app = Fastify({
18483
18560
  logger: {
18484
18561
  level: config.logLevel ?? "info"
18485
18562
  },
18486
18563
  disableRequestLogging: true,
18487
- bodyLimit
18564
+ bodyLimit,
18565
+ https: httpsOptions
18488
18566
  });
18489
18567
  app.addHook("onRequest", async (request, reply) => {
18490
18568
  const authConfig = (cachedConfig2 ?? getConfig()).webAuth;
@@ -18625,25 +18703,54 @@ async function createServer() {
18625
18703
  return app;
18626
18704
  }
18627
18705
  async function startServer(options = {}) {
18628
- const app = await createServer();
18629
- const envPort = process2.env.PORT ? Number.parseInt(process2.env.PORT, 10) : void 0;
18630
- const envHost = process2.env.HOST;
18631
- const configPort = cachedConfig2?.port;
18632
- const configHost = cachedConfig2?.host;
18633
- const port = options.port ?? envPort ?? configPort ?? DEFAULT_PORT;
18634
- const host = options.host ?? envHost ?? configHost ?? DEFAULT_HOST;
18635
- await app.listen({ port, host });
18636
- return app;
18706
+ const config = cachedConfig2 ?? loadConfig();
18707
+ const result = {};
18708
+ if (config.http?.enabled !== false) {
18709
+ const httpApp = await createServer("http");
18710
+ const httpPort = options.port ?? (process2.env.PORT ? Number.parseInt(process2.env.PORT, 10) : config.http?.port ?? config.port ?? DEFAULT_PORT);
18711
+ const httpHost = options.host ?? process2.env.HOST ?? config.http?.host ?? config.host ?? DEFAULT_HOST;
18712
+ await httpApp.listen({ port: httpPort, host: httpHost });
18713
+ httpApp.log.info(`HTTP server started at http://${httpHost}:${httpPort}`);
18714
+ result.http = httpApp;
18715
+ }
18716
+ if (config.https?.enabled === true) {
18717
+ try {
18718
+ const httpsApp = await createServer("https");
18719
+ const httpsPort = config.https.port;
18720
+ const httpsHost = config.https.host ?? config.host ?? DEFAULT_HOST;
18721
+ await httpsApp.listen({ port: httpsPort, host: httpsHost });
18722
+ httpsApp.log.info(`HTTPS server started at https://${httpsHost}:${httpsPort}`);
18723
+ result.https = httpsApp;
18724
+ } catch (error) {
18725
+ const errorMessage = error instanceof Error ? error.message : String(error);
18726
+ console.error(`HTTPS server\u542F\u52A8\u5931\u8D25: ${errorMessage}`);
18727
+ if (!result.http) {
18728
+ throw error;
18729
+ }
18730
+ console.warn("\u4EC5 HTTP \u670D\u52A1\u5668\u542F\u52A8\u6210\u529F,HTTPS \u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25");
18731
+ }
18732
+ }
18733
+ if (!result.http && !result.https) {
18734
+ throw new Error("HTTP \u548C HTTPS \u670D\u52A1\u5668\u5747\u672A\u542F\u52A8");
18735
+ }
18736
+ return result;
18637
18737
  }
18638
18738
  async function main() {
18639
18739
  try {
18640
- const app = await startServer();
18740
+ const servers = await startServer();
18641
18741
  const shutdown = async () => {
18642
18742
  try {
18643
- await app.close();
18743
+ const closePromises = [];
18744
+ if (servers.http) {
18745
+ closePromises.push(servers.http.close());
18746
+ }
18747
+ if (servers.https) {
18748
+ closePromises.push(servers.https.close());
18749
+ }
18750
+ await Promise.all(closePromises);
18644
18751
  process2.exit(0);
18645
18752
  } catch (err) {
18646
- app.log.error({ err }, "\u5173\u95ED\u670D\u52A1\u5931\u8D25");
18753
+ console.error("\u5173\u95ED\u670D\u52A1\u5931\u8D25:", err);
18647
18754
  process2.exit(1);
18648
18755
  }
18649
18756
  };
@@ -1,4 +1,4 @@
1
- import{c as p,u as v,a as k,r,j as e,d as o,U as j,m as i}from"./index-3UByifOG.js";import{u as N}from"./useApiQuery-7KXAW9hB.js";import{P as w,a as d}from"./PageSection-BT-depe5.js";import"./Input-CkTJl-fF.js";import{B as b}from"./Button-CgdNhVeu.js";import{I as y}from"./info-DAG4KwRm.js";/**
1
+ import{c as p,u as v,a as k,r,j as e,d as o,U as j,m as i}from"./index-DP6DzFEd.js";import{u as N}from"./useApiQuery-C5jmZPyb.js";import{P as w,a as d}from"./PageSection-Bq4tdag3.js";import"./Input-B6cOxhbI.js";import{B as b}from"./Button-BkhovQFd.js";import{I as y}from"./info-CADQNr0Q.js";/**
2
2
  * @license lucide-react v0.344.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -8,4 +8,4 @@ import{c as p,u as v,a as k,r,j as e,d as o,U as j,m as i}from"./index-3UByifOG.
8
8
  *
9
9
  * This source code is licensed under the ISC license.
10
10
  * See the LICENSE file in the root directory of this source tree.
11
- */const E=p("Sparkles",[["path",{d:"m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z",key:"17u4zn"}],["path",{d:"M5 3v4",key:"bklmnn"}],["path",{d:"M19 17v4",key:"iiml17"}],["path",{d:"M3 5h4",key:"nem4j1"}],["path",{d:"M17 19h4",key:"lbex7p"}]]),I="0.5.1",_={version:I},L={VITE_BUILD_TIME:"2025-10-31T08:47:42.611Z",VITE_NODE_VERSION:"v22.14.0"};function m({items:t}){return t.length===0?null:e.jsx("dl",{className:"grid gap-4 sm:grid-cols-2 xl:grid-cols-2",children:t.map(s=>e.jsxs("div",{className:"rounded-2xl border border-slate-200/50 bg-white p-4 shadow-sm shadow-slate-200/30 transition-all duration-200 hover:-translate-y-0.5 hover:border-slate-200/70 hover:shadow-md hover:shadow-slate-200/40 dark:border-slate-700/50 dark:bg-slate-900/80 dark:shadow-lg dark:shadow-slate-900/30 dark:hover:border-slate-600/70",children:[e.jsx("dt",{className:"text-xs font-semibold uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400",children:s.label}),e.jsx("dd",{className:"mt-2 text-base font-semibold text-slate-900 dark:text-slate-100",children:s.value}),s.hint?e.jsx("p",{className:o(i,"mt-2 text-xs leading-relaxed"),children:s.hint}):null]},s.label))})}function P(){const{t}=v(),{pushToast:s}=k(),a=N(["status","gateway"],{url:"/api/status",method:"GET"},{staleTime:6e4});r.useEffect(()=>{a.isError&&a.error&&s({title:t("about.toast.statusError.title"),description:a.error.message,variant:"error"})},[a.isError,a.error,s,t]);const n=_.version,l=r.useMemo(()=>{const u=L,f=u.VITE_BUILD_TIME,g=u.VITE_NODE_VERSION;return{buildTime:f,nodeVersion:g}},[]),h=r.useMemo(()=>[{label:t("about.app.labels.name"),value:e.jsx("span",{className:"font-mono text-sm font-semibold text-slate-900 dark:text-slate-100",children:"cc-gw"})},{label:t("about.app.labels.version"),value:e.jsxs("span",{className:"font-mono text-sm font-semibold text-blue-700 dark:text-blue-200",children:["v",n]})},{label:t("about.app.labels.buildTime"),value:l.buildTime,hint:t("about.app.hint.buildTime")},{label:t("about.app.labels.node"),value:e.jsx("span",{className:"font-mono text-sm text-slate-800 dark:text-slate-200",children:l.nodeVersion})}],[n,l.buildTime,l.nodeVersion,t]),c=r.useMemo(()=>a.data?[{label:t("about.status.labels.host"),value:a.data.host??"127.0.0.1"},{label:t("about.status.labels.port"),value:a.data.port.toLocaleString()},{label:t("about.status.labels.providers"),value:a.data.providers.toLocaleString()},{label:t("about.status.labels.active"),value:(a.data.activeRequests??0).toLocaleString(),hint:t("about.status.hint.active")}]:[],[a.data,t]),x=()=>{s({title:t("about.toast.updatesPlanned"),variant:"info"})};return e.jsxs("div",{className:"space-y-8",children:[e.jsx(w,{icon:e.jsx(y,{className:"h-6 w-6","aria-hidden":"true"}),title:t("about.title"),description:t("about.description"),badge:`v${n}`,actions:e.jsx(b,{variant:"primary",icon:e.jsx(E,{className:"h-4 w-4","aria-hidden":"true"}),onClick:x,children:t("about.support.actions.checkUpdates")})}),e.jsxs("div",{className:"grid gap-6 lg:grid-cols-2",children:[e.jsx(d,{title:t("about.app.title"),description:t("about.app.subtitle"),className:"h-full",contentClassName:"gap-4",children:e.jsx(m,{items:h})}),e.jsx(d,{title:t("about.status.title"),description:t("about.status.subtitle"),className:"h-full",contentClassName:"gap-4",actions:e.jsx(b,{variant:"subtle",size:"sm",icon:e.jsx(T,{className:"h-4 w-4","aria-hidden":"true"}),onClick:()=>a.refetch(),loading:a.isFetching,children:a.isFetching?t("common.actions.refreshing"):t("common.actions.refresh")}),children:a.isLoading?e.jsxs("div",{className:"flex h-36 flex-col items-center justify-center gap-3 text-center",children:[e.jsx("div",{className:"h-10 w-10 animate-spin rounded-full border-[3px] border-blue-500/30 border-t-blue-600 dark:border-blue-400/20 dark:border-t-blue-300"}),e.jsx("p",{className:o(i,"text-sm"),children:t("about.status.loading")})]}):c.length>0?e.jsx(m,{items:c}):e.jsxs("div",{className:"flex h-36 flex-col items-center justify-center gap-2 rounded-2xl border border-dashed border-slate-200/60 bg-white p-6 text-center shadow-inner dark:border-slate-700/60 dark:bg-slate-900/60",children:[e.jsx("p",{className:"text-sm font-semibold text-slate-700 dark:text-slate-200",children:t("about.status.empty")}),e.jsx("p",{className:o(i,"text-xs"),children:t("common.actions.refresh")})]})})]}),e.jsx(d,{title:t("about.support.title"),description:e.jsxs("span",{className:"space-y-1",children:[e.jsx("span",{className:"block text-sm font-semibold text-blue-600 dark:text-blue-300",children:t("about.support.subtitle")}),e.jsx("span",{children:t("about.support.description")})]}),className:"relative overflow-hidden",contentClassName:"gap-6",children:e.jsxs("div",{className:"flex flex-col gap-4 rounded-3xl border border-slate-200/50 bg-white p-6 shadow-lg shadow-slate-200/30 backdrop-blur-md dark:border-slate-700/50 dark:bg-slate-900/80 dark:shadow-slate-900/40",children:[e.jsxs("div",{className:"flex flex-wrap items-start gap-4",children:[e.jsx("div",{className:"grid h-12 w-12 place-items-center rounded-2xl bg-gradient-to-br from-blue-500/20 to-indigo-500/20 text-blue-600 shadow-inner dark:text-blue-200",children:e.jsx(j,{className:"h-6 w-6","aria-hidden":"true"})}),e.jsx("p",{className:o(i,"text-sm leading-6"),children:t("about.support.tip")})]}),e.jsx("code",{className:"inline-flex items-center gap-2 self-start rounded-full border border-blue-200/50 bg-blue-50/80 px-4 py-2 text-xs font-semibold tracking-wide text-blue-700 shadow-sm dark:border-blue-500/30 dark:bg-blue-900/30 dark:text-blue-200",children:"~/.cc-gw/config.json"})]})})]})}export{P as default};
11
+ */const E=p("Sparkles",[["path",{d:"m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z",key:"17u4zn"}],["path",{d:"M5 3v4",key:"bklmnn"}],["path",{d:"M19 17v4",key:"iiml17"}],["path",{d:"M3 5h4",key:"nem4j1"}],["path",{d:"M17 19h4",key:"lbex7p"}]]),I="0.5.2",_={version:I},L={VITE_BUILD_TIME:"2025-11-02T02:16:57.696Z",VITE_NODE_VERSION:"v22.16.0"};function m({items:t}){return t.length===0?null:e.jsx("dl",{className:"grid gap-4 sm:grid-cols-2 xl:grid-cols-2",children:t.map(s=>e.jsxs("div",{className:"rounded-2xl border border-slate-200/50 bg-white p-4 shadow-sm shadow-slate-200/30 transition-all duration-200 hover:-translate-y-0.5 hover:border-slate-200/70 hover:shadow-md hover:shadow-slate-200/40 dark:border-slate-700/50 dark:bg-slate-900/80 dark:shadow-lg dark:shadow-slate-900/30 dark:hover:border-slate-600/70",children:[e.jsx("dt",{className:"text-xs font-semibold uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400",children:s.label}),e.jsx("dd",{className:"mt-2 text-base font-semibold text-slate-900 dark:text-slate-100",children:s.value}),s.hint?e.jsx("p",{className:o(i,"mt-2 text-xs leading-relaxed"),children:s.hint}):null]},s.label))})}function P(){const{t}=v(),{pushToast:s}=k(),a=N(["status","gateway"],{url:"/api/status",method:"GET"},{staleTime:6e4});r.useEffect(()=>{a.isError&&a.error&&s({title:t("about.toast.statusError.title"),description:a.error.message,variant:"error"})},[a.isError,a.error,s,t]);const n=_.version,l=r.useMemo(()=>{const u=L,f=u.VITE_BUILD_TIME,g=u.VITE_NODE_VERSION;return{buildTime:f,nodeVersion:g}},[]),h=r.useMemo(()=>[{label:t("about.app.labels.name"),value:e.jsx("span",{className:"font-mono text-sm font-semibold text-slate-900 dark:text-slate-100",children:"cc-gw"})},{label:t("about.app.labels.version"),value:e.jsxs("span",{className:"font-mono text-sm font-semibold text-blue-700 dark:text-blue-200",children:["v",n]})},{label:t("about.app.labels.buildTime"),value:l.buildTime,hint:t("about.app.hint.buildTime")},{label:t("about.app.labels.node"),value:e.jsx("span",{className:"font-mono text-sm text-slate-800 dark:text-slate-200",children:l.nodeVersion})}],[n,l.buildTime,l.nodeVersion,t]),c=r.useMemo(()=>a.data?[{label:t("about.status.labels.host"),value:a.data.host??"127.0.0.1"},{label:t("about.status.labels.port"),value:a.data.port.toLocaleString()},{label:t("about.status.labels.providers"),value:a.data.providers.toLocaleString()},{label:t("about.status.labels.active"),value:(a.data.activeRequests??0).toLocaleString(),hint:t("about.status.hint.active")}]:[],[a.data,t]),x=()=>{s({title:t("about.toast.updatesPlanned"),variant:"info"})};return e.jsxs("div",{className:"space-y-8",children:[e.jsx(w,{icon:e.jsx(y,{className:"h-6 w-6","aria-hidden":"true"}),title:t("about.title"),description:t("about.description"),badge:`v${n}`,actions:e.jsx(b,{variant:"primary",icon:e.jsx(E,{className:"h-4 w-4","aria-hidden":"true"}),onClick:x,children:t("about.support.actions.checkUpdates")})}),e.jsxs("div",{className:"grid gap-6 lg:grid-cols-2",children:[e.jsx(d,{title:t("about.app.title"),description:t("about.app.subtitle"),className:"h-full",contentClassName:"gap-4",children:e.jsx(m,{items:h})}),e.jsx(d,{title:t("about.status.title"),description:t("about.status.subtitle"),className:"h-full",contentClassName:"gap-4",actions:e.jsx(b,{variant:"subtle",size:"sm",icon:e.jsx(T,{className:"h-4 w-4","aria-hidden":"true"}),onClick:()=>a.refetch(),loading:a.isFetching,children:a.isFetching?t("common.actions.refreshing"):t("common.actions.refresh")}),children:a.isLoading?e.jsxs("div",{className:"flex h-36 flex-col items-center justify-center gap-3 text-center",children:[e.jsx("div",{className:"h-10 w-10 animate-spin rounded-full border-[3px] border-blue-500/30 border-t-blue-600 dark:border-blue-400/20 dark:border-t-blue-300"}),e.jsx("p",{className:o(i,"text-sm"),children:t("about.status.loading")})]}):c.length>0?e.jsx(m,{items:c}):e.jsxs("div",{className:"flex h-36 flex-col items-center justify-center gap-2 rounded-2xl border border-dashed border-slate-200/60 bg-white p-6 text-center shadow-inner dark:border-slate-700/60 dark:bg-slate-900/60",children:[e.jsx("p",{className:"text-sm font-semibold text-slate-700 dark:text-slate-200",children:t("about.status.empty")}),e.jsx("p",{className:o(i,"text-xs"),children:t("common.actions.refresh")})]})})]}),e.jsx(d,{title:t("about.support.title"),description:e.jsxs("span",{className:"space-y-1",children:[e.jsx("span",{className:"block text-sm font-semibold text-blue-600 dark:text-blue-300",children:t("about.support.subtitle")}),e.jsx("span",{children:t("about.support.description")})]}),className:"relative overflow-hidden",contentClassName:"gap-6",children:e.jsxs("div",{className:"flex flex-col gap-4 rounded-3xl border border-slate-200/50 bg-white p-6 shadow-lg shadow-slate-200/30 backdrop-blur-md dark:border-slate-700/50 dark:bg-slate-900/80 dark:shadow-slate-900/40",children:[e.jsxs("div",{className:"flex flex-wrap items-start gap-4",children:[e.jsx("div",{className:"grid h-12 w-12 place-items-center rounded-2xl bg-gradient-to-br from-blue-500/20 to-indigo-500/20 text-blue-600 shadow-inner dark:text-blue-200",children:e.jsx(j,{className:"h-6 w-6","aria-hidden":"true"})}),e.jsx("p",{className:o(i,"text-sm leading-6"),children:t("about.support.tip")})]}),e.jsx("code",{className:"inline-flex items-center gap-2 self-start rounded-full border border-blue-200/50 bg-blue-50/80 px-4 py-2 text-xs font-semibold tracking-wide text-blue-700 shadow-sm dark:border-blue-500/30 dark:bg-blue-900/30 dark:text-blue-200",children:"~/.cc-gw/config.json"})]})})]})}export{P as default};
@@ -1,4 +1,4 @@
1
- import{c as T,u as W,a as J,r as d,j as e,L as X,N as Y,d as t,b as K,m as r,H as m,E as Z,k as ee,l as ae,f as se,h as te}from"./index-3UByifOG.js";import{E as le}from"./index-DVBy-Kka.js";import{u as w}from"./useApiQuery-7KXAW9hB.js";import{P as ie,a as q}from"./PageSection-BT-depe5.js";import{F as I}from"./FormField-DJjnoGU0.js";import{I as re}from"./Input-CkTJl-fF.js";import{B as C}from"./Button-CgdNhVeu.js";import{S as ne}from"./StatusBadge-CgBAXD_S.js";import{C as ce}from"./copy-OD9YQ85H.js";/**
1
+ import{c as T,u as W,a as J,r as d,j as e,L as X,N as Y,d as t,b as K,m as r,H as m,E as Z,k as ee,l as ae,f as se,h as te}from"./index-DP6DzFEd.js";import{E as le}from"./index-BLBh7aj6.js";import{u as w}from"./useApiQuery-C5jmZPyb.js";import{P as ie,a as q}from"./PageSection-Bq4tdag3.js";import{F as I}from"./FormField-C-bAE13W.js";import{I as re}from"./Input-B6cOxhbI.js";import{B as C}from"./Button-BkhovQFd.js";import{S as ne}from"./StatusBadge-P00M_NBZ.js";import{C as ce}from"./copy-D321KBhI.js";/**
2
2
  * @license lucide-react v0.344.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{j as s,d as c,J as i,E as p,k as b}from"./index-3UByifOG.js";function h({variant:n="subtle",size:o="md",children:a,loading:t,icon:e,className:l,disabled:r,...u}){const m={subtle:b,primary:p,danger:i}[n],x={sm:"h-8 px-3 text-xs",md:"h-10 px-4 text-sm",lg:"h-12 px-6 text-base"}[o];return s.jsx("button",{className:c(m,x,t&&"cursor-wait opacity-70",r&&"cursor-not-allowed opacity-50",l),disabled:r||t,...u,children:t?s.jsx("div",{className:"inline-block animate-spin rounded-full h-4 w-4 border-b-2 border-current"}):e?s.jsxs(s.Fragment,{children:[e,a]}):a})}export{h as B};
1
+ import{j as s,d as c,J as i,E as p,k as b}from"./index-DP6DzFEd.js";function h({variant:n="subtle",size:o="md",children:a,loading:t,icon:e,className:l,disabled:r,...u}){const m={subtle:b,primary:p,danger:i}[n],x={sm:"h-8 px-3 text-xs",md:"h-10 px-4 text-sm",lg:"h-12 px-6 text-base"}[o];return s.jsx("button",{className:c(m,x,t&&"cursor-wait opacity-70",r&&"cursor-not-allowed opacity-50",l),disabled:r||t,...u,children:t?s.jsx("div",{className:"inline-block animate-spin rounded-full h-4 w-4 border-b-2 border-current"}):e?s.jsxs(s.Fragment,{children:[e,a]}):a})}export{h as B};
@@ -1,4 +1,4 @@
1
- import{c as O,u as q,a as te,r as h,b as se,t as ae,j as e,L as re,B,d as o,m as g,g as z,e as $,f as G,h as oe,l as _,s as le,i as M,k as ie,n as P}from"./index-3UByifOG.js";import{E as ne}from"./index-DVBy-Kka.js";import{P as de,a as H}from"./PageSection-BT-depe5.js";import{S as ce}from"./Input-CkTJl-fF.js";import{S as me}from"./StatusBadge-CgBAXD_S.js";import{u as C}from"./useApiQuery-7KXAW9hB.js";/**
1
+ import{c as O,u as q,a as te,r as h,b as se,t as ae,j as e,L as re,B,d as o,m as g,g as z,e as $,f as G,h as oe,l as _,s as le,i as M,k as ie,n as P}from"./index-DP6DzFEd.js";import{E as ne}from"./index-BLBh7aj6.js";import{P as de,a as H}from"./PageSection-Bq4tdag3.js";import{S as ce}from"./Input-B6cOxhbI.js";import{S as me}from"./StatusBadge-P00M_NBZ.js";import{u as C}from"./useApiQuery-C5jmZPyb.js";/**
2
2
  * @license lucide-react v0.344.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{j as s,d,P as m,Q as x}from"./index-3UByifOG.js";function i({label:a,children:l,className:t,required:r,error:e}){return s.jsxs("div",{className:d(x,t),children:[s.jsxs("label",{className:m,children:[a,r&&s.jsx("span",{className:"text-red-500 ml-1",children:"*"})]}),l,e&&s.jsx("p",{className:"text-xs text-red-600 dark:text-red-400 mt-1",children:e})]})}export{i as F};
1
+ import{j as s,d,P as m,Q as x}from"./index-DP6DzFEd.js";function i({label:a,children:l,className:t,required:r,error:e}){return s.jsxs("div",{className:d(x,t),children:[s.jsxs("label",{className:m,children:[a,r&&s.jsx("span",{className:"text-red-500 ml-1",children:"*"})]}),l,e&&s.jsx("p",{className:"text-xs text-red-600 dark:text-red-400 mt-1",children:e})]})}export{i as F};
@@ -1,4 +1,4 @@
1
- import{c as u,u as p,r as b,j as e,d as x,m}from"./index-3UByifOG.js";import{P as g,a as o}from"./PageSection-BT-depe5.js";import{I as j}from"./info-DAG4KwRm.js";/**
1
+ import{c as u,u as p,r as b,j as e,d as x,m}from"./index-DP6DzFEd.js";import{P as g,a as o}from"./PageSection-Bq4tdag3.js";import{I as j}from"./info-CADQNr0Q.js";/**
2
2
  * @license lucide-react v0.344.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r as d,j as e,d as n,M as c,K as i}from"./index-3UByifOG.js";const f=d.forwardRef(({options:t,placeholder:s,className:r,...l},u)=>e.jsxs("select",{ref:u,className:n(c,r),...l,children:[s&&e.jsx("option",{value:"",disabled:!0,children:s}),t.map(a=>e.jsx("option",{value:a.value,disabled:a.disabled,children:a.label},a.value))]})),j=d.forwardRef(({variant:t="default",className:s,...r},l)=>e.jsx("input",{ref:l,className:n(i,s),...r}));export{j as I,f as S};
1
+ import{r as d,j as e,d as n,M as c,K as i}from"./index-DP6DzFEd.js";const f=d.forwardRef(({options:t,placeholder:s,className:r,...l},u)=>e.jsxs("select",{ref:u,className:n(c,r),...l,children:[s&&e.jsx("option",{value:"",disabled:!0,children:s}),t.map(a=>e.jsx("option",{value:a.value,disabled:a.disabled,children:a.label},a.value))]})),j=d.forwardRef(({variant:t="default",className:s,...r},l)=>e.jsx("input",{ref:l,className:n(i,s),...r}));export{j as I,f as S};