@lakphy/local-router 0.3.1 → 0.3.3
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 +20 -0
- package/config.schema.json +2 -2
- package/dist/cli.js +88 -105
- package/dist/entry.js +46 -97
- package/dist/index.js +13699 -0
- package/dist/web/assets/index-CbUvycUp.css +2 -0
- package/dist/web/assets/index-Dz4pO265.js +191 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-BhvlBO7m.js +0 -190
- package/dist/web/assets/index-DgDq1A8B.css +0 -2
package/README.md
CHANGED
|
@@ -99,6 +99,7 @@ local-router version
|
|
|
99
99
|
- `--host <host>`:指定监听地址
|
|
100
100
|
- `--port <port>`:指定监听端口
|
|
101
101
|
- `--daemon`:后台运行
|
|
102
|
+
- `--idle-timeout <sec>`:设置 Bun 连接空闲超时(默认 600 秒,设为 `0` 可关闭)
|
|
102
103
|
|
|
103
104
|
## 请求入口(给你的应用调用)
|
|
104
105
|
|
|
@@ -195,6 +196,25 @@ curl -X POST "http://127.0.0.1:4099/openai-completions/v1/chat/completions" \
|
|
|
195
196
|
- `routes` 引用的 provider 是否存在
|
|
196
197
|
- 配置文件是否是合法 JSON5
|
|
197
198
|
|
|
199
|
+
|
|
200
|
+
### 运行较久请求出现 `[Bun.serve]: request timed out after 10 seconds` 怎么办?
|
|
201
|
+
|
|
202
|
+
这是 Bun 服务端连接空闲超时触发导致的(常见于长流式响应或慢速上游)。
|
|
203
|
+
|
|
204
|
+
可在启动时放宽超时:
|
|
205
|
+
|
|
206
|
+
```sh
|
|
207
|
+
local-router start --idle-timeout 600
|
|
208
|
+
# 或彻底关闭空闲超时
|
|
209
|
+
local-router start --idle-timeout 0
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
也支持环境变量:
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
LOCAL_ROUTER_IDLE_TIMEOUT=600 local-router start
|
|
216
|
+
```
|
|
217
|
+
|
|
198
218
|
### 如何升级?
|
|
199
219
|
|
|
200
220
|
```sh
|
package/config.schema.json
CHANGED
|
@@ -218,8 +218,8 @@
|
|
|
218
218
|
"type": "string",
|
|
219
219
|
"enum": ["off", "masked", "full"],
|
|
220
220
|
"default": "off",
|
|
221
|
-
"description": "请求体与响应体的记录策略。off:不记录 body 内容(推荐日常使用);masked
|
|
222
|
-
"markdownDescription": "请求体与响应体的记录策略:\n- `off`:不记录 body 内容(推荐日常使用)\n- `
|
|
221
|
+
"description": "请求体与响应体的记录策略。off:不记录 body 内容(推荐日常使用);full:完整记录所有内容(仅建议调试时临时开启,注意隐私风险)。为兼容旧配置,masked 仍被接受,但当前行为等同于 full。",
|
|
222
|
+
"markdownDescription": "请求体与响应体的记录策略:\n- `off`:不记录 body 内容(推荐日常使用)\n- `full`:完整记录所有内容(仅调试时使用)\n- `masked`:兼容旧配置,当前行为等同于 `full`"
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
},
|
package/dist/cli.js
CHANGED
|
@@ -10440,7 +10440,7 @@ var DEFAULT_CONFIG = `{
|
|
|
10440
10440
|
// \u65E5\u5FD7\u914D\u7F6E\uFF08\u53EF\u9009\uFF0C\u4E0D\u914D\u7F6E\u5219\u4E0D\u542F\u7528\u65E5\u5FD7\u8BB0\u5F55\uFF09
|
|
10441
10441
|
// log: {
|
|
10442
10442
|
// enabled: true,
|
|
10443
|
-
// bodyPolicy: "off", // off |
|
|
10443
|
+
// bodyPolicy: "off", // off | full
|
|
10444
10444
|
// streams: {
|
|
10445
10445
|
// enabled: true,
|
|
10446
10446
|
// maxBytesPerRequest: 10485760, // 10MB
|
|
@@ -51823,7 +51823,6 @@ var MAX_QUERY_LIMIT = 200;
|
|
|
51823
51823
|
var DEFAULT_QUERY_LIMIT = 50;
|
|
51824
51824
|
var MAX_EXPORT_ROWS = 5000;
|
|
51825
51825
|
var MAX_Q_LENGTH = 200;
|
|
51826
|
-
var SENSITIVE_FIELD_PATTERN = /(authorization|token|cookie|password|passphrase|secret|api[_-]?key)/i;
|
|
51827
51826
|
function encodeBase64Url(value) {
|
|
51828
51827
|
return Buffer.from(value, "utf-8").toString("base64url");
|
|
51829
51828
|
}
|
|
@@ -52039,34 +52038,6 @@ function eventToSummary(item) {
|
|
|
52039
52038
|
sessionId: identity.sessionId
|
|
52040
52039
|
};
|
|
52041
52040
|
}
|
|
52042
|
-
function maskValue(value) {
|
|
52043
|
-
if (value == null)
|
|
52044
|
-
return value;
|
|
52045
|
-
if (typeof value === "string") {
|
|
52046
|
-
return value.length > 4 ? `${value.slice(0, 2)}****` : "****";
|
|
52047
|
-
}
|
|
52048
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
52049
|
-
return "****";
|
|
52050
|
-
}
|
|
52051
|
-
return "****";
|
|
52052
|
-
}
|
|
52053
|
-
function maskSensitiveDeep(value, parentKey = "") {
|
|
52054
|
-
if (Array.isArray(value)) {
|
|
52055
|
-
return value.map((item) => maskSensitiveDeep(item, parentKey));
|
|
52056
|
-
}
|
|
52057
|
-
if (value && typeof value === "object") {
|
|
52058
|
-
const output = {};
|
|
52059
|
-
for (const [key2, child] of Object.entries(value)) {
|
|
52060
|
-
if (SENSITIVE_FIELD_PATTERN.test(key2) || SENSITIVE_FIELD_PATTERN.test(parentKey)) {
|
|
52061
|
-
output[key2] = maskValue(child);
|
|
52062
|
-
} else {
|
|
52063
|
-
output[key2] = maskSensitiveDeep(child, key2);
|
|
52064
|
-
}
|
|
52065
|
-
}
|
|
52066
|
-
return output;
|
|
52067
|
-
}
|
|
52068
|
-
return value;
|
|
52069
|
-
}
|
|
52070
52041
|
function detectBodyPolicy(event) {
|
|
52071
52042
|
const hasRequestBody = event.request_body !== undefined;
|
|
52072
52043
|
const hasResponseBody = event.response_body !== undefined;
|
|
@@ -52139,53 +52110,53 @@ function readStreamContent(baseDir, streamFile) {
|
|
|
52139
52110
|
}
|
|
52140
52111
|
}
|
|
52141
52112
|
async function buildLogEventDetail(id, parsed, location, context2) {
|
|
52142
|
-
const
|
|
52143
|
-
const level = getLevel(
|
|
52144
|
-
const statusClass = getStatusClass2(
|
|
52145
|
-
const bodyPolicy = detectBodyPolicy(
|
|
52146
|
-
const requestBodyAvailable =
|
|
52147
|
-
const responseBodyAvailable =
|
|
52148
|
-
const streamCaptured = Boolean(
|
|
52149
|
-
const { content: streamContent, warning: streamWarning } = readStreamContent(resolveLogBaseDir(context2.logConfig),
|
|
52113
|
+
const event = parsed;
|
|
52114
|
+
const level = getLevel(event);
|
|
52115
|
+
const statusClass = getStatusClass2(event);
|
|
52116
|
+
const bodyPolicy = detectBodyPolicy(event);
|
|
52117
|
+
const requestBodyAvailable = event.request_body !== undefined;
|
|
52118
|
+
const responseBodyAvailable = event.response_body !== undefined;
|
|
52119
|
+
const streamCaptured = Boolean(event.stream_file);
|
|
52120
|
+
const { content: streamContent, warning: streamWarning } = readStreamContent(resolveLogBaseDir(context2.logConfig), event.stream_file);
|
|
52150
52121
|
return {
|
|
52151
52122
|
id,
|
|
52152
52123
|
summary: {
|
|
52153
52124
|
id,
|
|
52154
|
-
ts:
|
|
52125
|
+
ts: event.ts_start,
|
|
52155
52126
|
level,
|
|
52156
|
-
provider:
|
|
52157
|
-
routeType:
|
|
52158
|
-
routeRuleKey:
|
|
52159
|
-
requestId:
|
|
52160
|
-
latencyMs: Math.max(0,
|
|
52161
|
-
upstreamStatus:
|
|
52127
|
+
provider: event.provider,
|
|
52128
|
+
routeType: event.route_type,
|
|
52129
|
+
routeRuleKey: event.route_rule_key,
|
|
52130
|
+
requestId: event.request_id,
|
|
52131
|
+
latencyMs: Math.max(0, event.latency_ms ?? 0),
|
|
52132
|
+
upstreamStatus: event.upstream_status ?? 0,
|
|
52162
52133
|
statusClass,
|
|
52163
52134
|
hasError: level === "error",
|
|
52164
|
-
model:
|
|
52165
|
-
modelIn:
|
|
52166
|
-
modelOut:
|
|
52135
|
+
model: event.model_out || event.model_in,
|
|
52136
|
+
modelIn: event.model_in,
|
|
52137
|
+
modelOut: event.model_out
|
|
52167
52138
|
},
|
|
52168
52139
|
request: {
|
|
52169
|
-
method:
|
|
52170
|
-
path:
|
|
52171
|
-
contentType:
|
|
52172
|
-
|
|
52173
|
-
requestBody:
|
|
52140
|
+
method: event.method,
|
|
52141
|
+
path: event.path,
|
|
52142
|
+
contentType: event.content_type_req,
|
|
52143
|
+
requestHeaders: event.request_headers,
|
|
52144
|
+
requestBody: event.request_body ?? null
|
|
52174
52145
|
},
|
|
52175
52146
|
response: {
|
|
52176
|
-
upstreamStatus:
|
|
52177
|
-
contentType:
|
|
52178
|
-
responseHeaders:
|
|
52179
|
-
responseBody:
|
|
52147
|
+
upstreamStatus: event.upstream_status ?? 0,
|
|
52148
|
+
contentType: event.content_type_res,
|
|
52149
|
+
responseHeaders: event.response_headers,
|
|
52150
|
+
responseBody: event.response_body ?? null
|
|
52180
52151
|
},
|
|
52181
52152
|
upstream: {
|
|
52182
|
-
targetUrl:
|
|
52183
|
-
proxyUrl:
|
|
52184
|
-
providerRequestId:
|
|
52185
|
-
errorType:
|
|
52186
|
-
errorMessage:
|
|
52187
|
-
isStream:
|
|
52188
|
-
streamFile:
|
|
52153
|
+
targetUrl: event.target_url,
|
|
52154
|
+
proxyUrl: event.proxy_url ?? null,
|
|
52155
|
+
providerRequestId: event.provider_request_id,
|
|
52156
|
+
errorType: event.error_type,
|
|
52157
|
+
errorMessage: event.error_message,
|
|
52158
|
+
isStream: event.is_stream,
|
|
52159
|
+
streamFile: event.stream_file ?? null,
|
|
52189
52160
|
streamContent
|
|
52190
52161
|
},
|
|
52191
52162
|
capture: {
|
|
@@ -52194,11 +52165,11 @@ async function buildLogEventDetail(id, parsed, location, context2) {
|
|
|
52194
52165
|
responseBodyAvailable,
|
|
52195
52166
|
streamCaptured,
|
|
52196
52167
|
truncatedHints: [
|
|
52197
|
-
...buildTruncatedHints(
|
|
52168
|
+
...buildTruncatedHints(event, context2.logConfig?.bodyPolicy ?? bodyPolicy),
|
|
52198
52169
|
...streamWarning ? [streamWarning] : []
|
|
52199
52170
|
]
|
|
52200
52171
|
},
|
|
52201
|
-
rawEvent:
|
|
52172
|
+
rawEvent: event,
|
|
52202
52173
|
location
|
|
52203
52174
|
};
|
|
52204
52175
|
}
|
|
@@ -52910,13 +52881,6 @@ function startLogStorageBackgroundTask(logConfig) {
|
|
|
52910
52881
|
// src/logger.ts
|
|
52911
52882
|
import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
52912
52883
|
import { join as join7 } from "path";
|
|
52913
|
-
var SENSITIVE_HEADERS = new Set([
|
|
52914
|
-
"authorization",
|
|
52915
|
-
"x-api-key",
|
|
52916
|
-
"cookie",
|
|
52917
|
-
"set-cookie",
|
|
52918
|
-
"proxy-authorization"
|
|
52919
|
-
]);
|
|
52920
52884
|
|
|
52921
52885
|
class Logger {
|
|
52922
52886
|
baseDir;
|
|
@@ -52997,14 +52961,10 @@ function getLogger() {
|
|
|
52997
52961
|
function resetLogger() {
|
|
52998
52962
|
instance = null;
|
|
52999
52963
|
}
|
|
53000
|
-
function
|
|
52964
|
+
function collectHeaders(headers) {
|
|
53001
52965
|
const result = {};
|
|
53002
52966
|
headers.forEach((value, key2) => {
|
|
53003
|
-
|
|
53004
|
-
result[key2] = value.length > 4 ? `${value.slice(0, 4)}****` : "****";
|
|
53005
|
-
} else {
|
|
53006
|
-
result[key2] = value;
|
|
53007
|
-
}
|
|
52967
|
+
result[key2] = value;
|
|
53008
52968
|
});
|
|
53009
52969
|
return result;
|
|
53010
52970
|
}
|
|
@@ -53016,19 +52976,8 @@ function extractProviderRequestId(headers) {
|
|
|
53016
52976
|
}
|
|
53017
52977
|
return null;
|
|
53018
52978
|
}
|
|
53019
|
-
function
|
|
53020
|
-
|
|
53021
|
-
const parsed = new URL(rawUrl);
|
|
53022
|
-
if (!parsed.username && !parsed.password)
|
|
53023
|
-
return rawUrl;
|
|
53024
|
-
if (parsed.username)
|
|
53025
|
-
parsed.username = "****";
|
|
53026
|
-
if (parsed.password)
|
|
53027
|
-
parsed.password = "****";
|
|
53028
|
-
return parsed.toString();
|
|
53029
|
-
} catch {
|
|
53030
|
-
return rawUrl;
|
|
53031
|
-
}
|
|
52979
|
+
function normalizeUrl(rawUrl) {
|
|
52980
|
+
return rawUrl;
|
|
53032
52981
|
}
|
|
53033
52982
|
|
|
53034
52983
|
// src/openapi.ts
|
|
@@ -53475,7 +53424,7 @@ var openAPISpec = {
|
|
|
53475
53424
|
method: { type: "string" },
|
|
53476
53425
|
path: { type: "string" },
|
|
53477
53426
|
contentType: { type: ["string", "null"] },
|
|
53478
|
-
|
|
53427
|
+
requestHeaders: {
|
|
53479
53428
|
type: "object",
|
|
53480
53429
|
additionalProperties: { type: "string" }
|
|
53481
53430
|
},
|
|
@@ -54195,14 +54144,14 @@ function buildLogEvent(logMeta, targetUrl, proxyUrl, tsEnd, overrides) {
|
|
|
54195
54144
|
provider: logMeta.provider,
|
|
54196
54145
|
model_in: logMeta.modelIn,
|
|
54197
54146
|
model_out: logMeta.modelOut,
|
|
54198
|
-
target_url:
|
|
54199
|
-
proxy_url: proxyUrl ?
|
|
54147
|
+
target_url: normalizeUrl(targetUrl),
|
|
54148
|
+
proxy_url: proxyUrl ? normalizeUrl(proxyUrl) : null,
|
|
54200
54149
|
is_stream: logMeta.isStream,
|
|
54201
54150
|
upstream_status: 0,
|
|
54202
54151
|
content_type_req: logMeta.contentTypeReq,
|
|
54203
54152
|
content_type_res: null,
|
|
54204
54153
|
user_agent: logMeta.userAgent,
|
|
54205
|
-
|
|
54154
|
+
request_headers: logMeta.requestHeaders,
|
|
54206
54155
|
response_headers: {},
|
|
54207
54156
|
request_bytes: logMeta.requestBytes,
|
|
54208
54157
|
response_bytes: null,
|
|
@@ -54382,7 +54331,7 @@ function createModelRoutingHandler(options) {
|
|
|
54382
54331
|
contentTypeReq: c2.req.header("content-type") ?? null,
|
|
54383
54332
|
userAgent: c2.req.header("user-agent") ?? null,
|
|
54384
54333
|
requestBytes: Buffer.byteLength(body, "utf-8"),
|
|
54385
|
-
|
|
54334
|
+
requestHeaders: collectHeaders(c2.req.raw.headers)
|
|
54386
54335
|
};
|
|
54387
54336
|
return proxyRequest(c2, {
|
|
54388
54337
|
targetUrl,
|
|
@@ -55203,12 +55152,29 @@ function createAppRuntimeFromConfigPath(configPath) {
|
|
|
55203
55152
|
}
|
|
55204
55153
|
|
|
55205
55154
|
// src/server.ts
|
|
55155
|
+
var DEFAULT_IDLE_TIMEOUT_SECONDS = 600;
|
|
55156
|
+
function resolveIdleTimeoutSeconds(explicit) {
|
|
55157
|
+
if (typeof explicit === "number" && Number.isFinite(explicit) && explicit >= 0) {
|
|
55158
|
+
return explicit;
|
|
55159
|
+
}
|
|
55160
|
+
const fromEnv = process.env.LOCAL_ROUTER_IDLE_TIMEOUT;
|
|
55161
|
+
if (!fromEnv) {
|
|
55162
|
+
return DEFAULT_IDLE_TIMEOUT_SECONDS;
|
|
55163
|
+
}
|
|
55164
|
+
const parsed = Number.parseInt(fromEnv, 10);
|
|
55165
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
55166
|
+
return parsed;
|
|
55167
|
+
}
|
|
55168
|
+
return DEFAULT_IDLE_TIMEOUT_SECONDS;
|
|
55169
|
+
}
|
|
55206
55170
|
function startServer(options) {
|
|
55207
55171
|
const runtime = createAppRuntimeFromConfigPath(options.configPath);
|
|
55172
|
+
const idleTimeout = resolveIdleTimeoutSeconds(options.idleTimeoutSeconds);
|
|
55208
55173
|
const server = Bun.serve({
|
|
55209
55174
|
fetch: runtime.app.fetch,
|
|
55210
55175
|
hostname: options.host,
|
|
55211
|
-
port: options.port
|
|
55176
|
+
port: options.port,
|
|
55177
|
+
idleTimeout
|
|
55212
55178
|
});
|
|
55213
55179
|
const host = server.hostname;
|
|
55214
55180
|
const port = server.port;
|
|
@@ -55284,7 +55250,8 @@ function parseSharedFlags(args) {
|
|
|
55284
55250
|
options: {
|
|
55285
55251
|
config: { type: "string" },
|
|
55286
55252
|
host: { type: "string" },
|
|
55287
|
-
port: { type: "string" }
|
|
55253
|
+
port: { type: "string" },
|
|
55254
|
+
"idle-timeout": { type: "string" }
|
|
55288
55255
|
},
|
|
55289
55256
|
allowPositionals: true,
|
|
55290
55257
|
strict: false
|
|
@@ -55294,10 +55261,16 @@ function parseSharedFlags(args) {
|
|
|
55294
55261
|
if (portRaw && !Number.isFinite(port)) {
|
|
55295
55262
|
throw new Error(`\u65E0\u6548\u7AEF\u53E3: ${portRaw}`);
|
|
55296
55263
|
}
|
|
55264
|
+
const idleTimeoutRaw = parsed.values["idle-timeout"];
|
|
55265
|
+
const idleTimeoutSeconds = idleTimeoutRaw ? Number.parseInt(idleTimeoutRaw, 10) : undefined;
|
|
55266
|
+
if (idleTimeoutRaw && (!Number.isFinite(idleTimeoutSeconds) || idleTimeoutSeconds < 0)) {
|
|
55267
|
+
throw new Error(`\u65E0\u6548 idle-timeout: ${idleTimeoutRaw}`);
|
|
55268
|
+
}
|
|
55297
55269
|
return {
|
|
55298
55270
|
config: parsed.values.config,
|
|
55299
55271
|
host: parsed.values.host,
|
|
55300
|
-
port
|
|
55272
|
+
port,
|
|
55273
|
+
idleTimeoutSeconds
|
|
55301
55274
|
};
|
|
55302
55275
|
}
|
|
55303
55276
|
async function checkHealth(baseUrl, timeoutMs = 1500) {
|
|
@@ -55343,12 +55316,14 @@ async function runServerProcess(opts) {
|
|
|
55343
55316
|
if (!Number.isFinite(port)) {
|
|
55344
55317
|
throw new Error(`\u65E0\u6548\u7AEF\u53E3: ${opts.port ?? process.env.PORT}`);
|
|
55345
55318
|
}
|
|
55319
|
+
const idleTimeoutSeconds = opts.idleTimeoutSeconds ?? Number.parseInt(process.env.LOCAL_ROUTER_IDLE_TIMEOUT ?? "", 10);
|
|
55346
55320
|
let running;
|
|
55347
55321
|
try {
|
|
55348
55322
|
running = startServer({
|
|
55349
55323
|
configPath: ensured.path,
|
|
55350
55324
|
host,
|
|
55351
|
-
port
|
|
55325
|
+
port,
|
|
55326
|
+
idleTimeoutSeconds: Number.isFinite(idleTimeoutSeconds) ? idleTimeoutSeconds : undefined
|
|
55352
55327
|
});
|
|
55353
55328
|
} catch (err) {
|
|
55354
55329
|
const code = err?.code;
|
|
@@ -55422,6 +55397,9 @@ async function startDaemon(flags) {
|
|
|
55422
55397
|
if (typeof flags.port === "number") {
|
|
55423
55398
|
childArgs.push("--port", String(flags.port));
|
|
55424
55399
|
}
|
|
55400
|
+
if (typeof flags.idleTimeoutSeconds === "number") {
|
|
55401
|
+
childArgs.push("--idle-timeout", String(flags.idleTimeoutSeconds));
|
|
55402
|
+
}
|
|
55425
55403
|
childArgs.push("--log-file", files.daemonLog);
|
|
55426
55404
|
const child = Bun.spawn([process.execPath, ...childArgs], {
|
|
55427
55405
|
stdin: "ignore",
|
|
@@ -55947,9 +55925,9 @@ Usage:
|
|
|
55947
55925
|
local-router <command> [options]
|
|
55948
55926
|
|
|
55949
55927
|
Commands:
|
|
55950
|
-
start [--daemon] [--config <path>] [--host <host>] [--port <port>]
|
|
55928
|
+
start [--daemon] [--config <path>] [--host <host>] [--port <port>] [--idle-timeout <sec>]
|
|
55951
55929
|
stop
|
|
55952
|
-
restart [--daemon] [--config <path>] [--host <host>] [--port <port>]
|
|
55930
|
+
restart [--daemon] [--config <path>] [--host <host>] [--port <port>] [--idle-timeout <sec>]
|
|
55953
55931
|
status [--json]
|
|
55954
55932
|
logs [--follow] [--lines <n>]
|
|
55955
55933
|
init [--config <path>] [--force]
|
|
@@ -55959,7 +55937,7 @@ Commands:
|
|
|
55959
55937
|
version
|
|
55960
55938
|
|
|
55961
55939
|
Hidden commands:
|
|
55962
|
-
__run-server --mode <daemon|foreground> [--config] [--host] [--port] [--log-file]
|
|
55940
|
+
__run-server --mode <daemon|foreground> [--config] [--host] [--port] [--idle-timeout] [--log-file]
|
|
55963
55941
|
`);
|
|
55964
55942
|
}
|
|
55965
55943
|
async function printVersion() {
|
|
@@ -55989,7 +55967,8 @@ async function cmdStart(args) {
|
|
|
55989
55967
|
mode: "foreground",
|
|
55990
55968
|
config: flags.config,
|
|
55991
55969
|
host: flags.host,
|
|
55992
|
-
port: flags.port
|
|
55970
|
+
port: flags.port,
|
|
55971
|
+
idleTimeoutSeconds: flags.idleTimeoutSeconds
|
|
55993
55972
|
});
|
|
55994
55973
|
}
|
|
55995
55974
|
async function cmdStop() {
|
|
@@ -56188,6 +56167,7 @@ async function cmdRunServer(args) {
|
|
|
56188
56167
|
config: { type: "string" },
|
|
56189
56168
|
host: { type: "string" },
|
|
56190
56169
|
port: { type: "string" },
|
|
56170
|
+
"idle-timeout": { type: "string" },
|
|
56191
56171
|
"log-file": { type: "string" }
|
|
56192
56172
|
},
|
|
56193
56173
|
allowPositionals: true,
|
|
@@ -56196,11 +56176,14 @@ async function cmdRunServer(args) {
|
|
|
56196
56176
|
const mode = parsed.values.mode === "daemon" ? "daemon" : "foreground";
|
|
56197
56177
|
const portRaw = parsed.values.port;
|
|
56198
56178
|
const port = portRaw ? Number.parseInt(portRaw, 10) : undefined;
|
|
56179
|
+
const idleTimeoutRaw = parsed.values["idle-timeout"];
|
|
56180
|
+
const idleTimeoutSeconds = idleTimeoutRaw ? Number.parseInt(idleTimeoutRaw, 10) : undefined;
|
|
56199
56181
|
await runServerProcess({
|
|
56200
56182
|
mode,
|
|
56201
56183
|
config: parsed.values.config,
|
|
56202
56184
|
host: parsed.values.host,
|
|
56203
56185
|
port,
|
|
56186
|
+
idleTimeoutSeconds: Number.isFinite(idleTimeoutSeconds) ? idleTimeoutSeconds : undefined,
|
|
56204
56187
|
logFile: parsed.values["log-file"]
|
|
56205
56188
|
});
|
|
56206
56189
|
}
|
package/dist/entry.js
CHANGED
|
@@ -51279,7 +51279,7 @@ var DEFAULT_CONFIG = `{
|
|
|
51279
51279
|
// \u65E5\u5FD7\u914D\u7F6E\uFF08\u53EF\u9009\uFF0C\u4E0D\u914D\u7F6E\u5219\u4E0D\u542F\u7528\u65E5\u5FD7\u8BB0\u5F55\uFF09
|
|
51280
51280
|
// log: {
|
|
51281
51281
|
// enabled: true,
|
|
51282
|
-
// bodyPolicy: "off", // off |
|
|
51282
|
+
// bodyPolicy: "off", // off | full
|
|
51283
51283
|
// streams: {
|
|
51284
51284
|
// enabled: true,
|
|
51285
51285
|
// maxBytesPerRequest: 10485760, // 10MB
|
|
@@ -51807,7 +51807,6 @@ var MAX_QUERY_LIMIT = 200;
|
|
|
51807
51807
|
var DEFAULT_QUERY_LIMIT = 50;
|
|
51808
51808
|
var MAX_EXPORT_ROWS = 5000;
|
|
51809
51809
|
var MAX_Q_LENGTH = 200;
|
|
51810
|
-
var SENSITIVE_FIELD_PATTERN = /(authorization|token|cookie|password|passphrase|secret|api[_-]?key)/i;
|
|
51811
51810
|
function encodeBase64Url(value) {
|
|
51812
51811
|
return Buffer.from(value, "utf-8").toString("base64url");
|
|
51813
51812
|
}
|
|
@@ -52023,34 +52022,6 @@ function eventToSummary(item) {
|
|
|
52023
52022
|
sessionId: identity.sessionId
|
|
52024
52023
|
};
|
|
52025
52024
|
}
|
|
52026
|
-
function maskValue(value) {
|
|
52027
|
-
if (value == null)
|
|
52028
|
-
return value;
|
|
52029
|
-
if (typeof value === "string") {
|
|
52030
|
-
return value.length > 4 ? `${value.slice(0, 2)}****` : "****";
|
|
52031
|
-
}
|
|
52032
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
52033
|
-
return "****";
|
|
52034
|
-
}
|
|
52035
|
-
return "****";
|
|
52036
|
-
}
|
|
52037
|
-
function maskSensitiveDeep(value, parentKey = "") {
|
|
52038
|
-
if (Array.isArray(value)) {
|
|
52039
|
-
return value.map((item) => maskSensitiveDeep(item, parentKey));
|
|
52040
|
-
}
|
|
52041
|
-
if (value && typeof value === "object") {
|
|
52042
|
-
const output = {};
|
|
52043
|
-
for (const [key2, child] of Object.entries(value)) {
|
|
52044
|
-
if (SENSITIVE_FIELD_PATTERN.test(key2) || SENSITIVE_FIELD_PATTERN.test(parentKey)) {
|
|
52045
|
-
output[key2] = maskValue(child);
|
|
52046
|
-
} else {
|
|
52047
|
-
output[key2] = maskSensitiveDeep(child, key2);
|
|
52048
|
-
}
|
|
52049
|
-
}
|
|
52050
|
-
return output;
|
|
52051
|
-
}
|
|
52052
|
-
return value;
|
|
52053
|
-
}
|
|
52054
52025
|
function detectBodyPolicy(event) {
|
|
52055
52026
|
const hasRequestBody = event.request_body !== undefined;
|
|
52056
52027
|
const hasResponseBody = event.response_body !== undefined;
|
|
@@ -52123,53 +52094,53 @@ function readStreamContent(baseDir, streamFile) {
|
|
|
52123
52094
|
}
|
|
52124
52095
|
}
|
|
52125
52096
|
async function buildLogEventDetail(id, parsed, location, context2) {
|
|
52126
|
-
const
|
|
52127
|
-
const level = getLevel(
|
|
52128
|
-
const statusClass = getStatusClass2(
|
|
52129
|
-
const bodyPolicy = detectBodyPolicy(
|
|
52130
|
-
const requestBodyAvailable =
|
|
52131
|
-
const responseBodyAvailable =
|
|
52132
|
-
const streamCaptured = Boolean(
|
|
52133
|
-
const { content: streamContent, warning: streamWarning } = readStreamContent(resolveLogBaseDir(context2.logConfig),
|
|
52097
|
+
const event = parsed;
|
|
52098
|
+
const level = getLevel(event);
|
|
52099
|
+
const statusClass = getStatusClass2(event);
|
|
52100
|
+
const bodyPolicy = detectBodyPolicy(event);
|
|
52101
|
+
const requestBodyAvailable = event.request_body !== undefined;
|
|
52102
|
+
const responseBodyAvailable = event.response_body !== undefined;
|
|
52103
|
+
const streamCaptured = Boolean(event.stream_file);
|
|
52104
|
+
const { content: streamContent, warning: streamWarning } = readStreamContent(resolveLogBaseDir(context2.logConfig), event.stream_file);
|
|
52134
52105
|
return {
|
|
52135
52106
|
id,
|
|
52136
52107
|
summary: {
|
|
52137
52108
|
id,
|
|
52138
|
-
ts:
|
|
52109
|
+
ts: event.ts_start,
|
|
52139
52110
|
level,
|
|
52140
|
-
provider:
|
|
52141
|
-
routeType:
|
|
52142
|
-
routeRuleKey:
|
|
52143
|
-
requestId:
|
|
52144
|
-
latencyMs: Math.max(0,
|
|
52145
|
-
upstreamStatus:
|
|
52111
|
+
provider: event.provider,
|
|
52112
|
+
routeType: event.route_type,
|
|
52113
|
+
routeRuleKey: event.route_rule_key,
|
|
52114
|
+
requestId: event.request_id,
|
|
52115
|
+
latencyMs: Math.max(0, event.latency_ms ?? 0),
|
|
52116
|
+
upstreamStatus: event.upstream_status ?? 0,
|
|
52146
52117
|
statusClass,
|
|
52147
52118
|
hasError: level === "error",
|
|
52148
|
-
model:
|
|
52149
|
-
modelIn:
|
|
52150
|
-
modelOut:
|
|
52119
|
+
model: event.model_out || event.model_in,
|
|
52120
|
+
modelIn: event.model_in,
|
|
52121
|
+
modelOut: event.model_out
|
|
52151
52122
|
},
|
|
52152
52123
|
request: {
|
|
52153
|
-
method:
|
|
52154
|
-
path:
|
|
52155
|
-
contentType:
|
|
52156
|
-
|
|
52157
|
-
requestBody:
|
|
52124
|
+
method: event.method,
|
|
52125
|
+
path: event.path,
|
|
52126
|
+
contentType: event.content_type_req,
|
|
52127
|
+
requestHeaders: event.request_headers,
|
|
52128
|
+
requestBody: event.request_body ?? null
|
|
52158
52129
|
},
|
|
52159
52130
|
response: {
|
|
52160
|
-
upstreamStatus:
|
|
52161
|
-
contentType:
|
|
52162
|
-
responseHeaders:
|
|
52163
|
-
responseBody:
|
|
52131
|
+
upstreamStatus: event.upstream_status ?? 0,
|
|
52132
|
+
contentType: event.content_type_res,
|
|
52133
|
+
responseHeaders: event.response_headers,
|
|
52134
|
+
responseBody: event.response_body ?? null
|
|
52164
52135
|
},
|
|
52165
52136
|
upstream: {
|
|
52166
|
-
targetUrl:
|
|
52167
|
-
proxyUrl:
|
|
52168
|
-
providerRequestId:
|
|
52169
|
-
errorType:
|
|
52170
|
-
errorMessage:
|
|
52171
|
-
isStream:
|
|
52172
|
-
streamFile:
|
|
52137
|
+
targetUrl: event.target_url,
|
|
52138
|
+
proxyUrl: event.proxy_url ?? null,
|
|
52139
|
+
providerRequestId: event.provider_request_id,
|
|
52140
|
+
errorType: event.error_type,
|
|
52141
|
+
errorMessage: event.error_message,
|
|
52142
|
+
isStream: event.is_stream,
|
|
52143
|
+
streamFile: event.stream_file ?? null,
|
|
52173
52144
|
streamContent
|
|
52174
52145
|
},
|
|
52175
52146
|
capture: {
|
|
@@ -52178,11 +52149,11 @@ async function buildLogEventDetail(id, parsed, location, context2) {
|
|
|
52178
52149
|
responseBodyAvailable,
|
|
52179
52150
|
streamCaptured,
|
|
52180
52151
|
truncatedHints: [
|
|
52181
|
-
...buildTruncatedHints(
|
|
52152
|
+
...buildTruncatedHints(event, context2.logConfig?.bodyPolicy ?? bodyPolicy),
|
|
52182
52153
|
...streamWarning ? [streamWarning] : []
|
|
52183
52154
|
]
|
|
52184
52155
|
},
|
|
52185
|
-
rawEvent:
|
|
52156
|
+
rawEvent: event,
|
|
52186
52157
|
location
|
|
52187
52158
|
};
|
|
52188
52159
|
}
|
|
@@ -52894,13 +52865,6 @@ function startLogStorageBackgroundTask(logConfig) {
|
|
|
52894
52865
|
// src/logger.ts
|
|
52895
52866
|
import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
52896
52867
|
import { join as join7 } from "path";
|
|
52897
|
-
var SENSITIVE_HEADERS = new Set([
|
|
52898
|
-
"authorization",
|
|
52899
|
-
"x-api-key",
|
|
52900
|
-
"cookie",
|
|
52901
|
-
"set-cookie",
|
|
52902
|
-
"proxy-authorization"
|
|
52903
|
-
]);
|
|
52904
52868
|
|
|
52905
52869
|
class Logger {
|
|
52906
52870
|
baseDir;
|
|
@@ -52981,14 +52945,10 @@ function getLogger() {
|
|
|
52981
52945
|
function resetLogger() {
|
|
52982
52946
|
instance = null;
|
|
52983
52947
|
}
|
|
52984
|
-
function
|
|
52948
|
+
function collectHeaders(headers) {
|
|
52985
52949
|
const result = {};
|
|
52986
52950
|
headers.forEach((value, key2) => {
|
|
52987
|
-
|
|
52988
|
-
result[key2] = value.length > 4 ? `${value.slice(0, 4)}****` : "****";
|
|
52989
|
-
} else {
|
|
52990
|
-
result[key2] = value;
|
|
52991
|
-
}
|
|
52951
|
+
result[key2] = value;
|
|
52992
52952
|
});
|
|
52993
52953
|
return result;
|
|
52994
52954
|
}
|
|
@@ -53000,19 +52960,8 @@ function extractProviderRequestId(headers) {
|
|
|
53000
52960
|
}
|
|
53001
52961
|
return null;
|
|
53002
52962
|
}
|
|
53003
|
-
function
|
|
53004
|
-
|
|
53005
|
-
const parsed = new URL(rawUrl);
|
|
53006
|
-
if (!parsed.username && !parsed.password)
|
|
53007
|
-
return rawUrl;
|
|
53008
|
-
if (parsed.username)
|
|
53009
|
-
parsed.username = "****";
|
|
53010
|
-
if (parsed.password)
|
|
53011
|
-
parsed.password = "****";
|
|
53012
|
-
return parsed.toString();
|
|
53013
|
-
} catch {
|
|
53014
|
-
return rawUrl;
|
|
53015
|
-
}
|
|
52963
|
+
function normalizeUrl(rawUrl) {
|
|
52964
|
+
return rawUrl;
|
|
53016
52965
|
}
|
|
53017
52966
|
|
|
53018
52967
|
// src/openapi.ts
|
|
@@ -53459,7 +53408,7 @@ var openAPISpec = {
|
|
|
53459
53408
|
method: { type: "string" },
|
|
53460
53409
|
path: { type: "string" },
|
|
53461
53410
|
contentType: { type: ["string", "null"] },
|
|
53462
|
-
|
|
53411
|
+
requestHeaders: {
|
|
53463
53412
|
type: "object",
|
|
53464
53413
|
additionalProperties: { type: "string" }
|
|
53465
53414
|
},
|
|
@@ -54179,14 +54128,14 @@ function buildLogEvent(logMeta, targetUrl, proxyUrl, tsEnd, overrides) {
|
|
|
54179
54128
|
provider: logMeta.provider,
|
|
54180
54129
|
model_in: logMeta.modelIn,
|
|
54181
54130
|
model_out: logMeta.modelOut,
|
|
54182
|
-
target_url:
|
|
54183
|
-
proxy_url: proxyUrl ?
|
|
54131
|
+
target_url: normalizeUrl(targetUrl),
|
|
54132
|
+
proxy_url: proxyUrl ? normalizeUrl(proxyUrl) : null,
|
|
54184
54133
|
is_stream: logMeta.isStream,
|
|
54185
54134
|
upstream_status: 0,
|
|
54186
54135
|
content_type_req: logMeta.contentTypeReq,
|
|
54187
54136
|
content_type_res: null,
|
|
54188
54137
|
user_agent: logMeta.userAgent,
|
|
54189
|
-
|
|
54138
|
+
request_headers: logMeta.requestHeaders,
|
|
54190
54139
|
response_headers: {},
|
|
54191
54140
|
request_bytes: logMeta.requestBytes,
|
|
54192
54141
|
response_bytes: null,
|
|
@@ -54366,7 +54315,7 @@ function createModelRoutingHandler(options) {
|
|
|
54366
54315
|
contentTypeReq: c2.req.header("content-type") ?? null,
|
|
54367
54316
|
userAgent: c2.req.header("user-agent") ?? null,
|
|
54368
54317
|
requestBytes: Buffer.byteLength(body, "utf-8"),
|
|
54369
|
-
|
|
54318
|
+
requestHeaders: collectHeaders(c2.req.raw.headers)
|
|
54370
54319
|
};
|
|
54371
54320
|
return proxyRequest(c2, {
|
|
54372
54321
|
targetUrl,
|