@lakphy/local-router 0.3.0 → 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 +138 -105
- package/dist/entry.js +46 -97
- 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,18 +55925,19 @@ 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]
|
|
55956
55934
|
config <subcommand> [...args]
|
|
55935
|
+
get-route --type <route-type> [--model-alias <alias>] [--config <path>]
|
|
55957
55936
|
health
|
|
55958
55937
|
version
|
|
55959
55938
|
|
|
55960
55939
|
Hidden commands:
|
|
55961
|
-
__run-server --mode <daemon|foreground> [--config] [--host] [--port] [--log-file]
|
|
55940
|
+
__run-server --mode <daemon|foreground> [--config] [--host] [--port] [--idle-timeout] [--log-file]
|
|
55962
55941
|
`);
|
|
55963
55942
|
}
|
|
55964
55943
|
async function printVersion() {
|
|
@@ -55988,7 +55967,8 @@ async function cmdStart(args) {
|
|
|
55988
55967
|
mode: "foreground",
|
|
55989
55968
|
config: flags.config,
|
|
55990
55969
|
host: flags.host,
|
|
55991
|
-
port: flags.port
|
|
55970
|
+
port: flags.port,
|
|
55971
|
+
idleTimeoutSeconds: flags.idleTimeoutSeconds
|
|
55992
55972
|
});
|
|
55993
55973
|
}
|
|
55994
55974
|
async function cmdStop() {
|
|
@@ -56119,6 +56099,52 @@ async function cmdInit(args) {
|
|
|
56119
56099
|
const result = ensureConfigFile(configPath);
|
|
56120
56100
|
console.log(result.created ? `\u5DF2\u521D\u59CB\u5316\u914D\u7F6E: ${result.path}` : `\u914D\u7F6E\u5DF2\u5B58\u5728: ${result.path}`);
|
|
56121
56101
|
}
|
|
56102
|
+
function formatRouteTarget(target) {
|
|
56103
|
+
return `${target.provider} / ${target.model}`;
|
|
56104
|
+
}
|
|
56105
|
+
async function cmdGetRoute(args) {
|
|
56106
|
+
const parsed = parseArgs3({
|
|
56107
|
+
args,
|
|
56108
|
+
options: {
|
|
56109
|
+
type: { type: "string" },
|
|
56110
|
+
"model-alias": { type: "string" },
|
|
56111
|
+
model: { type: "string" },
|
|
56112
|
+
config: { type: "string" }
|
|
56113
|
+
},
|
|
56114
|
+
allowPositionals: true,
|
|
56115
|
+
strict: false
|
|
56116
|
+
});
|
|
56117
|
+
const routeType = parsed.values.type;
|
|
56118
|
+
if (!routeType) {
|
|
56119
|
+
throw new Error("\u7528\u6CD5: local-router get-route --type <route-type> [--model-alias <alias>] [--config <path>]");
|
|
56120
|
+
}
|
|
56121
|
+
const configPath = resolveConfigPath(parsed.values.config);
|
|
56122
|
+
const config2 = loadConfig(configPath);
|
|
56123
|
+
const modelMap = config2.routes[routeType];
|
|
56124
|
+
if (!modelMap) {
|
|
56125
|
+
throw new Error(`route type \u4E0D\u5B58\u5728: ${routeType}`);
|
|
56126
|
+
}
|
|
56127
|
+
const modelAlias = parsed.values["model-alias"] ?? parsed.values.model;
|
|
56128
|
+
if (modelAlias) {
|
|
56129
|
+
const target = modelMap[modelAlias] ?? modelMap["*"];
|
|
56130
|
+
if (!target) {
|
|
56131
|
+
throw new Error(`\u672A\u547D\u4E2D\u8DEF\u7531\u4E14\u7F3A\u5C11\u515C\u5E95: ${routeType}`);
|
|
56132
|
+
}
|
|
56133
|
+
console.log(formatRouteTarget(target));
|
|
56134
|
+
return;
|
|
56135
|
+
}
|
|
56136
|
+
const chunks = [];
|
|
56137
|
+
for (const [alias, target] of Object.entries(modelMap)) {
|
|
56138
|
+
if (alias === "*")
|
|
56139
|
+
continue;
|
|
56140
|
+
chunks.push(`${alias} : ${formatRouteTarget(target)}`);
|
|
56141
|
+
}
|
|
56142
|
+
const fallback = modelMap["*"];
|
|
56143
|
+
if (fallback) {
|
|
56144
|
+
chunks.push(`default : ${formatRouteTarget(fallback)}`);
|
|
56145
|
+
}
|
|
56146
|
+
console.log(chunks.join(" | "));
|
|
56147
|
+
}
|
|
56122
56148
|
async function cmdHealth() {
|
|
56123
56149
|
await cleanupIfStale();
|
|
56124
56150
|
const state = readRuntimeState();
|
|
@@ -56141,6 +56167,7 @@ async function cmdRunServer(args) {
|
|
|
56141
56167
|
config: { type: "string" },
|
|
56142
56168
|
host: { type: "string" },
|
|
56143
56169
|
port: { type: "string" },
|
|
56170
|
+
"idle-timeout": { type: "string" },
|
|
56144
56171
|
"log-file": { type: "string" }
|
|
56145
56172
|
},
|
|
56146
56173
|
allowPositionals: true,
|
|
@@ -56149,11 +56176,14 @@ async function cmdRunServer(args) {
|
|
|
56149
56176
|
const mode = parsed.values.mode === "daemon" ? "daemon" : "foreground";
|
|
56150
56177
|
const portRaw = parsed.values.port;
|
|
56151
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;
|
|
56152
56181
|
await runServerProcess({
|
|
56153
56182
|
mode,
|
|
56154
56183
|
config: parsed.values.config,
|
|
56155
56184
|
host: parsed.values.host,
|
|
56156
56185
|
port,
|
|
56186
|
+
idleTimeoutSeconds: Number.isFinite(idleTimeoutSeconds) ? idleTimeoutSeconds : undefined,
|
|
56157
56187
|
logFile: parsed.values["log-file"]
|
|
56158
56188
|
});
|
|
56159
56189
|
}
|
|
@@ -56184,6 +56214,9 @@ async function main() {
|
|
|
56184
56214
|
case "config":
|
|
56185
56215
|
await cmdConfig(rest);
|
|
56186
56216
|
return;
|
|
56217
|
+
case "get-route":
|
|
56218
|
+
await cmdGetRoute(rest);
|
|
56219
|
+
return;
|
|
56187
56220
|
case "version":
|
|
56188
56221
|
await printVersion();
|
|
56189
56222
|
return;
|