@chenpu17/cc-gw 0.3.0 → 0.3.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.
package/README.md CHANGED
@@ -28,7 +28,7 @@ cc-gw start --daemon --port 4100
28
28
 
29
29
  首启会在 `~/.cc-gw/config.json` 生成配置模板,推荐直接通过 Web UI (`http://127.0.0.1:4100/ui`) 完成所有后续配置与调整。`cc-gw status`、`cc-gw stop`、`cc-gw restart` 可用于日常运维。
30
30
 
31
- > ⚠️ **Linux 安装提示**:如果未命中 sqlite3 的预编译二进制,需要系统具备 `build-essential`、`python3`、`python3-gyp` 等编译依赖。可执行 `sudo apt install build-essential python3 python3-gyp` 后,再运行 `npm install -g @chenpu17/cc-gw --unsafe-perm --build-from-source`。
31
+ > ⚠️ **Linux 安装提示**:本项目依赖 `better-sqlite3`;该库已为 Node 20/22/24 glibc/musl(x64/arm64/arm)提供预编译二进制,通常无需额外工具。如果你的环境未命中预编译(例如更早版本的 Node 或稀有架构),请先安装 `build-essential python3 make g++`,再运行 `npm install -g @chenpu17/cc-gw --unsafe-perm --build-from-source`。
32
32
 
33
33
  ### 从源码构建(开发者)
34
34
 
@@ -67,10 +67,17 @@ UI 支持中英文、深色/浅色主题以及移动端响应式布局,提供
67
67
  1. 启动 cc-gw 并确认配置中 `host` 为 `127.0.0.1`,`port` 与 CLI 启动一致。
68
68
  2. 在安装了 Claude Code 的终端设置环境变量:
69
69
  ```bash
70
- export ANTHROPIC_BASE_URL=http://127.0.0.1:4100
70
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:4100/anthropic
71
71
  claude "help me review this file"
72
72
  ```
73
- 3. cc-gw 会根据 `modelRoutes`/默认策略将 Claude 请求路由到已配置的目标模型(如 Kimi、火山 DeepSeek、OpenAI 或自建模型)。
73
+ 3. 如果使用 VS Code 插件(Claude Code),在“自定义 API”中同样填写 `http://127.0.0.1:4100/anthropic`,插件会自动追加 `/v1/messages` 与 `?beta=true`,最后粘贴 cc-gw Web UI CLI 创建的 API Key。
74
+ 4. cc-gw 会根据 `modelRoutes`/默认策略将 Claude 请求路由到已配置的目标模型(如 Kimi、火山 DeepSeek、OpenAI 或自建模型)。
75
+
76
+ ### 连接 Codex(原 Claude Code for Repo)
77
+ 1. 在 Web UI 的“模型管理 → 路由配置”中为 `/openai` 端点选择目标模型,默认会映射至配置中的 `defaults.completion`。
78
+ 2. 在 Codex 或其他需要 OpenAI 风格接口的客户端中,将 Base URL 设置为 `http://127.0.0.1:4100/openai/v1`;若需手动指定路径,请调用 `POST /openai/v1/responses`。
79
+ 3. 将 API Key 设置为 cc-gw 生成的密钥(支持 Bearer Header 或 `x-api-key` Header)。
80
+ 4. 触发一次 `hello` 或最小请求检查 Streaming 是否正常;若遇到 `Unsupported parameter: thinking` 等提示,说明 cc-gw 已自动剥离该字段并兼容上游。
74
81
 
75
82
  ### 使用场景 / Usage Scenarios
76
83
 
@@ -134,6 +141,7 @@ UI 支持中英文、深色/浅色主题以及移动端响应式布局,提供
134
141
  - `modelRoutes`:将 Claude 发起的模型名映射到上游模型;未命中时使用 `defaults`。
135
142
  - `storePayloads`:是否在 SQLite 中压缩保存原始请求/响应(Brotli),关闭后仅保留元信息。
136
143
  - `logLevel`:控制 Fastify/Pino 控制台日志级别(`fatal`/`error`/`warn`/`info`/`debug`/`trace`)。
144
+ - `providers[].authMode`:仅在 `type: "anthropic"` 时生效,可选 `apiKey`(默认,发送 `x-api-key`)或 `authToken`(发送 `Authorization: Bearer`)。配置 Claude Code 使用 `ANTHROPIC_AUTH_TOKEN` 时,请选择 `authToken` 并在 `apiKey` 输入框填入该值。
137
145
  - `requestLogging`:是否输出每个 HTTP 请求的进入日志。
138
146
  - `responseLogging`:是否输出每个 HTTP 请求完成的日志,可独立于 `requestLogging` 控制。
139
147
  - 推荐通过 Web UI 的“模型管理 / 系统设置”在线编辑并热加载,无需手工修改文件。
@@ -166,7 +174,7 @@ pnpm --filter @cc-gw/cli exec tsx index.ts status
166
174
 
167
175
  ## 数据与日志
168
176
 
169
- - 数据库:`~/.cc-gw/data/gateway.db`(`sqlite3`)。
177
+ - 数据库:`~/.cc-gw/data/gateway.db`(`better-sqlite3` 管理的嵌入式 SQLite)。
170
178
  - `request_logs`:请求摘要、路由结果、耗时、TTFT/TPOT。
171
179
  - `request_payloads`:压缩的请求/响应正文(Brotli)。
172
180
  - `daily_metrics`:按日聚合的调用次数与 Token 统计。
@@ -190,7 +198,7 @@ cc-gw is a local gateway tailored for Claude Code and similar Anthropic-compatib
190
198
  | ------- | ------- |
191
199
  | Protocol adaptation | Converts Claude-style payloads into OpenAI-, Anthropic-, Kimi-, and DeepSeek-compatible requests while preserving tool calls and reasoning blocks. |
192
200
  | Model routing | Maps incoming model IDs to configured upstream providers with fallbacks for long-context and background tasks. |
193
- | Observability | Persists request logs, token usage (including cache hits), TTFT, TPOT, and daily aggregates in SQLite with Brotli-compressed payloads. |
201
+ | Observability | Persists request logs, token usage (including cache hits), TTFT, TPOT, and daily aggregates via better-sqlite3 with Brotli-compressed payloads. |
194
202
  | Web console | React + Vite UI with dashboards, filters, provider CRUD, bilingual copy, and responsive layout. |
195
203
  | CLI daemon | `cc-gw` command wraps start/stop/restart/status, manages PID/log files, and scaffolds a default config on first launch. |
196
204
 
@@ -203,7 +211,7 @@ cc-gw start --daemon --port 4100
203
211
 
204
212
  The first launch writes `~/.cc-gw/config.json`. Manage everything through the Web UI at `http://127.0.0.1:4100/ui`. Use `cc-gw status`, `cc-gw stop`, and `cc-gw restart` to control the daemon.
205
213
 
206
- > ⚠️ **Linux build note**: `sqlite3` may fall back to building from source. Install toolchain packages such as `build-essential`, `python3`, `python3-gyp`, then rerun `npm install -g @chenpu17/cc-gw --unsafe-perm --build-from-source` if needed.
214
+ > ⚠️ **Linux build note**: We now depend on `better-sqlite3`. Prebuilt binaries ship for Node 20/22/24 on glibc & musl (x64/arm64/arm). If you’re targeting an unsupported combo, install `build-essential python3 make g++` first, then rerun `npm install -g @chenpu17/cc-gw --unsafe-perm --build-from-source`.
207
215
 
208
216
  ### From Source (contributors)
209
217
 
@@ -214,13 +222,24 @@ pnpm --filter @cc-gw/web build
214
222
  pnpm --filter @cc-gw/cli exec tsx index.ts start --daemon --port 4100
215
223
  ```
216
224
 
217
- Connect Claude Code by pointing `ANTHROPIC_BASE_URL` to your local gateway:
225
+ Connect Claude Code by pointing `ANTHROPIC_BASE_URL` to the Anthropic namespace on cc-gw. Both the CLI and editor clients append `/v1/messages` automatically:
218
226
 
219
227
  ```bash
220
- export ANTHROPIC_BASE_URL=http://127.0.0.1:4100
228
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:4100/anthropic
221
229
  claude "help me review this file"
