@lakphy/local-router 0.5.5 → 0.5.6
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/dist/cli.js +967 -653
- package/dist/entry.js +1223 -909
- package/dist/web/assets/index-0-b0NcMV.js +192 -0
- package/dist/web/assets/index-_8dgANhJ.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-BxjdLPIh.js +0 -190
- package/dist/web/assets/index-Dc2P7nS-.css +0 -2
package/dist/cli.js
CHANGED
|
@@ -52922,513 +52922,134 @@ var init_crypto = __esm(() => {
|
|
|
52922
52922
|
ECDH_PARAMS = { name: "ECDH", namedCurve: "P-256" };
|
|
52923
52923
|
});
|
|
52924
52924
|
|
|
52925
|
-
// src/
|
|
52926
|
-
import {
|
|
52927
|
-
import {
|
|
52928
|
-
|
|
52929
|
-
|
|
52930
|
-
|
|
52931
|
-
|
|
52932
|
-
function toPercent(numerator, denominator) {
|
|
52933
|
-
if (denominator <= 0)
|
|
52934
|
-
return 0;
|
|
52935
|
-
return Number((numerator / denominator * 100).toFixed(2));
|
|
52925
|
+
// src/token-usage.ts
|
|
52926
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6, statSync } from "fs";
|
|
52927
|
+
import { resolve as resolve5 } from "path";
|
|
52928
|
+
function asRecord(value) {
|
|
52929
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
52930
|
+
return null;
|
|
52931
|
+
return value;
|
|
52936
52932
|
}
|
|
52937
|
-
function
|
|
52938
|
-
|
|
52939
|
-
|
|
52933
|
+
function numeric(value) {
|
|
52934
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
52935
|
+
return value;
|
|
52936
|
+
if (typeof value === "string" && value.trim()) {
|
|
52937
|
+
const parsed = Number(value);
|
|
52938
|
+
if (Number.isFinite(parsed))
|
|
52939
|
+
return parsed;
|
|
52940
|
+
}
|
|
52941
|
+
return null;
|
|
52940
52942
|
}
|
|
52941
|
-
function
|
|
52942
|
-
|
|
52943
|
-
for (
|
|
52944
|
-
|
|
52943
|
+
function numberAt(value, path) {
|
|
52944
|
+
let current = value;
|
|
52945
|
+
for (const key of path) {
|
|
52946
|
+
const record2 = asRecord(current);
|
|
52947
|
+
if (!record2 || !(key in record2))
|
|
52948
|
+
return null;
|
|
52949
|
+
current = record2[key];
|
|
52945
52950
|
}
|
|
52946
|
-
return
|
|
52951
|
+
return numeric(current);
|
|
52947
52952
|
}
|
|
52948
|
-
function
|
|
52949
|
-
|
|
52950
|
-
|
|
52951
|
-
|
|
52952
|
-
|
|
52953
|
-
|
|
52954
|
-
|
|
52955
|
-
return "4xx";
|
|
52956
|
-
if (status >= 500)
|
|
52957
|
-
return "5xx";
|
|
52958
|
-
return "network_error";
|
|
52953
|
+
function firstNumber(value, paths) {
|
|
52954
|
+
for (const path of paths) {
|
|
52955
|
+
const found = numberAt(value, path);
|
|
52956
|
+
if (found !== null)
|
|
52957
|
+
return found;
|
|
52958
|
+
}
|
|
52959
|
+
return null;
|
|
52959
52960
|
}
|
|
52960
|
-
function
|
|
52961
|
-
|
|
52962
|
-
|
|
52963
|
-
|
|
52964
|
-
return
|
|
52961
|
+
function maxNullable(...values) {
|
|
52962
|
+
const numbers = values.filter((value) => value !== null && value !== undefined);
|
|
52963
|
+
if (numbers.length === 0)
|
|
52964
|
+
return null;
|
|
52965
|
+
return Math.max(...numbers);
|
|
52965
52966
|
}
|
|
52966
|
-
function
|
|
52967
|
-
|
|
52968
|
-
|
|
52969
|
-
|
|
52970
|
-
|
|
52967
|
+
function sumNullable(...values) {
|
|
52968
|
+
let total = 0;
|
|
52969
|
+
let hasValue = false;
|
|
52970
|
+
for (const value of values) {
|
|
52971
|
+
if (value === null || value === undefined)
|
|
52972
|
+
continue;
|
|
52973
|
+
total += value;
|
|
52974
|
+
hasValue = true;
|
|
52975
|
+
}
|
|
52976
|
+
return hasValue ? total : null;
|
|
52971
52977
|
}
|
|
52972
|
-
function
|
|
52973
|
-
|
|
52974
|
-
|
|
52975
|
-
|
|
52976
|
-
const series = Array.from({ length: bucketCount }, (_, i) => ({
|
|
52977
|
-
ts: new Date(fromMs + i * bucketMs).toISOString(),
|
|
52978
|
-
requests: 0,
|
|
52979
|
-
errors: 0,
|
|
52980
|
-
avgLatencyMs: 0
|
|
52981
|
-
}));
|
|
52982
|
-
return {
|
|
52983
|
-
window: window2,
|
|
52984
|
-
from: new Date(fromMs).toISOString(),
|
|
52985
|
-
to: new Date(nowMs).toISOString(),
|
|
52986
|
-
generatedAt: new Date(nowMs).toISOString(),
|
|
52987
|
-
source,
|
|
52988
|
-
summary: {
|
|
52989
|
-
totalRequests: 0,
|
|
52990
|
-
successRequests: 0,
|
|
52991
|
-
errorRequests: 0,
|
|
52992
|
-
successRate: 0,
|
|
52993
|
-
avgLatencyMs: 0,
|
|
52994
|
-
p95LatencyMs: 0,
|
|
52995
|
-
totalRequestBytes: 0,
|
|
52996
|
-
totalResponseBytes: 0
|
|
52997
|
-
},
|
|
52998
|
-
series,
|
|
52999
|
-
topProviders: [],
|
|
53000
|
-
topRouteTypes: [],
|
|
53001
|
-
statusClasses: {
|
|
53002
|
-
"2xx": 0,
|
|
53003
|
-
"4xx": 0,
|
|
53004
|
-
"5xx": 0,
|
|
53005
|
-
network_error: 0
|
|
53006
|
-
},
|
|
53007
|
-
warnings
|
|
53008
|
-
};
|
|
52978
|
+
function roundPercent(numerator, denominator) {
|
|
52979
|
+
if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator <= 0)
|
|
52980
|
+
return null;
|
|
52981
|
+
return Number((numerator / denominator * 100).toFixed(2));
|
|
53009
52982
|
}
|
|
53010
|
-
|
|
53011
|
-
const
|
|
53012
|
-
|
|
53013
|
-
|
|
53014
|
-
|
|
53015
|
-
|
|
53016
|
-
|
|
53017
|
-
|
|
53018
|
-
|
|
53019
|
-
|
|
53020
|
-
|
|
53021
|
-
|
|
53022
|
-
|
|
52983
|
+
function inferProviderStyle(usage, providerHint) {
|
|
52984
|
+
const hint = providerHint?.toLowerCase() ?? "";
|
|
52985
|
+
if (hint.includes("anthropic") || hint.includes("claude"))
|
|
52986
|
+
return "anthropic";
|
|
52987
|
+
if (hint.includes("gemini") || hint.includes("google"))
|
|
52988
|
+
return "gemini";
|
|
52989
|
+
if (hint.includes("deepseek"))
|
|
52990
|
+
return "deepseek";
|
|
52991
|
+
if (hint.includes("cohere"))
|
|
52992
|
+
return "cohere";
|
|
52993
|
+
if (hint.includes("mistral"))
|
|
52994
|
+
return "mistral";
|
|
52995
|
+
if (hint.includes("openrouter"))
|
|
52996
|
+
return "openrouter";
|
|
52997
|
+
if (hint.includes("openai") || hint.includes("gpt-"))
|
|
52998
|
+
return "openai";
|
|
52999
|
+
if ("cache_read_input_tokens" in usage || "cache_creation_input_tokens" in usage) {
|
|
53000
|
+
return "anthropic";
|
|
53023
53001
|
}
|
|
53024
|
-
|
|
53025
|
-
|
|
53026
|
-
const cached2 = metricsCache.get(cacheKey);
|
|
53027
|
-
if (!refresh && cached2 && cached2.expiresAt > nowMs) {
|
|
53028
|
-
return cached2.value;
|
|
53002
|
+
if ("prompt_cache_hit_tokens" in usage || "prompt_cache_miss_tokens" in usage) {
|
|
53003
|
+
return "deepseek";
|
|
53029
53004
|
}
|
|
53030
|
-
|
|
53031
|
-
|
|
53032
|
-
const empty = createEmptyMetrics(window2, nowMs, {
|
|
53033
|
-
logEnabled: true,
|
|
53034
|
-
baseDir,
|
|
53035
|
-
filesScanned: 0,
|
|
53036
|
-
linesScanned: 0,
|
|
53037
|
-
partial: false
|
|
53038
|
-
}, ["\u65E5\u5FD7\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u6682\u65E0\u53EF\u5206\u6790\u6570\u636E"]);
|
|
53039
|
-
metricsCache.set(cacheKey, { expiresAt: nowMs + CACHE_TTL_MS, value: empty });
|
|
53040
|
-
return empty;
|
|
53005
|
+
if ("promptTokenCount" in usage || "usageMetadata" in usage || "cachedContentTokenCount" in usage) {
|
|
53006
|
+
return "gemini";
|
|
53041
53007
|
}
|
|
53042
|
-
|
|
53043
|
-
|
|
53044
|
-
|
|
53045
|
-
|
|
53046
|
-
|
|
53047
|
-
|
|
53048
|
-
|
|
53049
|
-
|
|
53050
|
-
}
|
|
53051
|
-
|
|
53052
|
-
|
|
53053
|
-
|
|
53054
|
-
|
|
53055
|
-
|
|
53056
|
-
|
|
53057
|
-
|
|
53058
|
-
|
|
53059
|
-
|
|
53060
|
-
|
|
53061
|
-
|
|
53062
|
-
|
|
53063
|
-
|
|
53064
|
-
|
|
53065
|
-
|
|
53066
|
-
|
|
53067
|
-
|
|
53068
|
-
|
|
53069
|
-
|
|
53070
|
-
|
|
53071
|
-
|
|
53072
|
-
|
|
53073
|
-
|
|
53074
|
-
|
|
53075
|
-
|
|
53076
|
-
|
|
53077
|
-
|
|
53078
|
-
|
|
53079
|
-
|
|
53080
|
-
|
|
53081
|
-
|
|
53082
|
-
|
|
53083
|
-
|
|
53084
|
-
|
|
53085
|
-
|
|
53086
|
-
|
|
53087
|
-
rl.close();
|
|
53088
|
-
stream.destroy();
|
|
53089
|
-
break;
|
|
53090
|
-
}
|
|
53091
|
-
linesScanned += 1;
|
|
53092
|
-
if (!line.trim())
|
|
53093
|
-
continue;
|
|
53094
|
-
let event;
|
|
53095
|
-
try {
|
|
53096
|
-
event = JSON.parse(line);
|
|
53097
|
-
} catch {
|
|
53098
|
-
parseErrors += 1;
|
|
53099
|
-
continue;
|
|
53100
|
-
}
|
|
53101
|
-
if (!event.ts_start)
|
|
53102
|
-
continue;
|
|
53103
|
-
const ts = Date.parse(event.ts_start);
|
|
53104
|
-
if (!Number.isFinite(ts) || ts < fromMs || ts > nowMs)
|
|
53105
|
-
continue;
|
|
53106
|
-
totalRequests += 1;
|
|
53107
|
-
const isError = isErrorEvent(event);
|
|
53108
|
-
if (isError) {
|
|
53109
|
-
errorRequests += 1;
|
|
53110
|
-
} else {
|
|
53111
|
-
successRequests += 1;
|
|
53112
|
-
}
|
|
53113
|
-
const latency = Number.isFinite(event.latency_ms) ? Math.max(0, event.latency_ms ?? 0) : 0;
|
|
53114
|
-
totalLatency += latency;
|
|
53115
|
-
latencies.push(latency);
|
|
53116
|
-
totalRequestBytes += Math.max(0, event.request_bytes ?? 0);
|
|
53117
|
-
totalResponseBytes += Math.max(0, event.response_bytes ?? 0) + Math.max(0, event.stream_bytes ?? 0);
|
|
53118
|
-
const bucketIndex = Math.min(bucketCount - 1, Math.max(0, Math.floor((ts - fromMs) / bucketMs)));
|
|
53119
|
-
const bucket = buckets[bucketIndex];
|
|
53120
|
-
bucket.requests += 1;
|
|
53121
|
-
bucket.latencySum += latency;
|
|
53122
|
-
bucket.latencyCount += 1;
|
|
53123
|
-
if (isError)
|
|
53124
|
-
bucket.errors += 1;
|
|
53125
|
-
const providerKey = event.provider || "unknown";
|
|
53126
|
-
const providerRow = providerAgg.get(providerKey) ?? {
|
|
53127
|
-
requests: 0,
|
|
53128
|
-
errors: 0,
|
|
53129
|
-
latencySum: 0
|
|
53130
|
-
};
|
|
53131
|
-
providerRow.requests += 1;
|
|
53132
|
-
providerRow.latencySum += latency;
|
|
53133
|
-
if (isError)
|
|
53134
|
-
providerRow.errors += 1;
|
|
53135
|
-
providerAgg.set(providerKey, providerRow);
|
|
53136
|
-
const routeTypeKey = event.route_type || "unknown";
|
|
53137
|
-
const routeTypeRow = routeTypeAgg.get(routeTypeKey) ?? {
|
|
53138
|
-
requests: 0,
|
|
53139
|
-
errors: 0,
|
|
53140
|
-
latencySum: 0
|
|
53141
|
-
};
|
|
53142
|
-
routeTypeRow.requests += 1;
|
|
53143
|
-
routeTypeRow.latencySum += latency;
|
|
53144
|
-
if (isError)
|
|
53145
|
-
routeTypeRow.errors += 1;
|
|
53146
|
-
routeTypeAgg.set(routeTypeKey, routeTypeRow);
|
|
53147
|
-
statusClasses[getStatusClass(event)] += 1;
|
|
53148
|
-
}
|
|
53149
|
-
} catch (err) {
|
|
53150
|
-
warnings.push(`\u8BFB\u53D6\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25: ${filePath} (${err instanceof Error ? err.message : String(err)})`);
|
|
53151
|
-
partial2 = true;
|
|
53152
|
-
}
|
|
53153
|
-
}
|
|
53154
|
-
if (parseErrors > 0) {
|
|
53155
|
-
warnings.push(`\u5DF2\u8DF3\u8FC7 ${parseErrors} \u884C\u65E0\u6548 JSON \u65E5\u5FD7`);
|
|
53156
|
-
}
|
|
53157
|
-
if (partial2) {
|
|
53158
|
-
warnings.push("\u65E5\u5FD7\u626B\u63CF\u5DF2\u90E8\u5206\u622A\u65AD\uFF0C\u7ED3\u679C\u53EF\u80FD\u4E0D\u5B8C\u6574");
|
|
53159
|
-
}
|
|
53160
|
-
latencies.sort((a, b) => a - b);
|
|
53161
|
-
const series = buckets.map((bucket, index) => ({
|
|
53162
|
-
ts: new Date(fromMs + index * bucketMs).toISOString(),
|
|
53163
|
-
requests: bucket.requests,
|
|
53164
|
-
errors: bucket.errors,
|
|
53165
|
-
avgLatencyMs: bucket.latencyCount > 0 ? Math.round(bucket.latencySum / bucket.latencyCount) : 0
|
|
53166
|
-
}));
|
|
53167
|
-
const topProviders = Array.from(providerAgg.entries()).map(([key, row]) => ({
|
|
53168
|
-
key,
|
|
53169
|
-
requests: row.requests,
|
|
53170
|
-
errorRate: toPercent(row.errors, row.requests),
|
|
53171
|
-
avgLatencyMs: row.requests > 0 ? Math.round(row.latencySum / row.requests) : 0
|
|
53172
|
-
})).sort((a, b) => b.requests - a.requests).slice(0, TOP_LIMIT);
|
|
53173
|
-
const topRouteTypes = Array.from(routeTypeAgg.entries()).map(([key, row]) => ({
|
|
53174
|
-
key,
|
|
53175
|
-
requests: row.requests,
|
|
53176
|
-
errorRate: toPercent(row.errors, row.requests)
|
|
53177
|
-
})).sort((a, b) => b.requests - a.requests).slice(0, TOP_LIMIT);
|
|
53178
|
-
const response = {
|
|
53179
|
-
window: window2,
|
|
53180
|
-
from: new Date(fromMs).toISOString(),
|
|
53181
|
-
to: new Date(nowMs).toISOString(),
|
|
53182
|
-
generatedAt: new Date(nowMs).toISOString(),
|
|
53183
|
-
source: {
|
|
53184
|
-
logEnabled: true,
|
|
53185
|
-
baseDir,
|
|
53186
|
-
filesScanned,
|
|
53187
|
-
linesScanned,
|
|
53188
|
-
partial: partial2
|
|
53189
|
-
},
|
|
53190
|
-
summary: {
|
|
53191
|
-
totalRequests,
|
|
53192
|
-
successRequests,
|
|
53193
|
-
errorRequests,
|
|
53194
|
-
successRate: toPercent(successRequests, totalRequests),
|
|
53195
|
-
avgLatencyMs: totalRequests > 0 ? Math.round(totalLatency / totalRequests) : 0,
|
|
53196
|
-
p95LatencyMs: percentile(latencies, 0.95),
|
|
53197
|
-
totalRequestBytes,
|
|
53198
|
-
totalResponseBytes
|
|
53199
|
-
},
|
|
53200
|
-
series,
|
|
53201
|
-
topProviders,
|
|
53202
|
-
topRouteTypes,
|
|
53203
|
-
statusClasses,
|
|
53204
|
-
warnings
|
|
53205
|
-
};
|
|
53206
|
-
metricsCache.set(cacheKey, {
|
|
53207
|
-
expiresAt: nowMs + CACHE_TTL_MS,
|
|
53208
|
-
value: response
|
|
53209
|
-
});
|
|
53210
|
-
return response;
|
|
53211
|
-
}
|
|
53212
|
-
var WINDOW_MS, BUCKET_MS, TOP_LIMIT = 5, MAX_LINES_SCANNED = 250000, CACHE_TTL_MS = 15000, metricsCache;
|
|
53213
|
-
var init_log_metrics = __esm(() => {
|
|
53214
|
-
init_config();
|
|
53215
|
-
WINDOW_MS = {
|
|
53216
|
-
"1h": 60 * 60 * 1000,
|
|
53217
|
-
"6h": 6 * 60 * 60 * 1000,
|
|
53218
|
-
"24h": 24 * 60 * 60 * 1000
|
|
53219
|
-
};
|
|
53220
|
-
BUCKET_MS = {
|
|
53221
|
-
"1h": 5 * 60 * 1000,
|
|
53222
|
-
"6h": 15 * 60 * 1000,
|
|
53223
|
-
"24h": 30 * 60 * 1000
|
|
53224
|
-
};
|
|
53225
|
-
metricsCache = new Map;
|
|
53226
|
-
});
|
|
53227
|
-
|
|
53228
|
-
// src/log-session-identity.ts
|
|
53229
|
-
function toRecord(value) {
|
|
53230
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
53231
|
-
return null;
|
|
53232
|
-
return value;
|
|
53233
|
-
}
|
|
53234
|
-
function extractUserIdRawFromRequestBody(requestBody) {
|
|
53235
|
-
const requestBodyRecord = toRecord(requestBody);
|
|
53236
|
-
const metadata = toRecord(requestBodyRecord?.metadata);
|
|
53237
|
-
if (!metadata) {
|
|
53238
|
-
return {
|
|
53239
|
-
hasMetadata: false,
|
|
53240
|
-
userIdRaw: null
|
|
53241
|
-
};
|
|
53242
|
-
}
|
|
53243
|
-
const userId = metadata.user_id;
|
|
53244
|
-
if (typeof userId !== "string" || userId.trim() === "") {
|
|
53245
|
-
return {
|
|
53246
|
-
hasMetadata: true,
|
|
53247
|
-
userIdRaw: null
|
|
53248
|
-
};
|
|
53249
|
-
}
|
|
53250
|
-
return {
|
|
53251
|
-
hasMetadata: true,
|
|
53252
|
-
userIdRaw: userId
|
|
53253
|
-
};
|
|
53254
|
-
}
|
|
53255
|
-
function parseUserSessionFromJsonFormat(userIdRaw) {
|
|
53256
|
-
let parsed;
|
|
53257
|
-
try {
|
|
53258
|
-
parsed = JSON.parse(userIdRaw);
|
|
53259
|
-
} catch {
|
|
53260
|
-
return null;
|
|
53261
|
-
}
|
|
53262
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
53263
|
-
return null;
|
|
53264
|
-
const obj = parsed;
|
|
53265
|
-
const sessionId = typeof obj.session_id === "string" ? obj.session_id.trim() : "";
|
|
53266
|
-
if (!sessionId)
|
|
53267
|
-
return null;
|
|
53268
|
-
const userKey = (typeof obj.account_uuid === "string" ? obj.account_uuid.trim() : "") || (typeof obj.device_id === "string" ? obj.device_id.trim() : "");
|
|
53269
|
-
return { userKey: userKey || sessionId, sessionId };
|
|
53270
|
-
}
|
|
53271
|
-
function parseUserSessionFromUserIdRaw(userIdRaw) {
|
|
53272
|
-
if (userIdRaw.trimStart().startsWith("{")) {
|
|
53273
|
-
return parseUserSessionFromJsonFormat(userIdRaw);
|
|
53274
|
-
}
|
|
53275
|
-
const index = userIdRaw.indexOf(USER_SESSION_DELIMITER);
|
|
53276
|
-
if (index <= 0)
|
|
53277
|
-
return null;
|
|
53278
|
-
const userKey = userIdRaw.slice(0, index).trim();
|
|
53279
|
-
const sessionId = userIdRaw.slice(index + USER_SESSION_DELIMITER.length).trim();
|
|
53280
|
-
if (!userKey || !sessionId)
|
|
53281
|
-
return null;
|
|
53282
|
-
return { userKey, sessionId };
|
|
53283
|
-
}
|
|
53284
|
-
function resolveLogSessionIdentity(requestBody) {
|
|
53285
|
-
const { hasMetadata, userIdRaw } = extractUserIdRawFromRequestBody(requestBody);
|
|
53286
|
-
if (!userIdRaw) {
|
|
53287
|
-
return {
|
|
53288
|
-
hasMetadata,
|
|
53289
|
-
userIdRaw: null,
|
|
53290
|
-
userKey: null,
|
|
53291
|
-
sessionId: null
|
|
53292
|
-
};
|
|
53293
|
-
}
|
|
53294
|
-
const parsed = parseUserSessionFromUserIdRaw(userIdRaw);
|
|
53295
|
-
return {
|
|
53296
|
-
hasMetadata,
|
|
53297
|
-
userIdRaw,
|
|
53298
|
-
userKey: parsed?.userKey ?? null,
|
|
53299
|
-
sessionId: parsed?.sessionId ?? null
|
|
53300
|
-
};
|
|
53301
|
-
}
|
|
53302
|
-
var USER_SESSION_DELIMITER = "_account__session_";
|
|
53303
|
-
|
|
53304
|
-
// src/token-usage.ts
|
|
53305
|
-
import { existsSync as existsSync5, readFileSync as readFileSync6, statSync } from "fs";
|
|
53306
|
-
import { resolve as resolve5 } from "path";
|
|
53307
|
-
function asRecord(value) {
|
|
53308
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
53309
|
-
return null;
|
|
53310
|
-
return value;
|
|
53311
|
-
}
|
|
53312
|
-
function numeric(value) {
|
|
53313
|
-
if (typeof value === "number" && Number.isFinite(value))
|
|
53314
|
-
return value;
|
|
53315
|
-
if (typeof value === "string" && value.trim()) {
|
|
53316
|
-
const parsed = Number(value);
|
|
53317
|
-
if (Number.isFinite(parsed))
|
|
53318
|
-
return parsed;
|
|
53319
|
-
}
|
|
53320
|
-
return null;
|
|
53321
|
-
}
|
|
53322
|
-
function numberAt(value, path) {
|
|
53323
|
-
let current = value;
|
|
53324
|
-
for (const key of path) {
|
|
53325
|
-
const record2 = asRecord(current);
|
|
53326
|
-
if (!record2 || !(key in record2))
|
|
53327
|
-
return null;
|
|
53328
|
-
current = record2[key];
|
|
53329
|
-
}
|
|
53330
|
-
return numeric(current);
|
|
53331
|
-
}
|
|
53332
|
-
function firstNumber(value, paths) {
|
|
53333
|
-
for (const path of paths) {
|
|
53334
|
-
const found = numberAt(value, path);
|
|
53335
|
-
if (found !== null)
|
|
53336
|
-
return found;
|
|
53337
|
-
}
|
|
53338
|
-
return null;
|
|
53339
|
-
}
|
|
53340
|
-
function maxNullable(...values) {
|
|
53341
|
-
const numbers = values.filter((value) => value !== null && value !== undefined);
|
|
53342
|
-
if (numbers.length === 0)
|
|
53343
|
-
return null;
|
|
53344
|
-
return Math.max(...numbers);
|
|
53345
|
-
}
|
|
53346
|
-
function sumNullable(...values) {
|
|
53347
|
-
let total = 0;
|
|
53348
|
-
let hasValue = false;
|
|
53349
|
-
for (const value of values) {
|
|
53350
|
-
if (value === null || value === undefined)
|
|
53351
|
-
continue;
|
|
53352
|
-
total += value;
|
|
53353
|
-
hasValue = true;
|
|
53354
|
-
}
|
|
53355
|
-
return hasValue ? total : null;
|
|
53356
|
-
}
|
|
53357
|
-
function roundPercent(numerator, denominator) {
|
|
53358
|
-
if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator <= 0)
|
|
53359
|
-
return null;
|
|
53360
|
-
return Number((numerator / denominator * 100).toFixed(2));
|
|
53361
|
-
}
|
|
53362
|
-
function inferProviderStyle(usage, providerHint) {
|
|
53363
|
-
const hint = providerHint?.toLowerCase() ?? "";
|
|
53364
|
-
if (hint.includes("anthropic") || hint.includes("claude"))
|
|
53365
|
-
return "anthropic";
|
|
53366
|
-
if (hint.includes("gemini") || hint.includes("google"))
|
|
53367
|
-
return "gemini";
|
|
53368
|
-
if (hint.includes("deepseek"))
|
|
53369
|
-
return "deepseek";
|
|
53370
|
-
if (hint.includes("cohere"))
|
|
53371
|
-
return "cohere";
|
|
53372
|
-
if (hint.includes("mistral"))
|
|
53373
|
-
return "mistral";
|
|
53374
|
-
if (hint.includes("openrouter"))
|
|
53375
|
-
return "openrouter";
|
|
53376
|
-
if (hint.includes("openai") || hint.includes("gpt-"))
|
|
53377
|
-
return "openai";
|
|
53378
|
-
if ("cache_read_input_tokens" in usage || "cache_creation_input_tokens" in usage) {
|
|
53379
|
-
return "anthropic";
|
|
53380
|
-
}
|
|
53381
|
-
if ("prompt_cache_hit_tokens" in usage || "prompt_cache_miss_tokens" in usage) {
|
|
53382
|
-
return "deepseek";
|
|
53383
|
-
}
|
|
53384
|
-
if ("promptTokenCount" in usage || "usageMetadata" in usage || "cachedContentTokenCount" in usage) {
|
|
53385
|
-
return "gemini";
|
|
53386
|
-
}
|
|
53387
|
-
if ("billed_units" in usage || "tokens" in usage) {
|
|
53388
|
-
return "cohere";
|
|
53389
|
-
}
|
|
53390
|
-
if ("prompt_tokens" in usage || "completion_tokens" in usage) {
|
|
53391
|
-
return "openai";
|
|
53392
|
-
}
|
|
53393
|
-
if ("input_tokens" in usage || "output_tokens" in usage) {
|
|
53394
|
-
return "openai";
|
|
53395
|
-
}
|
|
53396
|
-
return "unknown";
|
|
53397
|
-
}
|
|
53398
|
-
function createEmptyMetrics2(input) {
|
|
53399
|
-
return {
|
|
53400
|
-
schemaVersion: 1,
|
|
53401
|
-
source: input.source,
|
|
53402
|
-
providerStyle: input.providerStyle,
|
|
53403
|
-
inputTokens: null,
|
|
53404
|
-
outputTokens: null,
|
|
53405
|
-
totalTokens: null,
|
|
53406
|
-
cachedInputTokens: null,
|
|
53407
|
-
cacheHitInputTokens: null,
|
|
53408
|
-
cacheHitRate: null,
|
|
53409
|
-
cacheHitRateDenominatorTokens: null,
|
|
53410
|
-
cacheHitRateFormula: null,
|
|
53411
|
-
cacheReadInputTokens: null,
|
|
53412
|
-
cacheCreationInputTokens: null,
|
|
53413
|
-
cacheCreationInputTokens5m: null,
|
|
53414
|
-
cacheCreationInputTokens1h: null,
|
|
53415
|
-
cacheWriteInputTokens: null,
|
|
53416
|
-
cacheMissInputTokens: null,
|
|
53417
|
-
reasoningTokens: null,
|
|
53418
|
-
audioInputTokens: null,
|
|
53419
|
-
audioOutputTokens: null,
|
|
53420
|
-
textInputTokens: null,
|
|
53421
|
-
textOutputTokens: null,
|
|
53422
|
-
acceptedPredictionTokens: null,
|
|
53423
|
-
rejectedPredictionTokens: null,
|
|
53424
|
-
toolUsePromptTokens: null,
|
|
53425
|
-
billableInputTokens: null,
|
|
53426
|
-
billableOutputTokens: null,
|
|
53427
|
-
creditUsage: null,
|
|
53428
|
-
cost: null,
|
|
53429
|
-
rawUsage: input.rawUsage,
|
|
53430
|
-
rawUsagePath: input.rawUsagePath,
|
|
53431
|
-
warnings: []
|
|
53008
|
+
if ("billed_units" in usage || "tokens" in usage) {
|
|
53009
|
+
return "cohere";
|
|
53010
|
+
}
|
|
53011
|
+
if ("prompt_tokens" in usage || "completion_tokens" in usage) {
|
|
53012
|
+
return "openai";
|
|
53013
|
+
}
|
|
53014
|
+
if ("input_tokens" in usage || "output_tokens" in usage) {
|
|
53015
|
+
return "openai";
|
|
53016
|
+
}
|
|
53017
|
+
return "unknown";
|
|
53018
|
+
}
|
|
53019
|
+
function createEmptyMetrics(input) {
|
|
53020
|
+
return {
|
|
53021
|
+
schemaVersion: 1,
|
|
53022
|
+
source: input.source,
|
|
53023
|
+
providerStyle: input.providerStyle,
|
|
53024
|
+
inputTokens: null,
|
|
53025
|
+
outputTokens: null,
|
|
53026
|
+
totalTokens: null,
|
|
53027
|
+
cachedInputTokens: null,
|
|
53028
|
+
cacheHitInputTokens: null,
|
|
53029
|
+
cacheHitRate: null,
|
|
53030
|
+
cacheHitRateDenominatorTokens: null,
|
|
53031
|
+
cacheHitRateFormula: null,
|
|
53032
|
+
cacheReadInputTokens: null,
|
|
53033
|
+
cacheCreationInputTokens: null,
|
|
53034
|
+
cacheCreationInputTokens5m: null,
|
|
53035
|
+
cacheCreationInputTokens1h: null,
|
|
53036
|
+
cacheWriteInputTokens: null,
|
|
53037
|
+
cacheMissInputTokens: null,
|
|
53038
|
+
reasoningTokens: null,
|
|
53039
|
+
audioInputTokens: null,
|
|
53040
|
+
audioOutputTokens: null,
|
|
53041
|
+
textInputTokens: null,
|
|
53042
|
+
textOutputTokens: null,
|
|
53043
|
+
acceptedPredictionTokens: null,
|
|
53044
|
+
rejectedPredictionTokens: null,
|
|
53045
|
+
toolUsePromptTokens: null,
|
|
53046
|
+
billableInputTokens: null,
|
|
53047
|
+
billableOutputTokens: null,
|
|
53048
|
+
creditUsage: null,
|
|
53049
|
+
cost: null,
|
|
53050
|
+
rawUsage: input.rawUsage,
|
|
53051
|
+
rawUsagePath: input.rawUsagePath,
|
|
53052
|
+
warnings: []
|
|
53432
53053
|
};
|
|
53433
53054
|
}
|
|
53434
53055
|
function hasAnyTokenSignal(metrics) {
|
|
@@ -53451,7 +53072,7 @@ function hasAnyTokenSignal(metrics) {
|
|
|
53451
53072
|
function normalizeUsageObject(input) {
|
|
53452
53073
|
const { usage, source, rawUsagePath, providerHint } = input;
|
|
53453
53074
|
const providerStyle = inferProviderStyle(usage, providerHint);
|
|
53454
|
-
const metrics =
|
|
53075
|
+
const metrics = createEmptyMetrics({
|
|
53455
53076
|
source,
|
|
53456
53077
|
providerStyle,
|
|
53457
53078
|
rawUsage: usage,
|
|
@@ -53724,165 +53345,594 @@ function extractTokenUsageFromJson(value, options) {
|
|
|
53724
53345
|
}
|
|
53725
53346
|
return merged;
|
|
53726
53347
|
}
|
|
53727
|
-
function extractTokenUsageFromResponseText(text2, source = "response_body", providerHint) {
|
|
53728
|
-
if (!text2?.trim())
|
|
53729
|
-
return null;
|
|
53730
|
-
try {
|
|
53731
|
-
return extractTokenUsageFromJson(JSON.parse(text2), { source, providerHint });
|
|
53732
|
-
} catch {
|
|
53733
|
-
return null;
|
|
53348
|
+
function extractTokenUsageFromResponseText(text2, source = "response_body", providerHint) {
|
|
53349
|
+
if (!text2?.trim())
|
|
53350
|
+
return null;
|
|
53351
|
+
try {
|
|
53352
|
+
return extractTokenUsageFromJson(JSON.parse(text2), { source, providerHint });
|
|
53353
|
+
} catch {
|
|
53354
|
+
return null;
|
|
53355
|
+
}
|
|
53356
|
+
}
|
|
53357
|
+
function processSseMessage(dataLines, source, providerHint) {
|
|
53358
|
+
if (dataLines.length === 0)
|
|
53359
|
+
return null;
|
|
53360
|
+
const data = dataLines.join(`
|
|
53361
|
+
`).trim();
|
|
53362
|
+
if (!data || data === "[DONE]")
|
|
53363
|
+
return null;
|
|
53364
|
+
try {
|
|
53365
|
+
return extractTokenUsageFromJson(JSON.parse(data), {
|
|
53366
|
+
source,
|
|
53367
|
+
providerHint,
|
|
53368
|
+
rawUsagePathPrefix: source === "stream_file" ? "stream" : "stream"
|
|
53369
|
+
});
|
|
53370
|
+
} catch {
|
|
53371
|
+
return null;
|
|
53372
|
+
}
|
|
53373
|
+
}
|
|
53374
|
+
function extractTokenUsageFromSseText(text2, source = "stream_file", providerHint) {
|
|
53375
|
+
if (!text2?.trim())
|
|
53376
|
+
return null;
|
|
53377
|
+
let merged = null;
|
|
53378
|
+
let dataLines = [];
|
|
53379
|
+
const flush = () => {
|
|
53380
|
+
merged = mergeTokenUsageMetrics(merged, processSseMessage(dataLines, source, providerHint));
|
|
53381
|
+
dataLines = [];
|
|
53382
|
+
};
|
|
53383
|
+
for (const rawLine of text2.split(/\r?\n/)) {
|
|
53384
|
+
if (rawLine === "") {
|
|
53385
|
+
flush();
|
|
53386
|
+
continue;
|
|
53387
|
+
}
|
|
53388
|
+
if (rawLine.startsWith("data:")) {
|
|
53389
|
+
dataLines.push(rawLine.slice(5).trimStart());
|
|
53390
|
+
}
|
|
53391
|
+
}
|
|
53392
|
+
flush();
|
|
53393
|
+
return merged;
|
|
53394
|
+
}
|
|
53395
|
+
function createTokenUsageStreamCollector(providerHint) {
|
|
53396
|
+
const decoder = new TextDecoder;
|
|
53397
|
+
let buffer = "";
|
|
53398
|
+
let dataLines = [];
|
|
53399
|
+
let latest = null;
|
|
53400
|
+
const flushMessage = () => {
|
|
53401
|
+
latest = mergeTokenUsageMetrics(latest, processSseMessage(dataLines, "stream_chunk", providerHint));
|
|
53402
|
+
dataLines = [];
|
|
53403
|
+
};
|
|
53404
|
+
const processLine = (rawLine) => {
|
|
53405
|
+
const line = rawLine.replace(/\r$/, "");
|
|
53406
|
+
if (line === "") {
|
|
53407
|
+
flushMessage();
|
|
53408
|
+
return;
|
|
53409
|
+
}
|
|
53410
|
+
if (line.startsWith("data:")) {
|
|
53411
|
+
dataLines.push(line.slice(5).trimStart());
|
|
53412
|
+
}
|
|
53413
|
+
};
|
|
53414
|
+
return {
|
|
53415
|
+
addChunk(chunk) {
|
|
53416
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
53417
|
+
let newlineIndex = buffer.indexOf(`
|
|
53418
|
+
`);
|
|
53419
|
+
while (newlineIndex >= 0) {
|
|
53420
|
+
processLine(buffer.slice(0, newlineIndex));
|
|
53421
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
53422
|
+
newlineIndex = buffer.indexOf(`
|
|
53423
|
+
`);
|
|
53424
|
+
}
|
|
53425
|
+
},
|
|
53426
|
+
getUsage() {
|
|
53427
|
+
buffer += decoder.decode();
|
|
53428
|
+
if (buffer) {
|
|
53429
|
+
processLine(buffer);
|
|
53430
|
+
buffer = "";
|
|
53431
|
+
}
|
|
53432
|
+
flushMessage();
|
|
53433
|
+
return latest;
|
|
53434
|
+
}
|
|
53435
|
+
};
|
|
53436
|
+
}
|
|
53437
|
+
function safeReadStreamFile(streamFile, baseDir) {
|
|
53438
|
+
if (!streamFile)
|
|
53439
|
+
return { content: null, warning: null };
|
|
53440
|
+
try {
|
|
53441
|
+
const candidates = [streamFile];
|
|
53442
|
+
if (baseDir)
|
|
53443
|
+
candidates.push(resolve5(baseDir, streamFile));
|
|
53444
|
+
for (const candidate of candidates) {
|
|
53445
|
+
const resolved = resolve5(candidate);
|
|
53446
|
+
if (!resolved.endsWith(".sse.raw"))
|
|
53447
|
+
continue;
|
|
53448
|
+
if (!existsSync4(resolved))
|
|
53449
|
+
continue;
|
|
53450
|
+
const stats = statSync(resolved);
|
|
53451
|
+
if (stats.size > MAX_STREAM_USAGE_BYTES) {
|
|
53452
|
+
return {
|
|
53453
|
+
content: null,
|
|
53454
|
+
warning: `stream_file \u8D85\u8FC7 ${MAX_STREAM_USAGE_BYTES} \u5B57\u8282\uFF0C\u5DF2\u8DF3\u8FC7 token usage \u56DE\u586B`
|
|
53455
|
+
};
|
|
53456
|
+
}
|
|
53457
|
+
return { content: readFileSync6(resolved, "utf-8"), warning: null };
|
|
53458
|
+
}
|
|
53459
|
+
} catch (err) {
|
|
53460
|
+
return {
|
|
53461
|
+
content: null,
|
|
53462
|
+
warning: `stream_file token usage \u8BFB\u53D6\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
53463
|
+
};
|
|
53464
|
+
}
|
|
53465
|
+
return { content: null, warning: null };
|
|
53466
|
+
}
|
|
53467
|
+
function extractTokenUsageFromLogEvent(event, options = {}) {
|
|
53468
|
+
if (event.token_usage) {
|
|
53469
|
+
return {
|
|
53470
|
+
rawUsage: null,
|
|
53471
|
+
...event.token_usage,
|
|
53472
|
+
source: event.token_usage.source ?? "explicit"
|
|
53473
|
+
};
|
|
53474
|
+
}
|
|
53475
|
+
const providerHint = [event.provider, event.route_type, event.model_in, event.model_out].filter(Boolean).join(" ");
|
|
53476
|
+
const responseBodyUsage = extractTokenUsageFromResponseText(event.response_body, "response_body", providerHint);
|
|
53477
|
+
if (responseBodyUsage)
|
|
53478
|
+
return responseBodyUsage;
|
|
53479
|
+
const responseAfterPluginsUsage = extractTokenUsageFromResponseText(event.response_body_after_plugins, "response_body_after_plugins", providerHint);
|
|
53480
|
+
if (responseAfterPluginsUsage)
|
|
53481
|
+
return responseAfterPluginsUsage;
|
|
53482
|
+
const responseBeforePluginsUsage = extractTokenUsageFromResponseText(event.response_body_before_plugins, "response_body_before_plugins", providerHint);
|
|
53483
|
+
if (responseBeforePluginsUsage)
|
|
53484
|
+
return responseBeforePluginsUsage;
|
|
53485
|
+
const streamContent = options.streamContent ?? safeReadStreamFile(event.stream_file, options.baseDir).content;
|
|
53486
|
+
return extractTokenUsageFromSseText(streamContent ?? undefined, "stream_file", providerHint);
|
|
53487
|
+
}
|
|
53488
|
+
function extractTokenUsageSummaryFromLogEvent(event, options = {}) {
|
|
53489
|
+
const metrics = extractTokenUsageFromLogEvent(event, options);
|
|
53490
|
+
return metrics ? toTokenUsageSummary(metrics) : null;
|
|
53491
|
+
}
|
|
53492
|
+
function enrichLogEventTokenUsage(event, options = {}) {
|
|
53493
|
+
if (event.token_usage)
|
|
53494
|
+
return event;
|
|
53495
|
+
const tokenUsage = extractTokenUsageFromLogEvent(event, options);
|
|
53496
|
+
if (!tokenUsage)
|
|
53497
|
+
return event;
|
|
53498
|
+
return {
|
|
53499
|
+
...event,
|
|
53500
|
+
token_usage: tokenUsage
|
|
53501
|
+
};
|
|
53502
|
+
}
|
|
53503
|
+
var MAX_STREAM_USAGE_BYTES;
|
|
53504
|
+
var init_token_usage = __esm(() => {
|
|
53505
|
+
MAX_STREAM_USAGE_BYTES = 25 * 1024 * 1024;
|
|
53506
|
+
});
|
|
53507
|
+
|
|
53508
|
+
// src/log-metrics.ts
|
|
53509
|
+
import { createReadStream, existsSync as existsSync5 } from "fs";
|
|
53510
|
+
import { join as join6 } from "path";
|
|
53511
|
+
import { createInterface } from "readline";
|
|
53512
|
+
function isLogMetricsWindow(value) {
|
|
53513
|
+
return value === "1h" || value === "6h" || value === "24h";
|
|
53514
|
+
}
|
|
53515
|
+
function toPercent(numerator, denominator) {
|
|
53516
|
+
if (denominator <= 0)
|
|
53517
|
+
return 0;
|
|
53518
|
+
return Number((numerator / denominator * 100).toFixed(2));
|
|
53519
|
+
}
|
|
53520
|
+
function toDayStart(ms) {
|
|
53521
|
+
const date5 = new Date(ms);
|
|
53522
|
+
return Date.UTC(date5.getUTCFullYear(), date5.getUTCMonth(), date5.getUTCDate());
|
|
53523
|
+
}
|
|
53524
|
+
function listDateStrings(fromMs, toMs) {
|
|
53525
|
+
const result = [];
|
|
53526
|
+
for (let day = toDayStart(fromMs);day <= toDayStart(toMs); day += 24 * 60 * 60 * 1000) {
|
|
53527
|
+
result.push(new Date(day).toISOString().slice(0, 10));
|
|
53528
|
+
}
|
|
53529
|
+
return result;
|
|
53530
|
+
}
|
|
53531
|
+
function getStatusClass(event) {
|
|
53532
|
+
if (event.error_type)
|
|
53533
|
+
return "network_error";
|
|
53534
|
+
const status = event.upstream_status ?? 0;
|
|
53535
|
+
if (status >= 200 && status < 300)
|
|
53536
|
+
return "2xx";
|
|
53537
|
+
if (status >= 400 && status < 500)
|
|
53538
|
+
return "4xx";
|
|
53539
|
+
if (status >= 500)
|
|
53540
|
+
return "5xx";
|
|
53541
|
+
return "network_error";
|
|
53542
|
+
}
|
|
53543
|
+
function isErrorEvent(event) {
|
|
53544
|
+
if (event.error_type)
|
|
53545
|
+
return true;
|
|
53546
|
+
const status = event.upstream_status ?? 0;
|
|
53547
|
+
return status < 200 || status >= 400;
|
|
53548
|
+
}
|
|
53549
|
+
function percentile(sortedNumbers, ratio) {
|
|
53550
|
+
if (sortedNumbers.length === 0)
|
|
53551
|
+
return 0;
|
|
53552
|
+
const index = Math.min(sortedNumbers.length - 1, Math.ceil(sortedNumbers.length * ratio) - 1);
|
|
53553
|
+
return Math.round(sortedNumbers[index]);
|
|
53554
|
+
}
|
|
53555
|
+
function createEmptyMetrics2(window2, nowMs, source, warnings = []) {
|
|
53556
|
+
const fromMs = nowMs - WINDOW_MS[window2];
|
|
53557
|
+
const bucketMs = BUCKET_MS[window2];
|
|
53558
|
+
const bucketCount = Math.max(1, Math.ceil((nowMs - fromMs) / bucketMs));
|
|
53559
|
+
const series = Array.from({ length: bucketCount }, (_, i) => ({
|
|
53560
|
+
ts: new Date(fromMs + i * bucketMs).toISOString(),
|
|
53561
|
+
requests: 0,
|
|
53562
|
+
errors: 0,
|
|
53563
|
+
avgLatencyMs: 0
|
|
53564
|
+
}));
|
|
53565
|
+
return {
|
|
53566
|
+
window: window2,
|
|
53567
|
+
from: new Date(fromMs).toISOString(),
|
|
53568
|
+
to: new Date(nowMs).toISOString(),
|
|
53569
|
+
generatedAt: new Date(nowMs).toISOString(),
|
|
53570
|
+
source,
|
|
53571
|
+
summary: {
|
|
53572
|
+
totalRequests: 0,
|
|
53573
|
+
successRequests: 0,
|
|
53574
|
+
errorRequests: 0,
|
|
53575
|
+
successRate: 0,
|
|
53576
|
+
avgLatencyMs: 0,
|
|
53577
|
+
p95LatencyMs: 0,
|
|
53578
|
+
totalRequestBytes: 0,
|
|
53579
|
+
totalResponseBytes: 0
|
|
53580
|
+
},
|
|
53581
|
+
tokens: {
|
|
53582
|
+
usageCount: 0,
|
|
53583
|
+
inputTokens: 0,
|
|
53584
|
+
outputTokens: 0,
|
|
53585
|
+
totalTokens: 0,
|
|
53586
|
+
cachedInputTokens: 0,
|
|
53587
|
+
cacheHitInputTokens: 0,
|
|
53588
|
+
cacheHitRateDenominatorTokens: 0,
|
|
53589
|
+
cacheHitRate: 0,
|
|
53590
|
+
reasoningTokens: 0,
|
|
53591
|
+
cost: null
|
|
53592
|
+
},
|
|
53593
|
+
series,
|
|
53594
|
+
topProviders: [],
|
|
53595
|
+
topRouteTypes: [],
|
|
53596
|
+
statusClasses: {
|
|
53597
|
+
"2xx": 0,
|
|
53598
|
+
"4xx": 0,
|
|
53599
|
+
"5xx": 0,
|
|
53600
|
+
network_error: 0
|
|
53601
|
+
},
|
|
53602
|
+
warnings
|
|
53603
|
+
};
|
|
53604
|
+
}
|
|
53605
|
+
async function getLogMetrics(options) {
|
|
53606
|
+
const window2 = options.window ?? "24h";
|
|
53607
|
+
const refresh = options.refresh === true;
|
|
53608
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
53609
|
+
const logEnabled = options.logConfig?.enabled !== false && !!options.logConfig;
|
|
53610
|
+
if (!logEnabled) {
|
|
53611
|
+
return createEmptyMetrics2(window2, nowMs, {
|
|
53612
|
+
logEnabled: false,
|
|
53613
|
+
baseDir: null,
|
|
53614
|
+
filesScanned: 0,
|
|
53615
|
+
linesScanned: 0,
|
|
53616
|
+
partial: false
|
|
53617
|
+
}, ["\u65E5\u5FD7\u672A\u542F\u7528"]);
|
|
53734
53618
|
}
|
|
53735
|
-
|
|
53736
|
-
|
|
53737
|
-
|
|
53738
|
-
|
|
53739
|
-
|
|
53740
|
-
`).trim();
|
|
53741
|
-
if (!data || data === "[DONE]")
|
|
53742
|
-
return null;
|
|
53743
|
-
try {
|
|
53744
|
-
return extractTokenUsageFromJson(JSON.parse(data), {
|
|
53745
|
-
source,
|
|
53746
|
-
providerHint,
|
|
53747
|
-
rawUsagePathPrefix: source === "stream_file" ? "stream" : "stream"
|
|
53748
|
-
});
|
|
53749
|
-
} catch {
|
|
53750
|
-
return null;
|
|
53619
|
+
const baseDir = resolveLogBaseDir(options.logConfig);
|
|
53620
|
+
const cacheKey = `${baseDir}:${window2}`;
|
|
53621
|
+
const cached2 = metricsCache.get(cacheKey);
|
|
53622
|
+
if (!refresh && cached2 && cached2.expiresAt > nowMs) {
|
|
53623
|
+
return cached2.value;
|
|
53751
53624
|
}
|
|
53752
|
-
|
|
53753
|
-
|
|
53754
|
-
|
|
53755
|
-
|
|
53756
|
-
|
|
53757
|
-
|
|
53758
|
-
|
|
53759
|
-
|
|
53760
|
-
|
|
53761
|
-
|
|
53762
|
-
|
|
53763
|
-
if (rawLine === "") {
|
|
53764
|
-
flush();
|
|
53765
|
-
continue;
|
|
53766
|
-
}
|
|
53767
|
-
if (rawLine.startsWith("data:")) {
|
|
53768
|
-
dataLines.push(rawLine.slice(5).trimStart());
|
|
53769
|
-
}
|
|
53625
|
+
const eventsDir = join6(baseDir, "events");
|
|
53626
|
+
if (!existsSync5(eventsDir)) {
|
|
53627
|
+
const empty = createEmptyMetrics2(window2, nowMs, {
|
|
53628
|
+
logEnabled: true,
|
|
53629
|
+
baseDir,
|
|
53630
|
+
filesScanned: 0,
|
|
53631
|
+
linesScanned: 0,
|
|
53632
|
+
partial: false
|
|
53633
|
+
}, ["\u65E5\u5FD7\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u6682\u65E0\u53EF\u5206\u6790\u6570\u636E"]);
|
|
53634
|
+
metricsCache.set(cacheKey, { expiresAt: nowMs + CACHE_TTL_MS, value: empty });
|
|
53635
|
+
return empty;
|
|
53770
53636
|
}
|
|
53771
|
-
|
|
53772
|
-
|
|
53773
|
-
|
|
53774
|
-
|
|
53775
|
-
|
|
53776
|
-
|
|
53777
|
-
|
|
53778
|
-
|
|
53779
|
-
|
|
53780
|
-
|
|
53781
|
-
|
|
53637
|
+
const fromMs = nowMs - WINDOW_MS[window2];
|
|
53638
|
+
const bucketMs = BUCKET_MS[window2];
|
|
53639
|
+
const bucketCount = Math.max(1, Math.ceil((nowMs - fromMs) / bucketMs));
|
|
53640
|
+
const buckets = Array.from({ length: bucketCount }, () => ({
|
|
53641
|
+
requests: 0,
|
|
53642
|
+
errors: 0,
|
|
53643
|
+
latencySum: 0,
|
|
53644
|
+
latencyCount: 0
|
|
53645
|
+
}));
|
|
53646
|
+
const providerAgg = new Map;
|
|
53647
|
+
const routeTypeAgg = new Map;
|
|
53648
|
+
const latencies = [];
|
|
53649
|
+
const statusClasses = {
|
|
53650
|
+
"2xx": 0,
|
|
53651
|
+
"4xx": 0,
|
|
53652
|
+
"5xx": 0,
|
|
53653
|
+
network_error: 0
|
|
53782
53654
|
};
|
|
53783
|
-
|
|
53784
|
-
|
|
53785
|
-
|
|
53786
|
-
|
|
53787
|
-
|
|
53788
|
-
|
|
53789
|
-
|
|
53790
|
-
|
|
53655
|
+
let filesScanned = 0;
|
|
53656
|
+
let linesScanned = 0;
|
|
53657
|
+
let parseErrors = 0;
|
|
53658
|
+
let partial2 = false;
|
|
53659
|
+
let totalRequests = 0;
|
|
53660
|
+
let successRequests = 0;
|
|
53661
|
+
let errorRequests = 0;
|
|
53662
|
+
let totalLatency = 0;
|
|
53663
|
+
let totalRequestBytes = 0;
|
|
53664
|
+
let totalResponseBytes = 0;
|
|
53665
|
+
let tokenUsageCount = 0;
|
|
53666
|
+
let tokenInput = 0;
|
|
53667
|
+
let tokenOutput = 0;
|
|
53668
|
+
let tokenTotal = 0;
|
|
53669
|
+
let tokenCachedInput = 0;
|
|
53670
|
+
let tokenCacheHitInput = 0;
|
|
53671
|
+
let tokenCacheHitDenominator = 0;
|
|
53672
|
+
let tokenReasoning = 0;
|
|
53673
|
+
let tokenCost = 0;
|
|
53674
|
+
let tokenCostSeen = false;
|
|
53675
|
+
const warnings = [];
|
|
53676
|
+
const dateStrings = listDateStrings(fromMs, nowMs);
|
|
53677
|
+
for (const dateStr of dateStrings) {
|
|
53678
|
+
if (linesScanned >= MAX_LINES_SCANNED) {
|
|
53679
|
+
partial2 = true;
|
|
53680
|
+
break;
|
|
53791
53681
|
}
|
|
53792
|
-
|
|
53793
|
-
|
|
53794
|
-
|
|
53795
|
-
|
|
53796
|
-
|
|
53797
|
-
|
|
53798
|
-
|
|
53799
|
-
|
|
53800
|
-
|
|
53801
|
-
|
|
53802
|
-
|
|
53682
|
+
const filePath = join6(eventsDir, `${dateStr}.jsonl`);
|
|
53683
|
+
if (!existsSync5(filePath))
|
|
53684
|
+
continue;
|
|
53685
|
+
filesScanned += 1;
|
|
53686
|
+
try {
|
|
53687
|
+
const stream = createReadStream(filePath, { encoding: "utf-8" });
|
|
53688
|
+
const rl = createInterface({ input: stream, crlfDelay: Number.POSITIVE_INFINITY });
|
|
53689
|
+
for await (const line of rl) {
|
|
53690
|
+
if (linesScanned >= MAX_LINES_SCANNED) {
|
|
53691
|
+
partial2 = true;
|
|
53692
|
+
rl.close();
|
|
53693
|
+
stream.destroy();
|
|
53694
|
+
break;
|
|
53695
|
+
}
|
|
53696
|
+
linesScanned += 1;
|
|
53697
|
+
if (!line.trim())
|
|
53698
|
+
continue;
|
|
53699
|
+
let event;
|
|
53700
|
+
try {
|
|
53701
|
+
event = JSON.parse(line);
|
|
53702
|
+
} catch {
|
|
53703
|
+
parseErrors += 1;
|
|
53704
|
+
continue;
|
|
53705
|
+
}
|
|
53706
|
+
if (!event.ts_start)
|
|
53707
|
+
continue;
|
|
53708
|
+
const ts = Date.parse(event.ts_start);
|
|
53709
|
+
if (!Number.isFinite(ts) || ts < fromMs || ts > nowMs)
|
|
53710
|
+
continue;
|
|
53711
|
+
totalRequests += 1;
|
|
53712
|
+
const isError = isErrorEvent(event);
|
|
53713
|
+
if (isError) {
|
|
53714
|
+
errorRequests += 1;
|
|
53715
|
+
} else {
|
|
53716
|
+
successRequests += 1;
|
|
53717
|
+
}
|
|
53718
|
+
const latency = Number.isFinite(event.latency_ms) ? Math.max(0, event.latency_ms ?? 0) : 0;
|
|
53719
|
+
totalLatency += latency;
|
|
53720
|
+
latencies.push(latency);
|
|
53721
|
+
totalRequestBytes += Math.max(0, event.request_bytes ?? 0);
|
|
53722
|
+
totalResponseBytes += Math.max(0, event.response_bytes ?? 0) + Math.max(0, event.stream_bytes ?? 0);
|
|
53723
|
+
const bucketIndex = Math.min(bucketCount - 1, Math.max(0, Math.floor((ts - fromMs) / bucketMs)));
|
|
53724
|
+
const bucket = buckets[bucketIndex];
|
|
53725
|
+
bucket.requests += 1;
|
|
53726
|
+
bucket.latencySum += latency;
|
|
53727
|
+
bucket.latencyCount += 1;
|
|
53728
|
+
if (isError)
|
|
53729
|
+
bucket.errors += 1;
|
|
53730
|
+
const providerKey = event.provider || "unknown";
|
|
53731
|
+
const providerRow = providerAgg.get(providerKey) ?? {
|
|
53732
|
+
requests: 0,
|
|
53733
|
+
errors: 0,
|
|
53734
|
+
latencySum: 0
|
|
53735
|
+
};
|
|
53736
|
+
providerRow.requests += 1;
|
|
53737
|
+
providerRow.latencySum += latency;
|
|
53738
|
+
if (isError)
|
|
53739
|
+
providerRow.errors += 1;
|
|
53740
|
+
providerAgg.set(providerKey, providerRow);
|
|
53741
|
+
const routeTypeKey = event.route_type || "unknown";
|
|
53742
|
+
const routeTypeRow = routeTypeAgg.get(routeTypeKey) ?? {
|
|
53743
|
+
requests: 0,
|
|
53744
|
+
errors: 0,
|
|
53745
|
+
latencySum: 0
|
|
53746
|
+
};
|
|
53747
|
+
routeTypeRow.requests += 1;
|
|
53748
|
+
routeTypeRow.latencySum += latency;
|
|
53749
|
+
if (isError)
|
|
53750
|
+
routeTypeRow.errors += 1;
|
|
53751
|
+
routeTypeAgg.set(routeTypeKey, routeTypeRow);
|
|
53752
|
+
statusClasses[getStatusClass(event)] += 1;
|
|
53753
|
+
const usage = extractTokenUsageSummaryFromLogEvent(event, { baseDir });
|
|
53754
|
+
if (usage) {
|
|
53755
|
+
tokenUsageCount += 1;
|
|
53756
|
+
tokenInput += Math.max(0, usage.inputTokens ?? 0);
|
|
53757
|
+
tokenOutput += Math.max(0, usage.outputTokens ?? 0);
|
|
53758
|
+
tokenTotal += Math.max(0, usage.totalTokens ?? 0);
|
|
53759
|
+
tokenCachedInput += Math.max(0, usage.cachedInputTokens ?? 0);
|
|
53760
|
+
tokenCacheHitInput += Math.max(0, usage.cacheHitInputTokens ?? 0);
|
|
53761
|
+
tokenCacheHitDenominator += Math.max(0, usage.cacheHitRateDenominatorTokens ?? 0);
|
|
53762
|
+
tokenReasoning += Math.max(0, usage.reasoningTokens ?? 0);
|
|
53763
|
+
if (typeof usage.cost === "number" && Number.isFinite(usage.cost)) {
|
|
53764
|
+
tokenCost += usage.cost;
|
|
53765
|
+
tokenCostSeen = true;
|
|
53766
|
+
}
|
|
53767
|
+
}
|
|
53803
53768
|
}
|
|
53769
|
+
} catch (err) {
|
|
53770
|
+
warnings.push(`\u8BFB\u53D6\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25: ${filePath} (${err instanceof Error ? err.message : String(err)})`);
|
|
53771
|
+
partial2 = true;
|
|
53772
|
+
}
|
|
53773
|
+
}
|
|
53774
|
+
if (parseErrors > 0) {
|
|
53775
|
+
warnings.push(`\u5DF2\u8DF3\u8FC7 ${parseErrors} \u884C\u65E0\u6548 JSON \u65E5\u5FD7`);
|
|
53776
|
+
}
|
|
53777
|
+
if (partial2) {
|
|
53778
|
+
warnings.push("\u65E5\u5FD7\u626B\u63CF\u5DF2\u90E8\u5206\u622A\u65AD\uFF0C\u7ED3\u679C\u53EF\u80FD\u4E0D\u5B8C\u6574");
|
|
53779
|
+
}
|
|
53780
|
+
latencies.sort((a, b) => a - b);
|
|
53781
|
+
const series = buckets.map((bucket, index) => ({
|
|
53782
|
+
ts: new Date(fromMs + index * bucketMs).toISOString(),
|
|
53783
|
+
requests: bucket.requests,
|
|
53784
|
+
errors: bucket.errors,
|
|
53785
|
+
avgLatencyMs: bucket.latencyCount > 0 ? Math.round(bucket.latencySum / bucket.latencyCount) : 0
|
|
53786
|
+
}));
|
|
53787
|
+
const topProviders = Array.from(providerAgg.entries()).map(([key, row]) => ({
|
|
53788
|
+
key,
|
|
53789
|
+
requests: row.requests,
|
|
53790
|
+
errorRate: toPercent(row.errors, row.requests),
|
|
53791
|
+
avgLatencyMs: row.requests > 0 ? Math.round(row.latencySum / row.requests) : 0
|
|
53792
|
+
})).sort((a, b) => b.requests - a.requests).slice(0, TOP_LIMIT);
|
|
53793
|
+
const topRouteTypes = Array.from(routeTypeAgg.entries()).map(([key, row]) => ({
|
|
53794
|
+
key,
|
|
53795
|
+
requests: row.requests,
|
|
53796
|
+
errorRate: toPercent(row.errors, row.requests)
|
|
53797
|
+
})).sort((a, b) => b.requests - a.requests).slice(0, TOP_LIMIT);
|
|
53798
|
+
const response = {
|
|
53799
|
+
window: window2,
|
|
53800
|
+
from: new Date(fromMs).toISOString(),
|
|
53801
|
+
to: new Date(nowMs).toISOString(),
|
|
53802
|
+
generatedAt: new Date(nowMs).toISOString(),
|
|
53803
|
+
source: {
|
|
53804
|
+
logEnabled: true,
|
|
53805
|
+
baseDir,
|
|
53806
|
+
filesScanned,
|
|
53807
|
+
linesScanned,
|
|
53808
|
+
partial: partial2
|
|
53809
|
+
},
|
|
53810
|
+
summary: {
|
|
53811
|
+
totalRequests,
|
|
53812
|
+
successRequests,
|
|
53813
|
+
errorRequests,
|
|
53814
|
+
successRate: toPercent(successRequests, totalRequests),
|
|
53815
|
+
avgLatencyMs: totalRequests > 0 ? Math.round(totalLatency / totalRequests) : 0,
|
|
53816
|
+
p95LatencyMs: percentile(latencies, 0.95),
|
|
53817
|
+
totalRequestBytes,
|
|
53818
|
+
totalResponseBytes
|
|
53804
53819
|
},
|
|
53805
|
-
|
|
53806
|
-
|
|
53807
|
-
|
|
53808
|
-
|
|
53809
|
-
|
|
53810
|
-
|
|
53811
|
-
|
|
53812
|
-
|
|
53813
|
-
|
|
53820
|
+
tokens: {
|
|
53821
|
+
usageCount: tokenUsageCount,
|
|
53822
|
+
inputTokens: tokenInput,
|
|
53823
|
+
outputTokens: tokenOutput,
|
|
53824
|
+
totalTokens: tokenTotal,
|
|
53825
|
+
cachedInputTokens: tokenCachedInput,
|
|
53826
|
+
cacheHitInputTokens: tokenCacheHitInput,
|
|
53827
|
+
cacheHitRateDenominatorTokens: tokenCacheHitDenominator,
|
|
53828
|
+
cacheHitRate: toPercent(tokenCacheHitInput, tokenCacheHitDenominator),
|
|
53829
|
+
reasoningTokens: tokenReasoning,
|
|
53830
|
+
cost: tokenCostSeen ? Number(tokenCost.toFixed(6)) : null
|
|
53831
|
+
},
|
|
53832
|
+
series,
|
|
53833
|
+
topProviders,
|
|
53834
|
+
topRouteTypes,
|
|
53835
|
+
statusClasses,
|
|
53836
|
+
warnings
|
|
53814
53837
|
};
|
|
53838
|
+
metricsCache.set(cacheKey, {
|
|
53839
|
+
expiresAt: nowMs + CACHE_TTL_MS,
|
|
53840
|
+
value: response
|
|
53841
|
+
});
|
|
53842
|
+
return response;
|
|
53815
53843
|
}
|
|
53816
|
-
|
|
53817
|
-
|
|
53818
|
-
|
|
53819
|
-
|
|
53820
|
-
|
|
53821
|
-
|
|
53822
|
-
|
|
53823
|
-
|
|
53824
|
-
|
|
53825
|
-
|
|
53826
|
-
|
|
53827
|
-
|
|
53828
|
-
|
|
53829
|
-
|
|
53830
|
-
|
|
53831
|
-
|
|
53832
|
-
|
|
53833
|
-
|
|
53834
|
-
|
|
53835
|
-
|
|
53836
|
-
|
|
53837
|
-
|
|
53838
|
-
|
|
53844
|
+
var WINDOW_MS, BUCKET_MS, TOP_LIMIT = 5, MAX_LINES_SCANNED = 250000, CACHE_TTL_MS = 15000, metricsCache;
|
|
53845
|
+
var init_log_metrics = __esm(() => {
|
|
53846
|
+
init_config();
|
|
53847
|
+
init_token_usage();
|
|
53848
|
+
WINDOW_MS = {
|
|
53849
|
+
"1h": 60 * 60 * 1000,
|
|
53850
|
+
"6h": 6 * 60 * 60 * 1000,
|
|
53851
|
+
"24h": 24 * 60 * 60 * 1000
|
|
53852
|
+
};
|
|
53853
|
+
BUCKET_MS = {
|
|
53854
|
+
"1h": 5 * 60 * 1000,
|
|
53855
|
+
"6h": 15 * 60 * 1000,
|
|
53856
|
+
"24h": 30 * 60 * 1000
|
|
53857
|
+
};
|
|
53858
|
+
metricsCache = new Map;
|
|
53859
|
+
});
|
|
53860
|
+
|
|
53861
|
+
// src/log-session-identity.ts
|
|
53862
|
+
function toRecord(value) {
|
|
53863
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
53864
|
+
return null;
|
|
53865
|
+
return value;
|
|
53866
|
+
}
|
|
53867
|
+
function extractUserIdRawFromRequestBody(requestBody) {
|
|
53868
|
+
const requestBodyRecord = toRecord(requestBody);
|
|
53869
|
+
const metadata = toRecord(requestBodyRecord?.metadata);
|
|
53870
|
+
if (!metadata) {
|
|
53839
53871
|
return {
|
|
53840
|
-
|
|
53841
|
-
|
|
53872
|
+
hasMetadata: false,
|
|
53873
|
+
userIdRaw: null
|
|
53842
53874
|
};
|
|
53843
53875
|
}
|
|
53844
|
-
|
|
53845
|
-
|
|
53846
|
-
function extractTokenUsageFromLogEvent(event, options = {}) {
|
|
53847
|
-
if (event.token_usage) {
|
|
53876
|
+
const userId = metadata.user_id;
|
|
53877
|
+
if (typeof userId !== "string" || userId.trim() === "") {
|
|
53848
53878
|
return {
|
|
53849
|
-
|
|
53850
|
-
|
|
53851
|
-
source: event.token_usage.source ?? "explicit"
|
|
53879
|
+
hasMetadata: true,
|
|
53880
|
+
userIdRaw: null
|
|
53852
53881
|
};
|
|
53853
53882
|
}
|
|
53854
|
-
|
|
53855
|
-
|
|
53856
|
-
|
|
53857
|
-
|
|
53858
|
-
const responseAfterPluginsUsage = extractTokenUsageFromResponseText(event.response_body_after_plugins, "response_body_after_plugins", providerHint);
|
|
53859
|
-
if (responseAfterPluginsUsage)
|
|
53860
|
-
return responseAfterPluginsUsage;
|
|
53861
|
-
const responseBeforePluginsUsage = extractTokenUsageFromResponseText(event.response_body_before_plugins, "response_body_before_plugins", providerHint);
|
|
53862
|
-
if (responseBeforePluginsUsage)
|
|
53863
|
-
return responseBeforePluginsUsage;
|
|
53864
|
-
const streamContent = options.streamContent ?? safeReadStreamFile(event.stream_file, options.baseDir).content;
|
|
53865
|
-
return extractTokenUsageFromSseText(streamContent ?? undefined, "stream_file", providerHint);
|
|
53883
|
+
return {
|
|
53884
|
+
hasMetadata: true,
|
|
53885
|
+
userIdRaw: userId
|
|
53886
|
+
};
|
|
53866
53887
|
}
|
|
53867
|
-
function
|
|
53868
|
-
|
|
53869
|
-
|
|
53888
|
+
function parseUserSessionFromJsonFormat(userIdRaw) {
|
|
53889
|
+
let parsed;
|
|
53890
|
+
try {
|
|
53891
|
+
parsed = JSON.parse(userIdRaw);
|
|
53892
|
+
} catch {
|
|
53893
|
+
return null;
|
|
53894
|
+
}
|
|
53895
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
53896
|
+
return null;
|
|
53897
|
+
const obj = parsed;
|
|
53898
|
+
const sessionId = typeof obj.session_id === "string" ? obj.session_id.trim() : "";
|
|
53899
|
+
if (!sessionId)
|
|
53900
|
+
return null;
|
|
53901
|
+
const userKey = (typeof obj.account_uuid === "string" ? obj.account_uuid.trim() : "") || (typeof obj.device_id === "string" ? obj.device_id.trim() : "");
|
|
53902
|
+
return { userKey: userKey || sessionId, sessionId };
|
|
53870
53903
|
}
|
|
53871
|
-
function
|
|
53872
|
-
if (
|
|
53873
|
-
return
|
|
53874
|
-
|
|
53875
|
-
|
|
53876
|
-
|
|
53904
|
+
function parseUserSessionFromUserIdRaw(userIdRaw) {
|
|
53905
|
+
if (userIdRaw.trimStart().startsWith("{")) {
|
|
53906
|
+
return parseUserSessionFromJsonFormat(userIdRaw);
|
|
53907
|
+
}
|
|
53908
|
+
const index = userIdRaw.indexOf(USER_SESSION_DELIMITER);
|
|
53909
|
+
if (index <= 0)
|
|
53910
|
+
return null;
|
|
53911
|
+
const userKey = userIdRaw.slice(0, index).trim();
|
|
53912
|
+
const sessionId = userIdRaw.slice(index + USER_SESSION_DELIMITER.length).trim();
|
|
53913
|
+
if (!userKey || !sessionId)
|
|
53914
|
+
return null;
|
|
53915
|
+
return { userKey, sessionId };
|
|
53916
|
+
}
|
|
53917
|
+
function resolveLogSessionIdentity(requestBody) {
|
|
53918
|
+
const { hasMetadata, userIdRaw } = extractUserIdRawFromRequestBody(requestBody);
|
|
53919
|
+
if (!userIdRaw) {
|
|
53920
|
+
return {
|
|
53921
|
+
hasMetadata,
|
|
53922
|
+
userIdRaw: null,
|
|
53923
|
+
userKey: null,
|
|
53924
|
+
sessionId: null
|
|
53925
|
+
};
|
|
53926
|
+
}
|
|
53927
|
+
const parsed = parseUserSessionFromUserIdRaw(userIdRaw);
|
|
53877
53928
|
return {
|
|
53878
|
-
|
|
53879
|
-
|
|
53929
|
+
hasMetadata,
|
|
53930
|
+
userIdRaw,
|
|
53931
|
+
userKey: parsed?.userKey ?? null,
|
|
53932
|
+
sessionId: parsed?.sessionId ?? null
|
|
53880
53933
|
};
|
|
53881
53934
|
}
|
|
53882
|
-
var
|
|
53883
|
-
var init_token_usage = __esm(() => {
|
|
53884
|
-
MAX_STREAM_USAGE_BYTES = 25 * 1024 * 1024;
|
|
53885
|
-
});
|
|
53935
|
+
var USER_SESSION_DELIMITER = "_account__session_";
|
|
53886
53936
|
|
|
53887
53937
|
// src/log-index.ts
|
|
53888
53938
|
import { Database } from "bun:sqlite";
|
|
@@ -54275,6 +54325,31 @@ function buildWhereClause(query, options = {}) {
|
|
|
54275
54325
|
usesFts
|
|
54276
54326
|
};
|
|
54277
54327
|
}
|
|
54328
|
+
function buildSessionsWhereClause(query) {
|
|
54329
|
+
const pseudo = {
|
|
54330
|
+
fromMs: query.fromMs,
|
|
54331
|
+
toMs: query.toMs,
|
|
54332
|
+
levels: [],
|
|
54333
|
+
providers: [],
|
|
54334
|
+
routeTypes: [],
|
|
54335
|
+
models: [],
|
|
54336
|
+
modelIns: [],
|
|
54337
|
+
modelOuts: [],
|
|
54338
|
+
users: query.users,
|
|
54339
|
+
sessions: query.sessions,
|
|
54340
|
+
statusClasses: [],
|
|
54341
|
+
hasError: null,
|
|
54342
|
+
q: query.q,
|
|
54343
|
+
sort: "time_desc",
|
|
54344
|
+
limit: 1,
|
|
54345
|
+
cursor: null
|
|
54346
|
+
};
|
|
54347
|
+
const { whereSql, params } = buildWhereClause(pseudo);
|
|
54348
|
+
return { whereSql, params };
|
|
54349
|
+
}
|
|
54350
|
+
function sortIndexedCountItems(map2) {
|
|
54351
|
+
return Array.from(map2.entries()).map(([key, count]) => ({ key, count })).sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));
|
|
54352
|
+
}
|
|
54278
54353
|
|
|
54279
54354
|
class LogIndex {
|
|
54280
54355
|
baseDir;
|
|
@@ -54482,6 +54557,163 @@ class LogIndex {
|
|
|
54482
54557
|
}
|
|
54483
54558
|
};
|
|
54484
54559
|
}
|
|
54560
|
+
querySessions(query) {
|
|
54561
|
+
const startedAt = performance.now();
|
|
54562
|
+
const { whereSql, params } = buildSessionsWhereClause(query);
|
|
54563
|
+
const aggregatedWhere = `${whereSql} AND e.user_key IS NOT NULL AND e.session_id IS NOT NULL`;
|
|
54564
|
+
const summaryRow = this.db.query(`
|
|
54565
|
+
SELECT
|
|
54566
|
+
COUNT(*) AS totalRequests,
|
|
54567
|
+
COALESCE(SUM(has_metadata), 0) AS metadataRequests,
|
|
54568
|
+
COUNT(DISTINCT user_key) AS uniqueUsers,
|
|
54569
|
+
COUNT(DISTINCT CASE
|
|
54570
|
+
WHEN user_key IS NOT NULL AND session_id IS NOT NULL
|
|
54571
|
+
THEN user_key || ' ' || session_id
|
|
54572
|
+
END) AS uniqueSessions
|
|
54573
|
+
FROM log_events e
|
|
54574
|
+
${whereSql}
|
|
54575
|
+
`).get(...params);
|
|
54576
|
+
const userRows = this.db.query(`
|
|
54577
|
+
SELECT
|
|
54578
|
+
user_key AS userKey,
|
|
54579
|
+
COUNT(*) AS requestCount,
|
|
54580
|
+
MIN(ts_ms) AS firstMs,
|
|
54581
|
+
MAX(ts_ms) AS lastMs,
|
|
54582
|
+
COUNT(DISTINCT session_id) AS sessionCount
|
|
54583
|
+
FROM log_events e
|
|
54584
|
+
${aggregatedWhere}
|
|
54585
|
+
GROUP BY user_key
|
|
54586
|
+
`).all(...params);
|
|
54587
|
+
const sessionRows = this.db.query(`
|
|
54588
|
+
SELECT
|
|
54589
|
+
user_key AS userKey,
|
|
54590
|
+
session_id AS sessionId,
|
|
54591
|
+
COUNT(*) AS requestCount,
|
|
54592
|
+
MIN(ts_ms) AS firstMs,
|
|
54593
|
+
MAX(ts_ms) AS lastMs
|
|
54594
|
+
FROM log_events e
|
|
54595
|
+
${aggregatedWhere}
|
|
54596
|
+
GROUP BY user_key, session_id
|
|
54597
|
+
`).all(...params);
|
|
54598
|
+
const userModelRows = this.db.query(`
|
|
54599
|
+
SELECT user_key AS userKey, model AS key, COUNT(*) AS count
|
|
54600
|
+
FROM log_events e
|
|
54601
|
+
${aggregatedWhere}
|
|
54602
|
+
GROUP BY user_key, model
|
|
54603
|
+
`).all(...params);
|
|
54604
|
+
const userProviderRows = this.db.query(`
|
|
54605
|
+
SELECT user_key AS userKey, provider AS key, COUNT(*) AS count
|
|
54606
|
+
FROM log_events e
|
|
54607
|
+
${aggregatedWhere}
|
|
54608
|
+
GROUP BY user_key, provider
|
|
54609
|
+
`).all(...params);
|
|
54610
|
+
const userRouteRows = this.db.query(`
|
|
54611
|
+
SELECT user_key AS userKey, route_type AS key, COUNT(*) AS count
|
|
54612
|
+
FROM log_events e
|
|
54613
|
+
${aggregatedWhere}
|
|
54614
|
+
GROUP BY user_key, route_type
|
|
54615
|
+
`).all(...params);
|
|
54616
|
+
const sessionModelRows = this.db.query(`
|
|
54617
|
+
SELECT user_key AS userKey, session_id AS sessionId, model AS key, COUNT(*) AS count
|
|
54618
|
+
FROM log_events e
|
|
54619
|
+
${aggregatedWhere}
|
|
54620
|
+
GROUP BY user_key, session_id, model
|
|
54621
|
+
`).all(...params);
|
|
54622
|
+
const latestRows = this.db.query(`
|
|
54623
|
+
SELECT userKey, sessionId, request_id AS latestRequestId
|
|
54624
|
+
FROM (
|
|
54625
|
+
SELECT
|
|
54626
|
+
user_key AS userKey,
|
|
54627
|
+
session_id AS sessionId,
|
|
54628
|
+
request_id,
|
|
54629
|
+
ROW_NUMBER() OVER (
|
|
54630
|
+
PARTITION BY user_key, session_id ORDER BY ts_ms DESC, id DESC
|
|
54631
|
+
) AS rn
|
|
54632
|
+
FROM log_events e
|
|
54633
|
+
${aggregatedWhere}
|
|
54634
|
+
)
|
|
54635
|
+
WHERE rn = 1
|
|
54636
|
+
`).all(...params);
|
|
54637
|
+
const userModels = new Map;
|
|
54638
|
+
const userProviders = new Map;
|
|
54639
|
+
const userRoutes = new Map;
|
|
54640
|
+
const sessionModels = new Map;
|
|
54641
|
+
const latestBySession = new Map;
|
|
54642
|
+
const addCount = (target, groupKey, key, count) => {
|
|
54643
|
+
if (!key)
|
|
54644
|
+
return;
|
|
54645
|
+
let inner = target.get(groupKey);
|
|
54646
|
+
if (!inner) {
|
|
54647
|
+
inner = new Map;
|
|
54648
|
+
target.set(groupKey, inner);
|
|
54649
|
+
}
|
|
54650
|
+
inner.set(key, count);
|
|
54651
|
+
};
|
|
54652
|
+
for (const row of userModelRows)
|
|
54653
|
+
addCount(userModels, row.userKey, row.key, row.count);
|
|
54654
|
+
for (const row of userProviderRows)
|
|
54655
|
+
addCount(userProviders, row.userKey, row.key, row.count);
|
|
54656
|
+
for (const row of userRouteRows)
|
|
54657
|
+
addCount(userRoutes, row.userKey, row.key, row.count);
|
|
54658
|
+
for (const row of sessionModelRows) {
|
|
54659
|
+
addCount(sessionModels, `${row.userKey}\x00${row.sessionId}`, row.key, row.count);
|
|
54660
|
+
}
|
|
54661
|
+
for (const row of latestRows) {
|
|
54662
|
+
latestBySession.set(`${row.userKey}\x00${row.sessionId}`, row.latestRequestId);
|
|
54663
|
+
}
|
|
54664
|
+
const sessionsByUser = new Map;
|
|
54665
|
+
for (const row of sessionRows) {
|
|
54666
|
+
const sessionKey = `${row.userKey}\x00${row.sessionId}`;
|
|
54667
|
+
const session = {
|
|
54668
|
+
sessionId: row.sessionId,
|
|
54669
|
+
requestCount: row.requestCount,
|
|
54670
|
+
firstSeenAt: new Date(row.firstMs).toISOString(),
|
|
54671
|
+
lastSeenAt: new Date(row.lastMs).toISOString(),
|
|
54672
|
+
models: sortIndexedCountItems(sessionModels.get(sessionKey) ?? new Map),
|
|
54673
|
+
latestRequestId: latestBySession.get(sessionKey) ?? ""
|
|
54674
|
+
};
|
|
54675
|
+
const list = sessionsByUser.get(row.userKey);
|
|
54676
|
+
if (list) {
|
|
54677
|
+
list.push(session);
|
|
54678
|
+
} else {
|
|
54679
|
+
sessionsByUser.set(row.userKey, [session]);
|
|
54680
|
+
}
|
|
54681
|
+
}
|
|
54682
|
+
const users = userRows.map((row) => {
|
|
54683
|
+
const sessions = (sessionsByUser.get(row.userKey) ?? []).sort((a, b) => {
|
|
54684
|
+
if (a.requestCount !== b.requestCount)
|
|
54685
|
+
return b.requestCount - a.requestCount;
|
|
54686
|
+
return Date.parse(b.lastSeenAt) - Date.parse(a.lastSeenAt);
|
|
54687
|
+
});
|
|
54688
|
+
return {
|
|
54689
|
+
userKey: row.userKey,
|
|
54690
|
+
requestCount: row.requestCount,
|
|
54691
|
+
sessionCount: row.sessionCount,
|
|
54692
|
+
firstSeenAt: new Date(row.firstMs).toISOString(),
|
|
54693
|
+
lastSeenAt: new Date(row.lastMs).toISOString(),
|
|
54694
|
+
models: sortIndexedCountItems(userModels.get(row.userKey) ?? new Map),
|
|
54695
|
+
providers: sortIndexedCountItems(userProviders.get(row.userKey) ?? new Map),
|
|
54696
|
+
routeTypes: sortIndexedCountItems(userRoutes.get(row.userKey) ?? new Map),
|
|
54697
|
+
sessions
|
|
54698
|
+
};
|
|
54699
|
+
}).sort((a, b) => {
|
|
54700
|
+
if (a.requestCount !== b.requestCount)
|
|
54701
|
+
return b.requestCount - a.requestCount;
|
|
54702
|
+
return Date.parse(b.lastSeenAt) - Date.parse(a.lastSeenAt);
|
|
54703
|
+
});
|
|
54704
|
+
return {
|
|
54705
|
+
from: new Date(query.fromMs).toISOString(),
|
|
54706
|
+
to: new Date(query.toMs).toISOString(),
|
|
54707
|
+
summary: {
|
|
54708
|
+
totalRequests: Number(summaryRow.totalRequests) || 0,
|
|
54709
|
+
metadataRequests: Number(summaryRow.metadataRequests) || 0,
|
|
54710
|
+
uniqueUsers: Number(summaryRow.uniqueUsers) || 0,
|
|
54711
|
+
uniqueSessions: Number(summaryRow.uniqueSessions) || 0
|
|
54712
|
+
},
|
|
54713
|
+
users,
|
|
54714
|
+
queryMs: Math.round((performance.now() - startedAt) * 100) / 100
|
|
54715
|
+
};
|
|
54716
|
+
}
|
|
54485
54717
|
configure() {
|
|
54486
54718
|
this.db.exec(`
|
|
54487
54719
|
PRAGMA journal_mode = WAL;
|
|
@@ -54864,6 +55096,65 @@ async function queryIndexedLogEvents(logConfig, query) {
|
|
|
54864
55096
|
};
|
|
54865
55097
|
}
|
|
54866
55098
|
}
|
|
55099
|
+
async function queryIndexedLogSessions(logConfig, query) {
|
|
55100
|
+
if (!logConfig || logConfig.enabled === false) {
|
|
55101
|
+
return {
|
|
55102
|
+
from: new Date(query.fromMs).toISOString(),
|
|
55103
|
+
to: new Date(query.toMs).toISOString(),
|
|
55104
|
+
summary: { totalRequests: 0, metadataRequests: 0, uniqueUsers: 0, uniqueSessions: 0 },
|
|
55105
|
+
users: [],
|
|
55106
|
+
meta: {
|
|
55107
|
+
scannedFiles: 0,
|
|
55108
|
+
scannedLines: 0,
|
|
55109
|
+
parseErrors: 0,
|
|
55110
|
+
truncated: false,
|
|
55111
|
+
indexUsed: true,
|
|
55112
|
+
indexFresh: true,
|
|
55113
|
+
queryMs: 0
|
|
55114
|
+
}
|
|
55115
|
+
};
|
|
55116
|
+
}
|
|
55117
|
+
const baseDir = resolveLogBaseDir(logConfig);
|
|
55118
|
+
const index = getLogIndex(baseDir);
|
|
55119
|
+
if (!index)
|
|
55120
|
+
return null;
|
|
55121
|
+
try {
|
|
55122
|
+
const freshness = await index.ensureRangeIndexed(query.fromMs, query.toMs);
|
|
55123
|
+
const result = index.querySessions(query);
|
|
55124
|
+
return {
|
|
55125
|
+
from: result.from,
|
|
55126
|
+
to: result.to,
|
|
55127
|
+
summary: result.summary,
|
|
55128
|
+
users: result.users,
|
|
55129
|
+
meta: {
|
|
55130
|
+
scannedFiles: freshness.scannedFiles,
|
|
55131
|
+
scannedLines: freshness.scannedLines,
|
|
55132
|
+
parseErrors: freshness.parseErrors,
|
|
55133
|
+
truncated: false,
|
|
55134
|
+
indexUsed: true,
|
|
55135
|
+
indexFresh: true,
|
|
55136
|
+
queryMs: result.queryMs
|
|
55137
|
+
}
|
|
55138
|
+
};
|
|
55139
|
+
} catch (err) {
|
|
55140
|
+
return {
|
|
55141
|
+
from: new Date(query.fromMs).toISOString(),
|
|
55142
|
+
to: new Date(query.toMs).toISOString(),
|
|
55143
|
+
summary: { totalRequests: 0, metadataRequests: 0, uniqueUsers: 0, uniqueSessions: 0 },
|
|
55144
|
+
users: [],
|
|
55145
|
+
meta: {
|
|
55146
|
+
scannedFiles: 0,
|
|
55147
|
+
scannedLines: 0,
|
|
55148
|
+
parseErrors: 0,
|
|
55149
|
+
truncated: false,
|
|
55150
|
+
indexUsed: false,
|
|
55151
|
+
indexFresh: false,
|
|
55152
|
+
queryMs: 0,
|
|
55153
|
+
fallbackReason: err instanceof Error ? err.message : String(err)
|
|
55154
|
+
}
|
|
55155
|
+
};
|
|
55156
|
+
}
|
|
55157
|
+
}
|
|
54867
55158
|
function getIndexedLogEventDetail(logConfig, id) {
|
|
54868
55159
|
if (!logConfig || logConfig.enabled === false)
|
|
54869
55160
|
return null;
|
|
@@ -55543,7 +55834,7 @@ async function scanEvents(baseDir, query) {
|
|
|
55543
55834
|
};
|
|
55544
55835
|
}
|
|
55545
55836
|
function isLogQueryWindow(value) {
|
|
55546
|
-
return value === "1h" || value === "6h" || value === "24h";
|
|
55837
|
+
return value === "1h" || value === "6h" || value === "24h" || value === "7d" || value === "1mo" || value === "1y";
|
|
55547
55838
|
}
|
|
55548
55839
|
function getLogQueryWindowMs(window2) {
|
|
55549
55840
|
return WINDOW_MS2[window2];
|
|
@@ -55860,7 +56151,10 @@ var init_log_query = __esm(() => {
|
|
|
55860
56151
|
WINDOW_MS2 = {
|
|
55861
56152
|
"1h": 60 * 60 * 1000,
|
|
55862
56153
|
"6h": 6 * 60 * 60 * 1000,
|
|
55863
|
-
"24h": 24 * 60 * 60 * 1000
|
|
56154
|
+
"24h": 24 * 60 * 60 * 1000,
|
|
56155
|
+
"7d": 7 * 24 * 60 * 60 * 1000,
|
|
56156
|
+
"1mo": 30 * 24 * 60 * 60 * 1000,
|
|
56157
|
+
"1y": 365 * 24 * 60 * 60 * 1000
|
|
55864
56158
|
};
|
|
55865
56159
|
});
|
|
55866
56160
|
|
|
@@ -56049,7 +56343,7 @@ function compileRealtimeQuery(input) {
|
|
|
56049
56343
|
const nowMs = Date.now();
|
|
56050
56344
|
const windowRaw = parseStringValue(query.window) ?? "24h";
|
|
56051
56345
|
if (!isLogQueryWindow(windowRaw)) {
|
|
56052
|
-
throw new Error("window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h");
|
|
56346
|
+
throw new Error("window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h | 7d | 1mo | 1y");
|
|
56053
56347
|
}
|
|
56054
56348
|
const from = parseStringValue(query.from);
|
|
56055
56349
|
const to = parseStringValue(query.to);
|
|
@@ -56639,6 +56933,23 @@ async function queryLogSessions(context2, input) {
|
|
|
56639
56933
|
if (!logEnabled) {
|
|
56640
56934
|
return createEmptyResult(normalized.fromMs, normalized.toMs);
|
|
56641
56935
|
}
|
|
56936
|
+
const indexed = await queryIndexedLogSessions(context2.logConfig, {
|
|
56937
|
+
fromMs: normalized.fromMs,
|
|
56938
|
+
toMs: normalized.toMs,
|
|
56939
|
+
users: normalized.users,
|
|
56940
|
+
sessions: normalized.sessions,
|
|
56941
|
+
q: normalized.q
|
|
56942
|
+
});
|
|
56943
|
+
if (indexed?.meta.indexUsed) {
|
|
56944
|
+
return {
|
|
56945
|
+
from: indexed.from,
|
|
56946
|
+
to: indexed.to,
|
|
56947
|
+
summary: indexed.summary,
|
|
56948
|
+
users: indexed.users,
|
|
56949
|
+
meta: indexed.meta
|
|
56950
|
+
};
|
|
56951
|
+
}
|
|
56952
|
+
const fallbackReason = indexed?.meta.fallbackReason;
|
|
56642
56953
|
const baseDir = resolveLogBaseDir(context2.logConfig);
|
|
56643
56954
|
const eventsDir = join9(baseDir, "events");
|
|
56644
56955
|
if (!existsSync8(eventsDir)) {
|
|
@@ -56762,13 +57073,16 @@ async function queryLogSessions(context2, input) {
|
|
|
56762
57073
|
scannedFiles,
|
|
56763
57074
|
scannedLines,
|
|
56764
57075
|
parseErrors,
|
|
56765
|
-
truncated
|
|
57076
|
+
truncated,
|
|
57077
|
+
indexUsed: false,
|
|
57078
|
+
...fallbackReason ? { fallbackReason } : {}
|
|
56766
57079
|
}
|
|
56767
57080
|
};
|
|
56768
57081
|
}
|
|
56769
57082
|
var MAX_LINES_SCANNED3 = 250000, MAX_Q_LENGTH3 = 200;
|
|
56770
57083
|
var init_log_sessions = __esm(() => {
|
|
56771
57084
|
init_config();
|
|
57085
|
+
init_log_index();
|
|
56772
57086
|
});
|
|
56773
57087
|
|
|
56774
57088
|
// src/log-storage.ts
|
|
@@ -57299,7 +57613,7 @@ var init_openapi = __esm(() => {
|
|
|
57299
57613
|
required: false,
|
|
57300
57614
|
schema: {
|
|
57301
57615
|
type: "string",
|
|
57302
|
-
enum: ["1h", "6h", "24h"],
|
|
57616
|
+
enum: ["1h", "6h", "24h", "7d", "1mo", "1y"],
|
|
57303
57617
|
default: "24h"
|
|
57304
57618
|
},
|
|
57305
57619
|
description: "\u65F6\u95F4\u7A97\u53E3\uFF08\u5F53\u672A\u63D0\u4F9B from/to \u65F6\u751F\u6548\uFF09"
|
|
@@ -57629,7 +57943,7 @@ var init_openapi = __esm(() => {
|
|
|
57629
57943
|
required: false,
|
|
57630
57944
|
schema: {
|
|
57631
57945
|
type: "string",
|
|
57632
|
-
enum: ["1h", "6h", "24h"],
|
|
57946
|
+
enum: ["1h", "6h", "24h", "7d", "1mo", "1y"],
|
|
57633
57947
|
default: "24h"
|
|
57634
57948
|
},
|
|
57635
57949
|
description: "\u65F6\u95F4\u7A97\u53E3"
|
|
@@ -57750,7 +58064,7 @@ var init_openapi = __esm(() => {
|
|
|
57750
58064
|
required: false,
|
|
57751
58065
|
schema: {
|
|
57752
58066
|
type: "string",
|
|
57753
|
-
enum: ["1h", "6h", "24h"],
|
|
58067
|
+
enum: ["1h", "6h", "24h", "7d", "1mo", "1y"],
|
|
57754
58068
|
default: "1h"
|
|
57755
58069
|
}
|
|
57756
58070
|
},
|
|
@@ -59260,7 +59574,7 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
59260
59574
|
try {
|
|
59261
59575
|
const windowRaw = c.req.query("window") ?? "24h";
|
|
59262
59576
|
if (!isLogQueryWindow(windowRaw)) {
|
|
59263
|
-
return c.json({ error: "window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h" }, 400);
|
|
59577
|
+
return c.json({ error: "window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h | 7d | 1mo | 1y" }, 400);
|
|
59264
59578
|
}
|
|
59265
59579
|
const range = resolveLogQueryRange({
|
|
59266
59580
|
window: windowRaw,
|
|
@@ -59311,7 +59625,7 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
59311
59625
|
try {
|
|
59312
59626
|
const windowRaw = c.req.query("window") ?? "24h";
|
|
59313
59627
|
if (!isLogQueryWindow(windowRaw)) {
|
|
59314
|
-
return c.json({ error: "window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h" }, 400);
|
|
59628
|
+
return c.json({ error: "window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h | 7d | 1mo | 1y" }, 400);
|
|
59315
59629
|
}
|
|
59316
59630
|
const range = resolveLogQueryRange({
|
|
59317
59631
|
window: windowRaw,
|
|
@@ -59350,7 +59664,7 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
59350
59664
|
try {
|
|
59351
59665
|
const windowRaw = c.req.query("window") ?? "24h";
|
|
59352
59666
|
if (!isLogQueryWindow(windowRaw)) {
|
|
59353
|
-
return c.json({ error: "window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h" }, 400);
|
|
59667
|
+
return c.json({ error: "window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h | 7d | 1mo | 1y" }, 400);
|
|
59354
59668
|
}
|
|
59355
59669
|
const range = resolveLogQueryRange({
|
|
59356
59670
|
window: windowRaw,
|
|
@@ -59400,7 +59714,7 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
59400
59714
|
const target = c.req.raw;
|
|
59401
59715
|
const windowRaw = c.req.query("window") ?? "1h";
|
|
59402
59716
|
if (!isLogQueryWindow(windowRaw)) {
|
|
59403
|
-
return c.json({ error: "window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h" }, 400);
|
|
59717
|
+
return c.json({ error: "window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h | 7d | 1mo | 1y" }, 400);
|
|
59404
59718
|
}
|
|
59405
59719
|
const sortRaw = c.req.query("sort") ?? "time_desc";
|
|
59406
59720
|
if (!validateSort(sortRaw)) {
|
|
@@ -64676,7 +64990,7 @@ defineSchemaCommand({
|
|
|
64676
64990
|
{
|
|
64677
64991
|
name: "window",
|
|
64678
64992
|
type: "enum",
|
|
64679
|
-
enum: ["1h", "6h", "24h"],
|
|
64993
|
+
enum: ["1h", "6h", "24h", "7d", "1mo", "1y"],
|
|
64680
64994
|
default: "24h",
|
|
64681
64995
|
description: "\u65F6\u95F4\u7A97\u53E3"
|
|
64682
64996
|
},
|
|
@@ -64809,7 +65123,7 @@ defineSchemaCommand({
|
|
|
64809
65123
|
{
|
|
64810
65124
|
name: "window",
|
|
64811
65125
|
type: "enum",
|
|
64812
|
-
enum: ["1h", "6h", "24h"],
|
|
65126
|
+
enum: ["1h", "6h", "24h", "7d", "1mo", "1y"],
|
|
64813
65127
|
default: "24h",
|
|
64814
65128
|
description: "\u65F6\u95F4\u7A97\u53E3"
|
|
64815
65129
|
}
|
|
@@ -64933,7 +65247,7 @@ defineSchemaCommand({
|
|
|
64933
65247
|
{
|
|
64934
65248
|
name: "window",
|
|
64935
65249
|
type: "enum",
|
|
64936
|
-
enum: ["1h", "6h", "24h"],
|
|
65250
|
+
enum: ["1h", "6h", "24h", "7d", "1mo", "1y"],
|
|
64937
65251
|
default: "24h",
|
|
64938
65252
|
description: "\u65F6\u95F4\u7A97\u53E3"
|
|
64939
65253
|
},
|
|
@@ -65020,7 +65334,7 @@ defineSchemaCommand({
|
|
|
65020
65334
|
{
|
|
65021
65335
|
name: "window",
|
|
65022
65336
|
type: "enum",
|
|
65023
|
-
enum: ["1h", "6h", "24h"],
|
|
65337
|
+
enum: ["1h", "6h", "24h", "7d", "1mo", "1y"],
|
|
65024
65338
|
default: "24h",
|
|
65025
65339
|
description: "\u65F6\u95F4\u7A97\u53E3"
|
|
65026
65340
|
},
|
|
@@ -65146,7 +65460,7 @@ defineSchemaCommand({
|
|
|
65146
65460
|
{
|
|
65147
65461
|
name: "window",
|
|
65148
65462
|
type: "enum",
|
|
65149
|
-
enum: ["1h", "6h", "24h"],
|
|
65463
|
+
enum: ["1h", "6h", "24h", "7d", "1mo", "1y"],
|
|
65150
65464
|
default: "24h",
|
|
65151
65465
|
description: "\u65F6\u95F4\u7A97\u53E3"
|
|
65152
65466
|
}
|
|
@@ -65206,7 +65520,7 @@ defineSchemaCommand({
|
|
|
65206
65520
|
{
|
|
65207
65521
|
name: "window",
|
|
65208
65522
|
type: "enum",
|
|
65209
|
-
enum: ["1h", "6h", "24h"],
|
|
65523
|
+
enum: ["1h", "6h", "24h", "7d", "1mo", "1y"],
|
|
65210
65524
|
default: "24h",
|
|
65211
65525
|
description: "\u65F6\u95F4\u7A97\u53E3"
|
|
65212
65526
|
},
|