222
230
  ```
223
231
 
232
+ Using the Claude Code VS Code extension? Open the extension settings, enable the custom API mode, set the Base URL to the same `http://127.0.0.1:4100/anthropic`, and paste an API key generated from the cc-gw Web UI or CLI—the extension appends `/v1/messages?beta=true` automatically and cc-gw now forwards the query string upstream.
233
+
234
+ Connect Codex (or any OpenAI-compatible IDE integration) by targeting the OpenAI endpoint exposed by cc-gw:
235
+
236
+ ```bash
237
+ export OPENAI_BASE_URL=http://127.0.0.1:4100/openai/v1
238
+ export OPENAI_API_KEY="<your cc-gw api key>"
239
+ ```
240
+
241
+ If the client expects a full path, call `POST /openai/v1/responses`. The gateway strips unsupported fields (such as `thinking`) before forwarding to the upstream provider, so health checks like `hello` should stream back correctly.
242
+
224
243
  ### Configuration Snapshot
225
244
 
226
245
  - Providers include `type`, `baseUrl`, `apiKey`, and `models` descriptions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenpu17/cc-gw",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -51,16 +51,16 @@
51
51
  ],
52
52
  "repository": {
53
53
  "type": "git",
54
- "url": "https://github.com/chenpu17/cc-gw.git"
54
+ "url": "git+https://github.com/chenpu17/cc-gw.git"
55
55
  },
56
56
  "dependencies": {
57
57
  "@fastify/cors": "^9.0.1",
58
58
  "@fastify/static": "^7.0.4",
59
+ "better-sqlite3": "^12.4.1",
59
60
  "colorette": "^2.0.20",
60
61
  "commander": "^12.0.0",
61
62
  "fastify": "^4.26.2",
62
63
  "open": "^10.1.0",
63
- "sqlite3": "^5.1.7",
64
64
  "tiktoken": "^1.0.21",
65
65
  "undici": "^6.11.1"
66
66
  },
@@ -68,7 +68,6 @@
68
68
  "@eslint/js": "^9.5.0",
69
69
  "@playwright/test": "^1.55.1",
70
70
  "@types/node": "^20.12.7",
71
- "@types/sqlite3": "^3.1.11",
72
71
  "eslint": "^8.57.0",
73
72
  "globals": "^15.0.0",
74
73
  "prettier": "^3.2.5",
@@ -91,6 +91,16 @@ function parseConfig(raw) {
91
91
  if (!Array.isArray(data.providers)) {
92
92
  data.providers = [];
93
93
  }
94
+ data.providers = data.providers.map((provider) => {
95
+ if (!provider || typeof provider !== "object")
96
+ return provider;
97
+ if (provider.type === "anthropic") {
98
+ provider.authMode = provider.authMode === "authToken" ? "authToken" : "apiKey";
99
+ } else if ("authMode" in provider) {
100
+ delete provider.authMode;
101
+ }
102
+ return provider;
103
+ });
94
104
  const legacyDefaults = sanitizeDefaults(data.defaults);
95
105
  if (typeof data.logRetentionDays !== "number") {
96
106
  data.logRetentionDays = 30;
@@ -572,6 +582,12 @@ function buildAnthropicBody(payload, options = {}) {
572
582
  if (typeof options.temperature === "number") {
573
583
  body.temperature = options.temperature;
574
584
  }
585
+ if (payload.original && typeof payload.original === "object") {
586
+ const original = payload.original;
587
+ if (original.metadata && typeof original.metadata === "object") {
588
+ body.metadata = original.metadata;
589
+ }
590
+ }
575
591
  const tools = options.overrideTools ?? payload.tools;
576
592
  if (tools && tools.length > 0) {
577
593
  body.tools = tools.map((tool) => ({
@@ -590,6 +606,41 @@ function buildAnthropicBody(payload, options = {}) {
590
606
  // providers/openai.ts
591
607
  import { fetch } from "undici";
592
608
  import { ReadableStream } from "stream/web";
609
+
610
+ // providers/utils.ts
611
+ function appendQuery(url, query) {
612
+ if (!query)
613
+ return url;
614
+ const baseHasQuery = url.includes("?");
615
+ if (typeof query === "string") {
616
+ const normalized = query.startsWith("?") ? query.slice(1) : query;
617
+ if (!normalized) {
618
+ return baseHasQuery ? url : `${url}?`;
619
+ }
620
+ return baseHasQuery ? `${url}&${normalized}` : `${url}?${normalized}`;
621
+ }
622
+ const params = new URLSearchParams();
623
+ for (const [key, value] of Object.entries(query)) {
624
+ if (value == null)
625
+ continue;
626
+ if (Array.isArray(value)) {
627
+ for (const item of value) {
628
+ if (item == null)
629
+ continue;
630
+ params.append(key, String(item));
631
+ }
632
+ } else {
633
+ params.append(key, String(value));
634
+ }
635
+ }
636
+ const serialized = params.toString();
637
+ if (!serialized) {
638
+ return baseHasQuery ? url : `${url}?`;
639
+ }
640
+ return baseHasQuery ? `${url}&${serialized}` : `${url}?${serialized}`;
641
+ }
642
+
643
+ // providers/openai.ts
593
644
  var encoder = new TextEncoder();
594
645
  function createJsonStream(payload) {
595
646
  const text = typeof payload === "string" ? payload : JSON.stringify(payload);
@@ -619,7 +670,7 @@ function resolveEndpoint(config, options) {
619
670
  }
620
671
  function createOpenAIConnector(config, options) {
621
672
  const url = resolveEndpoint(config, options);
622
- const shouldLogEndpoint = process.env.CC_GW_DEBUG_ENDPOINTS === "1";
673
+ const shouldLogEndpoint = process.env.CC_GW_DEBUG_ENDPOINTS === "1" || process.env.CC_GW_DEBUG_OPENAI === "1";
623
674
  return {
624
675
  id: config.id,
625
676
  async send(request) {
@@ -637,10 +688,11 @@ function createOpenAIConnector(config, options) {
637
688
  stream: request.stream ?? false
638
689
  };
639
690
  const payload = options?.mutateBody ? options.mutateBody(body) : body;
691
+ const finalUrl = appendQuery(url, request.query);
640
692
  if (shouldLogEndpoint) {
641
- console.info(`[cc-gw] provider=${config.id} endpoint=${url}`);
693
+ console.info(`[cc-gw] provider=${config.id} endpoint=${finalUrl}`);
642
694
  }
643
- const res = await fetch(url, {
695
+ const res = await fetch(finalUrl, {
644
696
  method: "POST",
645
697
  headers,
646
698
  body: JSON.stringify(payload)
@@ -749,18 +801,26 @@ import { fetch as fetch2 } from "undici";
749
801
  var DEFAULT_VERSION = "2023-06-01";
750
802
  function createAnthropicConnector(config) {
751
803
  const baseUrl = config.baseUrl.replace(/\/$/, "");
804
+ const endpoint = resolveAnthropicEndpoint(baseUrl);
805
+ const shouldLogEndpoint = process.env.CC_GW_DEBUG_ENDPOINTS === "1" || process.env.CC_GW_DEBUG_OPENAI === "1";
752
806
  return {
753
807
  id: config.id,
754
808
  async send(request) {
755
- const headers = {
756
- "Content-Type": "application/json",
809
+ const headers = normalizeHeaders({
810
+ "content-type": "application/json",
757
811
  "anthropic-version": DEFAULT_VERSION,
758
812
  ...config.extraHeaders,
759
813
  ...request.headers
760
- };
761
- delete headers.Authorization;
814
+ });
815
+ delete headers.authorization;
816
+ delete headers["x-api-key"];
762
817
  if (config.apiKey) {
763
- headers["x-api-key"] = config.apiKey;
818
+ const mode = config.authMode === "authToken" ? "authToken" : "apiKey";
819
+ if (mode === "authToken") {
820
+ headers.authorization = `Bearer ${config.apiKey}`;
821
+ } else {
822
+ headers["x-api-key"] = config.apiKey;
823
+ }
764
824
  }
765
825
  if (!headers["anthropic-version"]) {
766
826
  headers["anthropic-version"] = DEFAULT_VERSION;
@@ -770,11 +830,37 @@ function createAnthropicConnector(config) {
770
830
  model: request.model,
771
831
  stream: request.stream ?? false
772
832
  };
773
- const response = await fetch2(`${baseUrl}/messages`, {
833
+ const finalUrl = appendQuery(endpoint, request.query);
834
+ if (shouldLogEndpoint) {
835
+ console.info(`[cc-gw] provider=${config.id} endpoint=${finalUrl}`);
836
+ if (process.env.CC_GW_DEBUG_HEADERS === "1") {
837
+ const safeHeaders = {};
838
+ for (const [key, value] of Object.entries(headers)) {
839
+ if (key.toLowerCase().includes("authorization")) {
840
+ safeHeaders[key] = "<redacted>";
841
+ } else {
842
+ safeHeaders[key] = value;
843
+ }
844
+ }
845
+ console.info(`[cc-gw] provider=${config.id} headers`, safeHeaders);
846
+ try {
847
+ console.info(`[cc-gw] provider=${config.id} payload`, JSON.stringify(payload).slice(0, 500));
848
+ } catch {
849
+ console.info(`[cc-gw] provider=${config.id} payload`, "[unserializable payload]");
850
+ }
851
+ }
852
+ }
853
+ if (headers["content-length"]) {
854
+ delete headers["content-length"];
855
+ }
856
+ const response = await fetch2(finalUrl, {
774
857
  method: "POST",
775
858
  headers,
776
859
  body: JSON.stringify(payload)
777
860
  });
861
+ if (shouldLogEndpoint) {
862
+ console.info(`[cc-gw] provider=${config.id} status=${response.status}`);
863
+ }
778
864
  return {
779
865
  status: response.status,
780
866
  headers: response.headers,
@@ -783,6 +869,39 @@ function createAnthropicConnector(config) {
783
869
  }
784
870
  };
785
871
  }
872
+ function normalizeHeaders(source) {
873
+ const result = {};
874
+ for (const [key, value] of Object.entries(source)) {
875
+ if (value == null)
876
+ continue;
877
+ const normalizedKey = key.toLowerCase();
878
+ if (Array.isArray(value)) {
879
+ const candidate = value.find((item) => item != null);
880
+ if (candidate != null) {
881
+ result[normalizedKey] = String(candidate);
882
+ }
883
+ } else {
884
+ result[normalizedKey] = String(value);
885
+ }
886
+ }
887
+ return result;
888
+ }
889
+ function resolveAnthropicEndpoint(baseUrl) {
890
+ const normalized = baseUrl.replace(/\/$/, "");
891
+ if (normalized.endsWith("/messages") || normalized.match(/\/v\d+\/messages$/)) {
892
+ return normalized;
893
+ }
894
+ if (normalized.match(/\/v\d+$/)) {
895
+ return `${normalized}/messages`;
896
+ }
897
+ if (normalized.endsWith("/anthropic")) {
898
+ return `${normalized}/v1/messages`;
899
+ }
900
+ if (normalized.endsWith("/anthropic/v1")) {
901
+ return `${normalized}/messages`;
902
+ }
903
+ return `${normalized}/v1/messages`;
904
+ }
786
905
 
787
906
  // providers/registry.ts
788
907
  var connectors = /* @__PURE__ */ new Map();
@@ -823,72 +942,32 @@ import { brotliCompressSync, brotliDecompressSync, constants as zlibConstants }
823
942
  import fs2 from "fs";
824
943
  import os2 from "os";
825
944
  import path2 from "path";
826
- import sqlite3 from "sqlite3";
945
+ import Database from "better-sqlite3";
827
946
  var HOME_OVERRIDE2 = process.env.CC_GW_HOME;
828
947
  var HOME_DIR2 = path2.resolve(HOME_OVERRIDE2 ?? path2.join(os2.homedir(), ".cc-gw"));
829
948
  var DATA_DIR = path2.join(HOME_DIR2, "data");
830
949
  var DB_PATH = path2.join(DATA_DIR, "gateway.db");
831
- sqlite3.verbose();
832
950
  var dbPromise = null;
833
951
  var dbInstance = null;
834
952
  function exec(db, sql) {
835
- return new Promise((resolve, reject) => {
836
- db.exec(sql, (error) => {
837
- if (error) {
838
- reject(error);
839
- return;
840
- }
841
- resolve();
842
- });
843
- });
953
+ db.exec(sql);
954
+ return Promise.resolve();
844
955
  }
845
956
  function run(db, sql, params = []) {
846
- return new Promise((resolve, reject) => {
847
- const handler = function(error) {
848
- if (error) {
849
- reject(error);
850
- return;
851
- }
852
- resolve({ lastID: this.lastID, changes: this.changes });
853
- };
854
- if (Array.isArray(params)) {
855
- db.run(sql, params, handler);
856
- } else {
857
- db.run(sql, params, handler);
858
- }
859
- });
957
+ const statement = db.prepare(sql);
958
+ const result = Array.isArray(params) ? statement.run(...params) : statement.run(params);
959
+ const lastID = typeof result.lastInsertRowid === "bigint" ? Number(result.lastInsertRowid) : result.lastInsertRowid;
960
+ return Promise.resolve({ lastID, changes: result.changes });
860
961
  }
861
962
  function all(db, sql, params = []) {
862
- return new Promise((resolve, reject) => {
863
- const callback = (error, rows) => {
864
- if (error) {
865
- reject(error);
866
- return;
867
- }
868
- resolve(rows);
869
- };
870
- if (Array.isArray(params)) {
871
- db.all(sql, params, callback);
872
- } else {
873
- db.all(sql, params, callback);
874
- }
875
- });
963
+ const statement = db.prepare(sql);
964
+ const rows = Array.isArray(params) ? statement.all(...params) : statement.all(params);
965
+ return Promise.resolve(rows);
876
966
  }
877
967
  function get(db, sql, params = []) {
878
- return new Promise((resolve, reject) => {
879
- const callback = (error, row) => {
880
- if (error) {
881
- reject(error);
882
- return;
883
- }
884
- resolve(row);
885
- };
886
- if (Array.isArray(params)) {
887
- db.get(sql, params, callback);
888
- } else {
889
- db.get(sql, params, callback);
890
- }
891
- });
968
+ const statement = db.prepare(sql);
969
+ const row = Array.isArray(params) ? statement.get(...params) : statement.get(params);
970
+ return Promise.resolve(row);
892
971
  }
893
972
  async function columnExists(db, table, column) {
894
973
  const rows = await all(db, `PRAGMA table_info(${table})`);
@@ -1053,20 +1132,18 @@ async function getDb() {
1053
1132
  }
1054
1133
  if (!dbPromise) {
1055
1134
  fs2.mkdirSync(DATA_DIR, { recursive: true });
1056
- dbPromise = new Promise((resolve, reject) => {
1057
- const instance = new sqlite3.Database(DB_PATH, (error) => {
1058
- if (error) {
1059
- reject(error);
1060
- return;
1061
- }
1062
- ensureSchema(instance).then(() => {
1063
- dbInstance = instance;
1064
- resolve(instance);
1065
- }).catch((schemaError) => {
1066
- instance.close(() => reject(schemaError));
1067
- });
1068
- });
1069
- });
1135
+ dbPromise = (async () => {
1136
+ const instance = new Database(DB_PATH);
1137
+ instance.pragma("journal_mode = WAL");
1138
+ try {
1139
+ await ensureSchema(instance);
1140
+ dbInstance = instance;
1141
+ return instance;
1142
+ } catch (error) {
1143
+ instance.close();
1144
+ throw error;
1145
+ }
1146
+ })();
1070
1147
  }
1071
1148
  return dbPromise;
1072
1149
  }
@@ -1808,13 +1885,18 @@ async function registerMessagesRoute(app) {
1808
1885
  };
1809
1886
  const rawUrl = typeof request.raw?.url === "string" ? request.raw.url : request.url ?? "";
1810
1887
  let querySuffix = null;
1811
- if (typeof rawUrl === "string" && rawUrl.includes("?")) {
1812
- querySuffix = rawUrl.slice(rawUrl.indexOf("?"));
1813
- } else if (typeof request.querystring === "string" && request.querystring.length > 0) {
1814
- querySuffix = `?${request.querystring}`;
1888
+ if (typeof rawUrl === "string") {
1889
+ const questionIndex = rawUrl.indexOf("?");
1890
+ if (questionIndex !== -1) {
1891
+ querySuffix = rawUrl.slice(questionIndex + 1);
1892
+ }
1815
1893
  }
1816
- if (querySuffix) {
1817
- console.info(`[cc-gw] inbound url ${rawUrl} query ${querySuffix}`);
1894
+ if (!querySuffix && typeof request.querystring === "string") {
1895
+ querySuffix = request.querystring || null;
1896
+ }
1897
+ if (querySuffix !== null) {
1898
+ const displaySuffix = querySuffix.length > 0 ? `?${querySuffix}` : "?";
1899
+ console.info(`[cc-gw] inbound url ${rawUrl} query ${displaySuffix}`);
1818
1900
  }
1819
1901
  const normalized = normalizeClaudePayload(payload);
1820
1902
  const requestedModel = typeof payload.model === "string" ? payload.model : void 0;
@@ -2472,6 +2554,7 @@ data: ${JSON.stringify(data)}
2472
2554
  };
2473
2555
  app.post("/v1/messages", handler);
2474
2556
  app.post("/anthropic/v1/messages", handler);
2557
+ app.post("/anthropic/v1/v1/messages", handler);
2475
2558
  }
2476
2559
 
2477
2560
  // protocol/normalize-openai.ts
@@ -3742,13 +3825,19 @@ async function createServer() {
3742
3825
  }
3743
3826
  if (responseLogEnabled) {
3744
3827
  app.addHook("onResponse", (request, reply, done) => {
3828
+ let elapsedTime;
3829
+ if (typeof reply.elapsedTime === "number") {
3830
+ elapsedTime = reply.elapsedTime;
3831
+ } else if (typeof reply.getResponseTime === "function") {
3832
+ elapsedTime = reply.getResponseTime();
3833
+ }
3745
3834
  app.log.info(
3746
3835
  {
3747
3836
  reqId: request.id,
3748
3837
  res: {
3749
3838
  statusCode: reply.statusCode
3750
3839
  },
3751
- responseTime: typeof reply.getResponseTime === "function" ? reply.getResponseTime() : void 0
3840
+ responseTime: elapsedTime
3752
3841
  },
3753
3842
  "request completed"
3754
3843
  );
@@ -1 +1 @@
1
- import{u as x,a as p,r as o,j as e}from"./index-CDJfhjXI.js";import{u as m}from"./useApiQuery-Bna2BImG.js";const h="0.3.0",v={version:h},g={};function N(){const{t:s}=x(),{pushToast:l}=p(),t=m(["status","gateway"],{url:"/api/status",method:"GET"},{staleTime:6e4});o.useEffect(()=>{t.isError&&t.error&&l({title:s("about.toast.statusError.title"),description:t.error.message,variant:"error"})},[t.isError,t.error,l,s]);const d=v.version,r=o.useMemo(()=>{const a=g,u=a.VITE_BUILD_TIME??"-",b=a.VITE_NODE_VERSION??"-";return{buildTime:u,nodeVersion:b}},[]),i=()=>{l({title:s("about.toast.updatesPlanned"),variant:"info"})},n=[{label:s("about.app.labels.name"),value:"cc-local-gw"},{label:s("about.app.labels.version"),value:d},{label:s("about.app.labels.buildTime"),value:r.buildTime},{label:s("about.app.labels.node"),value:r.nodeVersion}],c=t.data?[{label:s("about.status.labels.host"),value:t.data.host??"127.0.0.1"},{label:s("about.status.labels.port"),value:t.data.port},{label:s("about.status.labels.providers"),value:t.data.providers},{label:s("about.status.labels.active"),value:t.data.activeRequests??0}]:[];return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsxs("header",{className:"flex flex-col gap-2",children:[e.jsx("h1",{className:"text-2xl font-semibold",children:s("about.title")}),e.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:s("about.description")})]}),e.jsxs("section",{className:"grid gap-4 rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900 md:grid-cols-2",children:[e.jsxs("div",{className:"space-y-3",children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("about.app.title")}),e.jsx("dl",{className:"grid grid-cols-[160px_1fr] gap-2 text-sm text-slate-600 dark:text-slate-300",children:n.map(a=>e.jsxs("div",{className:"contents",children:[e.jsx("dt",{className:"font-medium",children:a.label}),e.jsx("dd",{children:a.value})]},a.label))})]}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("about.status.title")}),t.isLoading?e.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:s("about.status.loading")}):t.data?e.jsx("dl",{className:"grid grid-cols-[160px_1fr] gap-2 text-sm text-slate-600 dark:text-slate-300",children:c.map(a=>e.jsxs("div",{className:"contents",children:[e.jsx("dt",{className:"font-medium",children:a.label}),e.jsx("dd",{children:a.value})]},a.label))}):e.jsx("p",{className:"text-sm text-red-500",children:s("about.status.empty")})]})]}),e.jsxs("section",{className:"rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("about.support.title")}),e.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("about.support.subtitle")})]}),e.jsx("button",{type:"button",onClick:i,className:"rounded-md border border-slate-200 px-3 py-1 text-sm transition hover:bg-slate-100 dark:border-slate-700 dark:hover:bg-slate-800",children:s("about.support.actions.checkUpdates")})]}),e.jsx("p",{className:"mt-4 text-sm leading-6 text-slate-600 dark:text-slate-300",children:s("about.support.description")})]})]})}export{N as default};
1
+ import{u as x,a as p,r as o,j as e}from"./index-fNmJF3BX.js";import{u as m}from"./useApiQuery-CCi1tkCr.js";const h="0.3.2",v={version:h},g={};function N(){const{t:s}=x(),{pushToast:l}=p(),t=m(["status","gateway"],{url:"/api/status",method:"GET"},{staleTime:6e4});o.useEffect(()=>{t.isError&&t.error&&l({title:s("about.toast.statusError.title"),description:t.error.message,variant:"error"})},[t.isError,t.error,l,s]);const d=v.version,r=o.useMemo(()=>{const a=g,u=a.VITE_BUILD_TIME??"-",b=a.VITE_NODE_VERSION??"-";return{buildTime:u,nodeVersion:b}},[]),i=()=>{l({title:s("about.toast.updatesPlanned"),variant:"info"})},n=[{label:s("about.app.labels.name"),value:"cc-local-gw"},{label:s("about.app.labels.version"),value:d},{label:s("about.app.labels.buildTime"),value:r.buildTime},{label:s("about.app.labels.node"),value:r.nodeVersion}],c=t.data?[{label:s("about.status.labels.host"),value:t.data.host??"127.0.0.1"},{label:s("about.status.labels.port"),value:t.data.port},{label:s("about.status.labels.providers"),value:t.data.providers},{label:s("about.status.labels.active"),value:t.data.activeRequests??0}]:[];return e.jsxs("div",{className:"flex flex-col gap-6",children:[e.jsxs("header",{className:"flex flex-col gap-2",children:[e.jsx("h1",{className:"text-2xl font-semibold",children:s("about.title")}),e.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:s("about.description")})]}),e.jsxs("section",{className:"grid gap-4 rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900 md:grid-cols-2",children:[e.jsxs("div",{className:"space-y-3",children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("about.app.title")}),e.jsx("dl",{className:"grid grid-cols-[160px_1fr] gap-2 text-sm text-slate-600 dark:text-slate-300",children:n.map(a=>e.jsxs("div",{className:"contents",children:[e.jsx("dt",{className:"font-medium",children:a.label}),e.jsx("dd",{children:a.value})]},a.label))})]}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("about.status.title")}),t.isLoading?e.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:s("about.status.loading")}):t.data?e.jsx("dl",{className:"grid grid-cols-[160px_1fr] gap-2 text-sm text-slate-600 dark:text-slate-300",children:c.map(a=>e.jsxs("div",{className:"contents",children:[e.jsx("dt",{className:"font-medium",children:a.label}),e.jsx("dd",{children:a.value})]},a.label))}):e.jsx("p",{className:"text-sm text-red-500",children:s("about.status.empty")})]})]}),e.jsxs("section",{className:"rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("about.support.title")}),e.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("about.support.subtitle")})]}),e.jsx("button",{type:"button",onClick:i,className:"rounded-md border border-slate-200 px-3 py-1 text-sm transition hover:bg-slate-100 dark:border-slate-700 dark:hover:bg-slate-800",children:s("about.support.actions.checkUpdates")})]}),e.jsx("p",{className:"mt-4 text-sm leading-6 text-slate-600 dark:text-slate-300",children:s("about.support.description")})]})]})}export{N as default};
@@ -1,4 +1,4 @@
1
- import{c as g,u as R,a as U,r as d,j as e,L as k,K as W}from"./index-CDJfhjXI.js";import{E as L}from"./index-DG02tgGK.js";import{u as N,a as v}from"./useApiQuery-Bna2BImG.js";/**
1
+ import{c as g,u as R,a as U,r as d,j as e,L as k,K as W}from"./index-fNmJF3BX.js";import{E as L}from"./index-DWhAov2p.js";import{u as N,a as v}from"./useApiQuery-CCi1tkCr.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{u as A,a as R,r as h,j as t,L as D}from"./index-CDJfhjXI.js";import{E as O}from"./index-DG02tgGK.js";import{u as v}from"./useApiQuery-Bna2BImG.js";function N(e,a,s){return e==null?"-":`${e.toLocaleString(void 0,s)} ${a}`}function B(e){if(e==null)return"-";if(e<1024)return`${e} B`;const a=["KB","MB","GB","TB"];let s=e/1024,d=0;for(;s>=1024&&d<a.length-1;)s/=1024,d+=1;return`${s.toFixed(s>=100?0:s>=10?1:2)} ${a[d]}`}function _(){var S;const{t:e}=A(),{pushToast:a}=R(),[s,d]=h.useState("all"),l=s==="all"?void 0:s,b=v(["stats","overview",s],{url:"/api/stats/overview",method:"GET",params:l?{endpoint:l}:void 0}),p=v(["stats","daily",14,s],{url:"/api/stats/daily",method:"GET",params:{days:14,...l?{endpoint:l}:{}}}),u=v(["stats","model",7,6,s],{url:"/api/stats/model",method:"GET",params:{days:7,limit:6,...l?{endpoint:l}:{}}}),g=v(["status"],{url:"/api/status",method:"GET"}),f=v(["db","info"],{url:"/api/db/info",method:"GET"}),y=v(["logs","recent",s],{url:"/api/logs",method:"GET",params:{limit:5,...l?{endpoint:l}:{}}},{refetchInterval:3e4});h.useEffect(()=>{b.isError&&b.error&&a({title:e("dashboard.toast.overviewError"),description:b.error.message,variant:"error"})},[b.isError,b.error,a,e]),h.useEffect(()=>{p.isError&&p.error&&a({title:e("dashboard.toast.dailyError"),description:p.error.message,variant:"error"})},[p.isError,p.error,a,e]),h.useEffect(()=>{u.isError&&u.error&&a({title:e("dashboard.toast.modelError"),description:u.error.message,variant:"error"})},[u.isError,u.error,a,e]),h.useEffect(()=>{g.isError&&g.error&&a({title:e("dashboard.toast.statusError"),description:g.error.message,variant:"error"})},[g.isError,g.error,a,e]),h.useEffect(()=>{f.isError&&f.error&&a({title:e("dashboard.toast.dbError"),description:f.error.message,variant:"error"})},[f.isError,f.error,a,e]),h.useEffect(()=>{y.isError&&y.error&&a({title:e("dashboard.toast.recentError"),description:y.error.message,variant:"error"})},[y.isError,y.error,a,e]);const c=b.data,j=p.data??[],n=u.data??[],k=g.data,M=f.data,$=((S=y.data)==null?void 0:S.items)??[],q=h.useMemo(()=>{const i=j.map(o=>o.date),x=e("dashboard.charts.barRequests"),r=e("dashboard.charts.lineInput"),m=e("dashboard.charts.lineOutput");return{tooltip:{trigger:"axis"},legend:{data:[x,r,m]},grid:{left:40,right:20,top:40,bottom:40},xAxis:{type:"category",data:i},yAxis:{type:"value"},series:[{name:x,type:"bar",data:j.map(o=>o.requestCount),itemStyle:{color:"#2563eb"}},{name:r,type:"line",yAxisIndex:0,data:j.map(o=>o.inputTokens),smooth:!0,itemStyle:{color:"#22c55e"}},{name:m,type:"line",yAxisIndex:0,data:j.map(o=>o.outputTokens),smooth:!0,itemStyle:{color:"#f97316"}}]}},[j,e]),w=h.useMemo(()=>{const i=n.map(o=>`${o.provider}/${o.model}`),x=e("dashboard.charts.barRequests"),r=e("dashboard.charts.lineInput"),m=e("dashboard.charts.lineOutput");return{tooltip:{trigger:"axis"},legend:{data:[x,r,m]},grid:{left:50,right:40,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:[{type:"value",name:x},{type:"value",name:e("dashboard.charts.axisTokens"),position:"right"}],series:[{name:x,type:"bar",data:n.map(o=>o.requests),itemStyle:{color:"#6366f1"},yAxisIndex:0},{name:r,type:"line",yAxisIndex:1,smooth:!0,data:n.map(o=>o.inputTokens??0),itemStyle:{color:"#22c55e"}},{name:m,type:"line",yAxisIndex:1,smooth:!0,data:n.map(o=>o.outputTokens??0),itemStyle:{color:"#f97316"}}]}},[n,e]),I=h.useMemo(()=>{const i=n.map(r=>`${r.provider}/${r.model}`),x=e("dashboard.charts.ttftLabel");return{tooltip:{trigger:"axis",formatter(r){var T;if(!Array.isArray(r)||r.length===0)return"";const m=((T=r[0])==null?void 0:T.dataIndex)??0,o=n[m];return o?`<strong>${i[m]}</strong><br/>${x}: ${N(o.avgTtftMs,e("common.units.ms"))}`:""}},grid:{left:50,right:30,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:{type:"value",name:e("dashboard.charts.ttftAxis")},series:[{name:x,type:"bar",data:n.map(r=>r.avgTtftMs??0),itemStyle:{color:"#2563eb"}}]}},[n,e]),P=h.useMemo(()=>{const i=n.map(r=>`${r.provider}/${r.model}`),x=e("dashboard.charts.tpotLabel");return{tooltip:{trigger:"axis",formatter(r){var T;if(!Array.isArray(r)||r.length===0)return"";const m=((T=r[0])==null?void 0:T.dataIndex)??0,o=n[m];return o?`<strong>${i[m]}</strong><br/>${x}: ${N(o.avgTpotMs,e("common.units.msPerToken"),{maximumFractionDigits:2})}`:""}},grid:{left:50,right:30,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:{type:"value",name:e("dashboard.charts.tpotAxis")},series:[{name:x,type:"bar",data:n.map(r=>r.avgTpotMs??0),itemStyle:{color:"#f97316"}}]}},[n,e]);return b.isPending||g.isPending||f.isPending?t.jsx(D,{}):t.jsxs("div",{className:"flex flex-col gap-6",children:[t.jsxs("section",{className:"flex flex-col gap-2",children:[t.jsx("h1",{className:"text-2xl font-semibold",children:e("nav.dashboard")}),t.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:e("dashboard.description")}),k?t.jsxs("div",{className:"flex flex-wrap gap-3 rounded-lg border border-slate-200 bg-white p-4 text-sm shadow-sm dark:border-slate-800 dark:bg-slate-900","aria-live":"polite",children:[t.jsx("span",{className:"font-medium",children:e("dashboard.status.listening",{host:k.host??"0.0.0.0",port:k.port})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.providers",{value:k.providers})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.todayRequests",{value:((c==null?void 0:c.today.requests)??0).toLocaleString()})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.active",{value:(k.activeRequests??0).toLocaleString()})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.dbSize",{value:M?B(M.sizeBytes):"-"})})]}):null,t.jsxs("div",{className:"mt-2 flex flex-wrap items-center gap-2 text-sm text-slate-500 dark:text-slate-400",children:[t.jsx("label",{className:"text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400",children:e("dashboard.filters.endpoint")}),t.jsxs("select",{value:s,onChange:i=>d(i.target.value),className:"h-9 rounded-md border border-slate-200 bg-white px-3 text-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:focus:border-blue-400 dark:focus:ring-blue-400/40",children:[t.jsx("option",{value:"all",children:e("dashboard.filters.endpointAll")}),t.jsx("option",{value:"anthropic",children:e("dashboard.filters.endpointAnthropic")}),t.jsx("option",{value:"openai",children:e("dashboard.filters.endpointOpenAI")})]})]})]}),t.jsxs("section",{className:"grid gap-4 md:grid-cols-2 xl:grid-cols-4",children:[t.jsx(E,{title:e("dashboard.cards.todayRequests"),value:(c==null?void 0:c.today.requests)??0,suffix:e("common.units.request")}),t.jsx(E,{title:e("dashboard.cards.todayInput"),value:(c==null?void 0:c.today.inputTokens)??0,suffix:e("common.units.token")}),t.jsx(E,{title:e("dashboard.cards.todayOutput"),value:(c==null?void 0:c.today.outputTokens)??0,suffix:e("common.units.token")}),t.jsx(E,{title:e("dashboard.cards.avgLatency"),value:(c==null?void 0:c.today.avgLatencyMs)??0,suffix:e("common.units.ms")})]}),t.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[t.jsx(L,{title:e("dashboard.charts.requestsTitle"),description:e("dashboard.charts.requestsDesc"),loading:p.isPending,option:q,empty:!j.length,emptyText:e("dashboard.charts.empty")}),t.jsx(L,{title:e("dashboard.charts.modelTitle"),description:e("dashboard.charts.modelDesc"),loading:u.isPending,option:w,empty:!n.length,emptyText:e("dashboard.charts.empty")})]}),t.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[t.jsx(L,{title:e("dashboard.charts.ttftTitle"),description:e("dashboard.charts.ttftDesc"),loading:u.isPending,option:I,empty:!n.some(i=>i.avgTtftMs!=null&&i.avgTtftMs>0),emptyText:e("dashboard.charts.ttftEmpty")}),t.jsx(L,{title:e("dashboard.charts.tpotTitle"),description:e("dashboard.charts.tpotDesc"),loading:u.isPending,option:P,empty:!n.some(i=>i.avgTpotMs!=null&&i.avgTpotMs>0),emptyText:e("dashboard.charts.tpotEmpty")})]}),t.jsx(G,{models:n,loading:u.isPending}),t.jsx(Q,{loading:y.isPending,records:$})]})}function E({title:e,value:a,suffix:s}){return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:e}),t.jsxs("p",{className:"mt-2 text-2xl font-semibold",children:[a.toLocaleString(),s?t.jsx("span",{className:"ml-1 text-base font-normal text-slate-500 dark:text-slate-400",children:s}):null]})]})}function L({title:e,description:a,option:s,loading:d,empty:l,emptyText:b}){const{t:p}=A();return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsxs("div",{className:"mb-4",children:[t.jsx("p",{className:"text-sm font-semibold",children:e}),t.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:a})]}),d?t.jsx("div",{className:"flex h-60 items-center justify-center text-sm text-slate-400",children:p("common.loadingShort")}):l?t.jsx("div",{className:"flex h-60 items-center justify-center text-sm text-slate-400",children:b??p("dashboard.charts.empty")}):t.jsx(O,{option:s,style:{height:260},notMerge:!0,lazyUpdate:!0,theme:void 0})]})}function G({models:e,loading:a}){const{t:s}=A(),d=e.length>0;return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsx("div",{className:"flex items-center justify-between border-b border-slate-200 px-4 py-3 dark:border-slate-800",children:t.jsxs("div",{children:[t.jsx("p",{className:"text-sm font-semibold",children:s("dashboard.modelTable.title")}),t.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.description")})]})}),a?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("common.loadingShort")}):d?t.jsx("div",{className:"max-h-80 overflow-x-auto",children:t.jsxs("table",{className:"min-w-full divide-y divide-slate-200 text-sm dark:divide-slate-800",children:[t.jsx("caption",{className:"sr-only",children:s("dashboard.modelTable.title")}),t.jsx("thead",{className:"bg-slate-100 dark:bg-slate-800/50",children:t.jsxs("tr",{children:[t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.model")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.requests")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.latency")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.ttft")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.tpot")})]})}),t.jsx("tbody",{className:"divide-y divide-slate-200 dark:divide-slate-800",children:e.map(l=>t.jsxs("tr",{className:"hover:bg-slate-50 dark:hover:bg-slate-800/60",children:[t.jsx("td",{className:"px-4 py-2",children:t.jsxs("div",{className:"flex flex-col",children:[t.jsx("span",{className:"font-medium text-slate-700 dark:text-slate-100",children:l.provider}),t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:l.model})]})}),t.jsx("td",{className:"px-4 py-2 text-right",children:l.requests.toLocaleString()}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgLatencyMs,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgTtftMs,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgTpotMs,s("common.units.msPerToken"),{maximumFractionDigits:2})})]},`${l.provider}/${l.model}`))})]})}):t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.modelTable.empty")})]})}function Q({records:e,loading:a}){const{t:s}=A();return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsxs("div",{className:"flex items-center justify-between border-b border-slate-200 px-4 py-3 dark:border-slate-800",children:[t.jsx("p",{className:"text-sm font-semibold",children:s("dashboard.recent.title")}),t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("dashboard.recent.subtitle",{count:5})})]}),a?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.recent.loading")}):e.length===0?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.recent.empty")}):t.jsx("div",{className:"max-h-80 overflow-auto",children:t.jsxs("table",{className:"min-w-full divide-y divide-slate-200 text-sm dark:divide-slate-800",children:[t.jsx("caption",{className:"sr-only",children:s("dashboard.recent.title")}),t.jsx("thead",{className:"bg-slate-100 dark:bg-slate-800/50",children:t.jsxs("tr",{children:[t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.time")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.endpoint")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.provider")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.route")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.latency")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.status")})]})}),t.jsx("tbody",{className:"divide-y divide-slate-200 dark:divide-slate-800",children:e.map(d=>t.jsxs("tr",{className:"hover:bg-slate-50 dark:hover:bg-slate-800/60",children:[t.jsx("td",{className:"px-4 py-2 text-xs text-slate-500 dark:text-slate-400",children:new Date(d.timestamp).toLocaleString()}),t.jsx("td",{className:"px-4 py-2 text-xs text-slate-500 dark:text-slate-400",children:d.endpoint==="anthropic"?s("logs.table.endpointAnthropic"):d.endpoint==="openai"?s("logs.table.endpointOpenAI"):d.endpoint}),t.jsx("td",{className:"px-4 py-2",children:d.provider}),t.jsxs("td",{className:"px-4 py-2",children:[t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:d.client_model??s("dashboard.recent.routePlaceholder")}),t.jsx("span",{className:"mx-1 text-slate-400",children:"→"}),t.jsx("span",{className:"font-medium text-slate-700 dark:text-slate-100",children:d.model})]}),t.jsx("td",{className:"px-4 py-2",children:N(d.latency_ms,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2",children:t.jsxs("span",{className:`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${d.error?"bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-200":"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-200"}`,children:[(d.status_code??200).toString(),t.jsx("span",{className:"ml-1",children:d.error?s("common.status.error"):s("common.status.success")})]})})]},d.id))})]})})]})}export{_ as default};
1
+ import{u as A,a as R,r as h,j as t,L as D}from"./index-fNmJF3BX.js";import{E as O}from"./index-DWhAov2p.js";import{u as v}from"./useApiQuery-CCi1tkCr.js";function N(e,a,s){return e==null?"-":`${e.toLocaleString(void 0,s)} ${a}`}function B(e){if(e==null)return"-";if(e<1024)return`${e} B`;const a=["KB","MB","GB","TB"];let s=e/1024,d=0;for(;s>=1024&&d<a.length-1;)s/=1024,d+=1;return`${s.toFixed(s>=100?0:s>=10?1:2)} ${a[d]}`}function _(){var S;const{t:e}=A(),{pushToast:a}=R(),[s,d]=h.useState("all"),l=s==="all"?void 0:s,b=v(["stats","overview",s],{url:"/api/stats/overview",method:"GET",params:l?{endpoint:l}:void 0}),p=v(["stats","daily",14,s],{url:"/api/stats/daily",method:"GET",params:{days:14,...l?{endpoint:l}:{}}}),u=v(["stats","model",7,6,s],{url:"/api/stats/model",method:"GET",params:{days:7,limit:6,...l?{endpoint:l}:{}}}),g=v(["status"],{url:"/api/status",method:"GET"}),f=v(["db","info"],{url:"/api/db/info",method:"GET"}),y=v(["logs","recent",s],{url:"/api/logs",method:"GET",params:{limit:5,...l?{endpoint:l}:{}}},{refetchInterval:3e4});h.useEffect(()=>{b.isError&&b.error&&a({title:e("dashboard.toast.overviewError"),description:b.error.message,variant:"error"})},[b.isError,b.error,a,e]),h.useEffect(()=>{p.isError&&p.error&&a({title:e("dashboard.toast.dailyError"),description:p.error.message,variant:"error"})},[p.isError,p.error,a,e]),h.useEffect(()=>{u.isError&&u.error&&a({title:e("dashboard.toast.modelError"),description:u.error.message,variant:"error"})},[u.isError,u.error,a,e]),h.useEffect(()=>{g.isError&&g.error&&a({title:e("dashboard.toast.statusError"),description:g.error.message,variant:"error"})},[g.isError,g.error,a,e]),h.useEffect(()=>{f.isError&&f.error&&a({title:e("dashboard.toast.dbError"),description:f.error.message,variant:"error"})},[f.isError,f.error,a,e]),h.useEffect(()=>{y.isError&&y.error&&a({title:e("dashboard.toast.recentError"),description:y.error.message,variant:"error"})},[y.isError,y.error,a,e]);const c=b.data,j=p.data??[],n=u.data??[],k=g.data,M=f.data,$=((S=y.data)==null?void 0:S.items)??[],q=h.useMemo(()=>{const i=j.map(o=>o.date),x=e("dashboard.charts.barRequests"),r=e("dashboard.charts.lineInput"),m=e("dashboard.charts.lineOutput");return{tooltip:{trigger:"axis"},legend:{data:[x,r,m]},grid:{left:40,right:20,top:40,bottom:40},xAxis:{type:"category",data:i},yAxis:{type:"value"},series:[{name:x,type:"bar",data:j.map(o=>o.requestCount),itemStyle:{color:"#2563eb"}},{name:r,type:"line",yAxisIndex:0,data:j.map(o=>o.inputTokens),smooth:!0,itemStyle:{color:"#22c55e"}},{name:m,type:"line",yAxisIndex:0,data:j.map(o=>o.outputTokens),smooth:!0,itemStyle:{color:"#f97316"}}]}},[j,e]),w=h.useMemo(()=>{const i=n.map(o=>`${o.provider}/${o.model}`),x=e("dashboard.charts.barRequests"),r=e("dashboard.charts.lineInput"),m=e("dashboard.charts.lineOutput");return{tooltip:{trigger:"axis"},legend:{data:[x,r,m]},grid:{left:50,right:40,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:[{type:"value",name:x},{type:"value",name:e("dashboard.charts.axisTokens"),position:"right"}],series:[{name:x,type:"bar",data:n.map(o=>o.requests),itemStyle:{color:"#6366f1"},yAxisIndex:0},{name:r,type:"line",yAxisIndex:1,smooth:!0,data:n.map(o=>o.inputTokens??0),itemStyle:{color:"#22c55e"}},{name:m,type:"line",yAxisIndex:1,smooth:!0,data:n.map(o=>o.outputTokens??0),itemStyle:{color:"#f97316"}}]}},[n,e]),I=h.useMemo(()=>{const i=n.map(r=>`${r.provider}/${r.model}`),x=e("dashboard.charts.ttftLabel");return{tooltip:{trigger:"axis",formatter(r){var T;if(!Array.isArray(r)||r.length===0)return"";const m=((T=r[0])==null?void 0:T.dataIndex)??0,o=n[m];return o?`<strong>${i[m]}</strong><br/>${x}: ${N(o.avgTtftMs,e("common.units.ms"))}`:""}},grid:{left:50,right:30,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:{type:"value",name:e("dashboard.charts.ttftAxis")},series:[{name:x,type:"bar",data:n.map(r=>r.avgTtftMs??0),itemStyle:{color:"#2563eb"}}]}},[n,e]),P=h.useMemo(()=>{const i=n.map(r=>`${r.provider}/${r.model}`),x=e("dashboard.charts.tpotLabel");return{tooltip:{trigger:"axis",formatter(r){var T;if(!Array.isArray(r)||r.length===0)return"";const m=((T=r[0])==null?void 0:T.dataIndex)??0,o=n[m];return o?`<strong>${i[m]}</strong><br/>${x}: ${N(o.avgTpotMs,e("common.units.msPerToken"),{maximumFractionDigits:2})}`:""}},grid:{left:50,right:30,top:40,bottom:70},xAxis:{type:"category",data:i,axisLabel:{rotate:30}},yAxis:{type:"value",name:e("dashboard.charts.tpotAxis")},series:[{name:x,type:"bar",data:n.map(r=>r.avgTpotMs??0),itemStyle:{color:"#f97316"}}]}},[n,e]);return b.isPending||g.isPending||f.isPending?t.jsx(D,{}):t.jsxs("div",{className:"flex flex-col gap-6",children:[t.jsxs("section",{className:"flex flex-col gap-2",children:[t.jsx("h1",{className:"text-2xl font-semibold",children:e("nav.dashboard")}),t.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:e("dashboard.description")}),k?t.jsxs("div",{className:"flex flex-wrap gap-3 rounded-lg border border-slate-200 bg-white p-4 text-sm shadow-sm dark:border-slate-800 dark:bg-slate-900","aria-live":"polite",children:[t.jsx("span",{className:"font-medium",children:e("dashboard.status.listening",{host:k.host??"0.0.0.0",port:k.port})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.providers",{value:k.providers})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.todayRequests",{value:((c==null?void 0:c.today.requests)??0).toLocaleString()})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.active",{value:(k.activeRequests??0).toLocaleString()})}),t.jsx("span",{className:"text-slate-500 dark:text-slate-400",children:e("dashboard.status.dbSize",{value:M?B(M.sizeBytes):"-"})})]}):null,t.jsxs("div",{className:"mt-2 flex flex-wrap items-center gap-2 text-sm text-slate-500 dark:text-slate-400",children:[t.jsx("label",{className:"text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-slate-400",children:e("dashboard.filters.endpoint")}),t.jsxs("select",{value:s,onChange:i=>d(i.target.value),className:"h-9 rounded-md border border-slate-200 bg-white px-3 text-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:focus:border-blue-400 dark:focus:ring-blue-400/40",children:[t.jsx("option",{value:"all",children:e("dashboard.filters.endpointAll")}),t.jsx("option",{value:"anthropic",children:e("dashboard.filters.endpointAnthropic")}),t.jsx("option",{value:"openai",children:e("dashboard.filters.endpointOpenAI")})]})]})]}),t.jsxs("section",{className:"grid gap-4 md:grid-cols-2 xl:grid-cols-4",children:[t.jsx(E,{title:e("dashboard.cards.todayRequests"),value:(c==null?void 0:c.today.requests)??0,suffix:e("common.units.request")}),t.jsx(E,{title:e("dashboard.cards.todayInput"),value:(c==null?void 0:c.today.inputTokens)??0,suffix:e("common.units.token")}),t.jsx(E,{title:e("dashboard.cards.todayOutput"),value:(c==null?void 0:c.today.outputTokens)??0,suffix:e("common.units.token")}),t.jsx(E,{title:e("dashboard.cards.avgLatency"),value:(c==null?void 0:c.today.avgLatencyMs)??0,suffix:e("common.units.ms")})]}),t.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[t.jsx(L,{title:e("dashboard.charts.requestsTitle"),description:e("dashboard.charts.requestsDesc"),loading:p.isPending,option:q,empty:!j.length,emptyText:e("dashboard.charts.empty")}),t.jsx(L,{title:e("dashboard.charts.modelTitle"),description:e("dashboard.charts.modelDesc"),loading:u.isPending,option:w,empty:!n.length,emptyText:e("dashboard.charts.empty")})]}),t.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[t.jsx(L,{title:e("dashboard.charts.ttftTitle"),description:e("dashboard.charts.ttftDesc"),loading:u.isPending,option:I,empty:!n.some(i=>i.avgTtftMs!=null&&i.avgTtftMs>0),emptyText:e("dashboard.charts.ttftEmpty")}),t.jsx(L,{title:e("dashboard.charts.tpotTitle"),description:e("dashboard.charts.tpotDesc"),loading:u.isPending,option:P,empty:!n.some(i=>i.avgTpotMs!=null&&i.avgTpotMs>0),emptyText:e("dashboard.charts.tpotEmpty")})]}),t.jsx(G,{models:n,loading:u.isPending}),t.jsx(Q,{loading:y.isPending,records:$})]})}function E({title:e,value:a,suffix:s}){return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsx("p",{className:"text-sm text-slate-500 dark:text-slate-400",children:e}),t.jsxs("p",{className:"mt-2 text-2xl font-semibold",children:[a.toLocaleString(),s?t.jsx("span",{className:"ml-1 text-base font-normal text-slate-500 dark:text-slate-400",children:s}):null]})]})}function L({title:e,description:a,option:s,loading:d,empty:l,emptyText:b}){const{t:p}=A();return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsxs("div",{className:"mb-4",children:[t.jsx("p",{className:"text-sm font-semibold",children:e}),t.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:a})]}),d?t.jsx("div",{className:"flex h-60 items-center justify-center text-sm text-slate-400",children:p("common.loadingShort")}):l?t.jsx("div",{className:"flex h-60 items-center justify-center text-sm text-slate-400",children:b??p("dashboard.charts.empty")}):t.jsx(O,{option:s,style:{height:260},notMerge:!0,lazyUpdate:!0,theme:void 0})]})}function G({models:e,loading:a}){const{t:s}=A(),d=e.length>0;return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsx("div",{className:"flex items-center justify-between border-b border-slate-200 px-4 py-3 dark:border-slate-800",children:t.jsxs("div",{children:[t.jsx("p",{className:"text-sm font-semibold",children:s("dashboard.modelTable.title")}),t.jsx("p",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.description")})]})}),a?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("common.loadingShort")}):d?t.jsx("div",{className:"max-h-80 overflow-x-auto",children:t.jsxs("table",{className:"min-w-full divide-y divide-slate-200 text-sm dark:divide-slate-800",children:[t.jsx("caption",{className:"sr-only",children:s("dashboard.modelTable.title")}),t.jsx("thead",{className:"bg-slate-100 dark:bg-slate-800/50",children:t.jsxs("tr",{children:[t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.model")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.requests")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.latency")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.ttft")}),t.jsx("th",{className:"px-4 py-2 text-right font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.modelTable.columns.tpot")})]})}),t.jsx("tbody",{className:"divide-y divide-slate-200 dark:divide-slate-800",children:e.map(l=>t.jsxs("tr",{className:"hover:bg-slate-50 dark:hover:bg-slate-800/60",children:[t.jsx("td",{className:"px-4 py-2",children:t.jsxs("div",{className:"flex flex-col",children:[t.jsx("span",{className:"font-medium text-slate-700 dark:text-slate-100",children:l.provider}),t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:l.model})]})}),t.jsx("td",{className:"px-4 py-2 text-right",children:l.requests.toLocaleString()}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgLatencyMs,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgTtftMs,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2 text-right",children:N(l.avgTpotMs,s("common.units.msPerToken"),{maximumFractionDigits:2})})]},`${l.provider}/${l.model}`))})]})}):t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.modelTable.empty")})]})}function Q({records:e,loading:a}){const{t:s}=A();return t.jsxs("div",{className:"rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[t.jsxs("div",{className:"flex items-center justify-between border-b border-slate-200 px-4 py-3 dark:border-slate-800",children:[t.jsx("p",{className:"text-sm font-semibold",children:s("dashboard.recent.title")}),t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:s("dashboard.recent.subtitle",{count:5})})]}),a?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.recent.loading")}):e.length===0?t.jsx("div",{className:"flex h-40 items-center justify-center text-sm text-slate-400",children:s("dashboard.recent.empty")}):t.jsx("div",{className:"max-h-80 overflow-auto",children:t.jsxs("table",{className:"min-w-full divide-y divide-slate-200 text-sm dark:divide-slate-800",children:[t.jsx("caption",{className:"sr-only",children:s("dashboard.recent.title")}),t.jsx("thead",{className:"bg-slate-100 dark:bg-slate-800/50",children:t.jsxs("tr",{children:[t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.time")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.endpoint")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.provider")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.route")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.latency")}),t.jsx("th",{className:"px-4 py-2 text-left font-medium text-slate-500 dark:text-slate-400",children:s("dashboard.recent.columns.status")})]})}),t.jsx("tbody",{className:"divide-y divide-slate-200 dark:divide-slate-800",children:e.map(d=>t.jsxs("tr",{className:"hover:bg-slate-50 dark:hover:bg-slate-800/60",children:[t.jsx("td",{className:"px-4 py-2 text-xs text-slate-500 dark:text-slate-400",children:new Date(d.timestamp).toLocaleString()}),t.jsx("td",{className:"px-4 py-2 text-xs text-slate-500 dark:text-slate-400",children:d.endpoint==="anthropic"?s("logs.table.endpointAnthropic"):d.endpoint==="openai"?s("logs.table.endpointOpenAI"):d.endpoint}),t.jsx("td",{className:"px-4 py-2",children:d.provider}),t.jsxs("td",{className:"px-4 py-2",children:[t.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:d.client_model??s("dashboard.recent.routePlaceholder")}),t.jsx("span",{className:"mx-1 text-slate-400",children:"→"}),t.jsx("span",{className:"font-medium text-slate-700 dark:text-slate-100",children:d.model})]}),t.jsx("td",{className:"px-4 py-2",children:N(d.latency_ms,s("common.units.ms"))}),t.jsx("td",{className:"px-4 py-2",children:t.jsxs("span",{className:`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${d.error?"bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-200":"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-200"}`,children:[(d.status_code??200).toString(),t.jsx("span",{className:"ml-1",children:d.error?s("common.status.error"):s("common.status.success")})]})})]},d.id))})]})})]})}export{_ as default};
@@ -1 +1 @@
1
- import{u as c,r as d,j as e}from"./index-CDJfhjXI.js";function o(){const{t:s}=c(),a=d.useMemo(()=>{const t=s("help.sections.configuration.items",{returnObjects:!0}),l=s("help.sections.usage.items",{returnObjects:!0}),i=s("help.sections.tips.items",{returnObjects:!0});return[{title:s("help.sections.configuration.title"),items:t},{title:s("help.sections.usage.title"),items:l},{title:s("help.sections.tips.title"),items:i}]},[s]),r=s("help.faq.items",{returnObjects:!0});return e.jsxs("div",{className:"mx-auto flex max-w-4xl flex-col gap-8",children:[e.jsxs("header",{className:"space-y-3",children:[e.jsx("h1",{className:"text-2xl font-semibold",children:s("help.title")}),e.jsx("p",{className:"text-sm text-slate-600 dark:text-slate-300",children:s("help.intro")}),e.jsx("div",{className:"rounded-md border border-blue-100 bg-blue-50 px-4 py-3 text-xs text-blue-700 dark:border-blue-900/40 dark:bg-blue-950/60 dark:text-blue-200",children:s("help.note")})]}),a.map(t=>e.jsxs("section",{className:"space-y-3 rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[e.jsx("h2",{className:"text-lg font-semibold",children:t.title}),e.jsx("ol",{className:"list-decimal space-y-2 pl-6 text-sm text-slate-700 dark:text-slate-300",children:t.items.map(l=>e.jsx("li",{children:l},l))})]},t.title)),e.jsxs("section",{className:"space-y-3 rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("help.faq.title")}),e.jsx("dl",{className:"space-y-4 text-sm text-slate-700 dark:text-slate-300",children:r.map(t=>e.jsxs("div",{className:"space-y-1",children:[e.jsx("dt",{className:"font-medium text-slate-900 dark:text-slate-100",children:t.q}),e.jsx("dd",{children:t.a})]},t.q))})]})]})}export{o as default};
1
+ import{u as c,r as d,j as e}from"./index-fNmJF3BX.js";function o(){const{t:s}=c(),a=d.useMemo(()=>{const t=s("help.sections.configuration.items",{returnObjects:!0}),l=s("help.sections.usage.items",{returnObjects:!0}),i=s("help.sections.tips.items",{returnObjects:!0});return[{title:s("help.sections.configuration.title"),items:t},{title:s("help.sections.usage.title"),items:l},{title:s("help.sections.tips.title"),items:i}]},[s]),r=s("help.faq.items",{returnObjects:!0});return e.jsxs("div",{className:"mx-auto flex max-w-4xl flex-col gap-8",children:[e.jsxs("header",{className:"space-y-3",children:[e.jsx("h1",{className:"text-2xl font-semibold",children:s("help.title")}),e.jsx("p",{className:"text-sm text-slate-600 dark:text-slate-300",children:s("help.intro")}),e.jsx("div",{className:"rounded-md border border-blue-100 bg-blue-50 px-4 py-3 text-xs text-blue-700 dark:border-blue-900/40 dark:bg-blue-950/60 dark:text-blue-200",children:s("help.note")})]}),a.map(t=>e.jsxs("section",{className:"space-y-3 rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[e.jsx("h2",{className:"text-lg font-semibold",children:t.title}),e.jsx("ol",{className:"list-decimal space-y-2 pl-6 text-sm text-slate-700 dark:text-slate-300",children:t.items.map(l=>e.jsx("li",{children:l},l))})]},t.title)),e.jsxs("section",{className:"space-y-3 rounded-lg border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900",children:[e.jsx("h2",{className:"text-lg font-semibold",children:s("help.faq.title")}),e.jsx("dl",{className:"space-y-4 text-sm text-slate-700 dark:text-slate-300",children:r.map(t=>e.jsxs("div",{className:"space-y-1",children:[e.jsx("dt",{className:"font-medium text-slate-900 dark:text-slate-100",children:t.q}),e.jsx("dd",{children:t.a})]},t.q))})]})]})}export{o as default};