@lakphy/local-router 0.3.1 → 0.4.0-beta.1
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 +28 -2
- package/dist/cli.js +495 -151
- package/dist/entry.js +446 -136
- package/dist/index.js +13699 -0
- package/dist/web/assets/{index-BhvlBO7m.js → index-DuaLtWlv.js} +61 -60
- package/dist/web/assets/index-RuflkSNT.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-DgDq1A8B.css +0 -2
package/dist/cli.js
CHANGED
|
@@ -4935,7 +4935,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
4935
4935
|
const schOrFunc = root2.refs[ref];
|
|
4936
4936
|
if (schOrFunc)
|
|
4937
4937
|
return schOrFunc;
|
|
4938
|
-
let _sch =
|
|
4938
|
+
let _sch = resolve6.call(this, root2, ref);
|
|
4939
4939
|
if (_sch === undefined) {
|
|
4940
4940
|
const schema = (_a21 = root2.localRefs) === null || _a21 === undefined ? undefined : _a21[ref];
|
|
4941
4941
|
const { schemaId } = this.opts;
|
|
@@ -4962,7 +4962,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
4962
4962
|
function sameSchemaEnv(s1, s2) {
|
|
4963
4963
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
4964
4964
|
}
|
|
4965
|
-
function
|
|
4965
|
+
function resolve6(root2, ref) {
|
|
4966
4966
|
let sch;
|
|
4967
4967
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
4968
4968
|
ref = sch;
|
|
@@ -5492,7 +5492,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
5492
5492
|
}
|
|
5493
5493
|
return uri;
|
|
5494
5494
|
}
|
|
5495
|
-
function
|
|
5495
|
+
function resolve6(baseURI, relativeURI, options) {
|
|
5496
5496
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
5497
5497
|
const resolved = resolveComponent(parse7(baseURI, schemelessOptions), parse7(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
5498
5498
|
schemelessOptions.skipEscape = true;
|
|
@@ -5720,7 +5720,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
5720
5720
|
var fastUri = {
|
|
5721
5721
|
SCHEMES,
|
|
5722
5722
|
normalize,
|
|
5723
|
-
resolve:
|
|
5723
|
+
resolve: resolve6,
|
|
5724
5724
|
resolveComponent,
|
|
5725
5725
|
equal,
|
|
5726
5726
|
serialize,
|
|
@@ -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
|
|
@@ -10518,6 +10518,7 @@ import { parseArgs } from "util";
|
|
|
10518
10518
|
|
|
10519
10519
|
// src/index.ts
|
|
10520
10520
|
import { readFileSync as readFileSync4 } from "fs";
|
|
10521
|
+
import { dirname as dirname3, resolve as resolve6 } from "path";
|
|
10521
10522
|
|
|
10522
10523
|
// node_modules/.bun/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/dist/index.mjs
|
|
10523
10524
|
var marker = "vercel.ai.error";
|
|
@@ -51823,7 +51824,6 @@ var MAX_QUERY_LIMIT = 200;
|
|
|
51823
51824
|
var DEFAULT_QUERY_LIMIT = 50;
|
|
51824
51825
|
var MAX_EXPORT_ROWS = 5000;
|
|
51825
51826
|
var MAX_Q_LENGTH = 200;
|
|
51826
|
-
var SENSITIVE_FIELD_PATTERN = /(authorization|token|cookie|password|passphrase|secret|api[_-]?key)/i;
|
|
51827
51827
|
function encodeBase64Url(value) {
|
|
51828
51828
|
return Buffer.from(value, "utf-8").toString("base64url");
|
|
51829
51829
|
}
|
|
@@ -52039,34 +52039,6 @@ function eventToSummary(item) {
|
|
|
52039
52039
|
sessionId: identity.sessionId
|
|
52040
52040
|
};
|
|
52041
52041
|
}
|
|
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
52042
|
function detectBodyPolicy(event) {
|
|
52071
52043
|
const hasRequestBody = event.request_body !== undefined;
|
|
52072
52044
|
const hasResponseBody = event.response_body !== undefined;
|
|
@@ -52139,53 +52111,61 @@ function readStreamContent(baseDir, streamFile) {
|
|
|
52139
52111
|
}
|
|
52140
52112
|
}
|
|
52141
52113
|
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),
|
|
52114
|
+
const event = parsed;
|
|
52115
|
+
const level = getLevel(event);
|
|
52116
|
+
const statusClass = getStatusClass2(event);
|
|
52117
|
+
const bodyPolicy = detectBodyPolicy(event);
|
|
52118
|
+
const requestBodyAvailable = event.request_body !== undefined;
|
|
52119
|
+
const responseBodyAvailable = event.response_body !== undefined;
|
|
52120
|
+
const streamCaptured = Boolean(event.stream_file);
|
|
52121
|
+
const { content: streamContent, warning: streamWarning } = readStreamContent(resolveLogBaseDir(context2.logConfig), event.stream_file);
|
|
52122
|
+
const hasPluginData = event.plugins_request || event.plugins_response || event.request_body_after_plugins !== undefined || event.request_url_after_plugins !== undefined || event.response_body_after_plugins !== undefined;
|
|
52123
|
+
const pluginsSection = hasPluginData ? {
|
|
52124
|
+
request: event.plugins_request,
|
|
52125
|
+
response: event.plugins_response,
|
|
52126
|
+
requestBodyAfterPlugins: event.request_body_after_plugins,
|
|
52127
|
+
requestUrlAfterPlugins: event.request_url_after_plugins,
|
|
52128
|
+
responseBodyAfterPlugins: event.response_body_after_plugins
|
|
52129
|
+
} : undefined;
|
|
52150
52130
|
return {
|
|
52151
52131
|
id,
|
|
52152
52132
|
summary: {
|
|
52153
52133
|
id,
|
|
52154
|
-
ts:
|
|
52134
|
+
ts: event.ts_start,
|
|
52155
52135
|
level,
|
|
52156
|
-
provider:
|
|
52157
|
-
routeType:
|
|
52158
|
-
routeRuleKey:
|
|
52159
|
-
requestId:
|
|
52160
|
-
latencyMs: Math.max(0,
|
|
52161
|
-
upstreamStatus:
|
|
52136
|
+
provider: event.provider,
|
|
52137
|
+
routeType: event.route_type,
|
|
52138
|
+
routeRuleKey: event.route_rule_key,
|
|
52139
|
+
requestId: event.request_id,
|
|
52140
|
+
latencyMs: Math.max(0, event.latency_ms ?? 0),
|
|
52141
|
+
upstreamStatus: event.upstream_status ?? 0,
|
|
52162
52142
|
statusClass,
|
|
52163
52143
|
hasError: level === "error",
|
|
52164
|
-
model:
|
|
52165
|
-
modelIn:
|
|
52166
|
-
modelOut:
|
|
52144
|
+
model: event.model_out || event.model_in,
|
|
52145
|
+
modelIn: event.model_in,
|
|
52146
|
+
modelOut: event.model_out
|
|
52167
52147
|
},
|
|
52168
52148
|
request: {
|
|
52169
|
-
method:
|
|
52170
|
-
path:
|
|
52171
|
-
contentType:
|
|
52172
|
-
|
|
52173
|
-
requestBody:
|
|
52149
|
+
method: event.method,
|
|
52150
|
+
path: event.path,
|
|
52151
|
+
contentType: event.content_type_req,
|
|
52152
|
+
requestHeaders: event.request_headers,
|
|
52153
|
+
requestBody: event.request_body ?? null
|
|
52174
52154
|
},
|
|
52175
52155
|
response: {
|
|
52176
|
-
upstreamStatus:
|
|
52177
|
-
contentType:
|
|
52178
|
-
responseHeaders:
|
|
52179
|
-
responseBody:
|
|
52156
|
+
upstreamStatus: event.upstream_status ?? 0,
|
|
52157
|
+
contentType: event.content_type_res,
|
|
52158
|
+
responseHeaders: event.response_headers,
|
|
52159
|
+
responseBody: event.response_body ?? null
|
|
52180
52160
|
},
|
|
52181
52161
|
upstream: {
|
|
52182
|
-
targetUrl:
|
|
52183
|
-
proxyUrl:
|
|
52184
|
-
providerRequestId:
|
|
52185
|
-
errorType:
|
|
52186
|
-
errorMessage:
|
|
52187
|
-
isStream:
|
|
52188
|
-
streamFile:
|
|
52162
|
+
targetUrl: event.target_url,
|
|
52163
|
+
proxyUrl: event.proxy_url ?? null,
|
|
52164
|
+
providerRequestId: event.provider_request_id,
|
|
52165
|
+
errorType: event.error_type,
|
|
52166
|
+
errorMessage: event.error_message,
|
|
52167
|
+
isStream: event.is_stream,
|
|
52168
|
+
streamFile: event.stream_file ?? null,
|
|
52189
52169
|
streamContent
|
|
52190
52170
|
},
|
|
52191
52171
|
capture: {
|
|
@@ -52194,11 +52174,12 @@ async function buildLogEventDetail(id, parsed, location, context2) {
|
|
|
52194
52174
|
responseBodyAvailable,
|
|
52195
52175
|
streamCaptured,
|
|
52196
52176
|
truncatedHints: [
|
|
52197
|
-
...buildTruncatedHints(
|
|
52177
|
+
...buildTruncatedHints(event, context2.logConfig?.bodyPolicy ?? bodyPolicy),
|
|
52198
52178
|
...streamWarning ? [streamWarning] : []
|
|
52199
52179
|
]
|
|
52200
52180
|
},
|
|
52201
|
-
|
|
52181
|
+
...pluginsSection && { plugins: pluginsSection },
|
|
52182
|
+
rawEvent: event,
|
|
52202
52183
|
location
|
|
52203
52184
|
};
|
|
52204
52185
|
}
|
|
@@ -52910,13 +52891,6 @@ function startLogStorageBackgroundTask(logConfig) {
|
|
|
52910
52891
|
// src/logger.ts
|
|
52911
52892
|
import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
52912
52893
|
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
52894
|
|
|
52921
52895
|
class Logger {
|
|
52922
52896
|
baseDir;
|
|
@@ -52997,14 +52971,10 @@ function getLogger() {
|
|
|
52997
52971
|
function resetLogger() {
|
|
52998
52972
|
instance = null;
|
|
52999
52973
|
}
|
|
53000
|
-
function
|
|
52974
|
+
function collectHeaders(headers) {
|
|
53001
52975
|
const result = {};
|
|
53002
52976
|
headers.forEach((value, key2) => {
|
|
53003
|
-
|
|
53004
|
-
result[key2] = value.length > 4 ? `${value.slice(0, 4)}****` : "****";
|
|
53005
|
-
} else {
|
|
53006
|
-
result[key2] = value;
|
|
53007
|
-
}
|
|
52977
|
+
result[key2] = value;
|
|
53008
52978
|
});
|
|
53009
52979
|
return result;
|
|
53010
52980
|
}
|
|
@@ -53016,19 +52986,8 @@ function extractProviderRequestId(headers) {
|
|
|
53016
52986
|
}
|
|
53017
52987
|
return null;
|
|
53018
52988
|
}
|
|
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
|
-
}
|
|
52989
|
+
function normalizeUrl(rawUrl) {
|
|
52990
|
+
return rawUrl;
|
|
53032
52991
|
}
|
|
53033
52992
|
|
|
53034
52993
|
// src/openapi.ts
|
|
@@ -53475,7 +53434,7 @@ var openAPISpec = {
|
|
|
53475
53434
|
method: { type: "string" },
|
|
53476
53435
|
path: { type: "string" },
|
|
53477
53436
|
contentType: { type: ["string", "null"] },
|
|
53478
|
-
|
|
53437
|
+
requestHeaders: {
|
|
53479
53438
|
type: "object",
|
|
53480
53439
|
additionalProperties: { type: "string" }
|
|
53481
53440
|
},
|
|
@@ -54132,10 +54091,249 @@ var openAPISpec = {
|
|
|
54132
54091
|
}
|
|
54133
54092
|
};
|
|
54134
54093
|
|
|
54094
|
+
// src/plugin-loader.ts
|
|
54095
|
+
import { resolve as resolve5 } from "path";
|
|
54096
|
+
function isLocalPath(pkg) {
|
|
54097
|
+
return pkg.startsWith("./") || pkg.startsWith("../") || pkg.startsWith("/") || /^[A-Za-z]:[\\/]/.test(pkg);
|
|
54098
|
+
}
|
|
54099
|
+
async function importPlugin(pkg, configDir) {
|
|
54100
|
+
let modulePath;
|
|
54101
|
+
if (isLocalPath(pkg)) {
|
|
54102
|
+
const absolutePath = resolve5(configDir, pkg);
|
|
54103
|
+
modulePath = `${absolutePath}?t=${Date.now()}`;
|
|
54104
|
+
} else {
|
|
54105
|
+
modulePath = pkg;
|
|
54106
|
+
}
|
|
54107
|
+
const mod = await import(modulePath);
|
|
54108
|
+
const definition = mod.default ?? mod;
|
|
54109
|
+
if (!definition || typeof definition.name !== "string" || typeof definition.create !== "function") {
|
|
54110
|
+
throw new Error(`\u63D2\u4EF6 "${pkg}" \u5BFC\u51FA\u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u9700\u5BFC\u51FA\u5305\u542B name \u548C create \u7684 PluginDefinition`);
|
|
54111
|
+
}
|
|
54112
|
+
return definition;
|
|
54113
|
+
}
|
|
54114
|
+
|
|
54115
|
+
class PluginManager {
|
|
54116
|
+
plugins = new Map;
|
|
54117
|
+
configDir;
|
|
54118
|
+
constructor(configDir) {
|
|
54119
|
+
this.configDir = configDir;
|
|
54120
|
+
}
|
|
54121
|
+
async loadPluginsForProvider(providerName, pluginConfigs) {
|
|
54122
|
+
const loaded = [];
|
|
54123
|
+
const failures = [];
|
|
54124
|
+
for (const config2 of pluginConfigs) {
|
|
54125
|
+
try {
|
|
54126
|
+
const definition = await importPlugin(config2.package, this.configDir);
|
|
54127
|
+
const instance2 = await definition.create(config2.params ?? {});
|
|
54128
|
+
loaded.push({ config: config2, definition, instance: instance2 });
|
|
54129
|
+
} catch (err) {
|
|
54130
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
54131
|
+
console.error(`[plugin] \u52A0\u8F7D\u63D2\u4EF6 "${config2.package}" \u5931\u8D25 (provider: ${providerName}):`, errorMsg);
|
|
54132
|
+
failures.push({ provider: providerName, package: config2.package, error: errorMsg });
|
|
54133
|
+
}
|
|
54134
|
+
}
|
|
54135
|
+
return { loaded, failures };
|
|
54136
|
+
}
|
|
54137
|
+
async reloadAll(providers) {
|
|
54138
|
+
const newPlugins = new Map;
|
|
54139
|
+
const allFailures = [];
|
|
54140
|
+
const oldPluginsToDispose = [];
|
|
54141
|
+
for (const [providerName, providerConfig] of Object.entries(providers)) {
|
|
54142
|
+
if (providerConfig.plugins && providerConfig.plugins.length > 0) {
|
|
54143
|
+
const { loaded, failures } = await this.loadPluginsForProvider(providerName, providerConfig.plugins);
|
|
54144
|
+
allFailures.push(...failures);
|
|
54145
|
+
if (failures.length > 0) {
|
|
54146
|
+
console.warn(`[plugin] provider "${providerName}" \u6709\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25\uFF0C\u4FDD\u7559\u65E7\u63D2\u4EF6\u94FE`);
|
|
54147
|
+
const oldLoaded = this.plugins.get(providerName);
|
|
54148
|
+
if (oldLoaded) {
|
|
54149
|
+
newPlugins.set(providerName, oldLoaded);
|
|
54150
|
+
}
|
|
54151
|
+
for (const { instance: instance2, config: config2 } of loaded) {
|
|
54152
|
+
try {
|
|
54153
|
+
await instance2.dispose?.();
|
|
54154
|
+
} catch (err) {
|
|
54155
|
+
console.error(`[plugin] \u56DE\u6EDA\u9500\u6BC1\u63D2\u4EF6 "${config2.package}" \u5931\u8D25:`, err instanceof Error ? err.message : err);
|
|
54156
|
+
}
|
|
54157
|
+
}
|
|
54158
|
+
} else {
|
|
54159
|
+
newPlugins.set(providerName, loaded);
|
|
54160
|
+
const oldLoaded = this.plugins.get(providerName);
|
|
54161
|
+
if (oldLoaded) {
|
|
54162
|
+
oldPluginsToDispose.push(...oldLoaded);
|
|
54163
|
+
}
|
|
54164
|
+
}
|
|
54165
|
+
}
|
|
54166
|
+
}
|
|
54167
|
+
for (const [providerName, oldLoaded] of this.plugins) {
|
|
54168
|
+
if (!newPlugins.has(providerName)) {
|
|
54169
|
+
oldPluginsToDispose.push(...oldLoaded);
|
|
54170
|
+
}
|
|
54171
|
+
}
|
|
54172
|
+
this.plugins = newPlugins;
|
|
54173
|
+
if (oldPluginsToDispose.length > 0) {
|
|
54174
|
+
setTimeout(() => {
|
|
54175
|
+
this.disposePluginList(oldPluginsToDispose).catch((err) => {
|
|
54176
|
+
console.error("[plugin] \u65E7\u63D2\u4EF6\u9500\u6BC1\u5931\u8D25:", err);
|
|
54177
|
+
});
|
|
54178
|
+
}, 5000);
|
|
54179
|
+
}
|
|
54180
|
+
if (allFailures.length > 0) {
|
|
54181
|
+
console.warn(`[plugin] \u70ED\u91CD\u8F7D\u5B8C\u6210\uFF0C\u4F46\u6709 ${allFailures.length} \u4E2A\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25:`, allFailures.map((f) => `${f.provider}/${f.package}`).join(", "));
|
|
54182
|
+
}
|
|
54183
|
+
return { ok: allFailures.length === 0, failures: allFailures };
|
|
54184
|
+
}
|
|
54185
|
+
getPlugins(providerName) {
|
|
54186
|
+
const loaded = this.plugins.get(providerName);
|
|
54187
|
+
if (!loaded)
|
|
54188
|
+
return [];
|
|
54189
|
+
return loaded.map((l) => l.instance);
|
|
54190
|
+
}
|
|
54191
|
+
getLoadedPlugins(providerName) {
|
|
54192
|
+
return this.plugins.get(providerName) ?? [];
|
|
54193
|
+
}
|
|
54194
|
+
async disposeAll() {
|
|
54195
|
+
const allPlugins = [];
|
|
54196
|
+
for (const [, loadedPlugins] of this.plugins) {
|
|
54197
|
+
allPlugins.push(...loadedPlugins);
|
|
54198
|
+
}
|
|
54199
|
+
this.plugins.clear();
|
|
54200
|
+
await this.disposePluginList(allPlugins);
|
|
54201
|
+
}
|
|
54202
|
+
async disposePluginList(plugins) {
|
|
54203
|
+
for (const { instance: instance2, config: config2 } of plugins) {
|
|
54204
|
+
try {
|
|
54205
|
+
await instance2.dispose?.();
|
|
54206
|
+
} catch (err) {
|
|
54207
|
+
console.error(`[plugin] \u9500\u6BC1\u63D2\u4EF6 "${config2.package}" \u5931\u8D25:`, err instanceof Error ? err.message : err);
|
|
54208
|
+
}
|
|
54209
|
+
}
|
|
54210
|
+
}
|
|
54211
|
+
async disposePluginMap(pluginMap) {
|
|
54212
|
+
const allPlugins = [];
|
|
54213
|
+
for (const [, loadedPlugins] of pluginMap) {
|
|
54214
|
+
allPlugins.push(...loadedPlugins);
|
|
54215
|
+
}
|
|
54216
|
+
await this.disposePluginList(allPlugins);
|
|
54217
|
+
}
|
|
54218
|
+
}
|
|
54219
|
+
|
|
54135
54220
|
// src/proxy.ts
|
|
54136
54221
|
import { appendFile, readFile, unlink } from "fs/promises";
|
|
54137
54222
|
import { join as join8 } from "path";
|
|
54138
54223
|
import { tmpdir } from "os";
|
|
54224
|
+
|
|
54225
|
+
// src/plugin-engine.ts
|
|
54226
|
+
async function executeRequestPlugins(plugins, ctx, url2, headers, body) {
|
|
54227
|
+
let currentUrl = url2;
|
|
54228
|
+
let currentHeaders = headers;
|
|
54229
|
+
let currentBody = body;
|
|
54230
|
+
for (const plugin of plugins) {
|
|
54231
|
+
if (!plugin.onRequest)
|
|
54232
|
+
continue;
|
|
54233
|
+
try {
|
|
54234
|
+
const result = await plugin.onRequest({
|
|
54235
|
+
ctx,
|
|
54236
|
+
url: currentUrl,
|
|
54237
|
+
headers: currentHeaders,
|
|
54238
|
+
body: currentBody
|
|
54239
|
+
});
|
|
54240
|
+
if (result) {
|
|
54241
|
+
if (result.url !== undefined)
|
|
54242
|
+
currentUrl = result.url;
|
|
54243
|
+
if (result.headers !== undefined)
|
|
54244
|
+
currentHeaders = result.headers;
|
|
54245
|
+
if (result.body !== undefined)
|
|
54246
|
+
currentBody = result.body;
|
|
54247
|
+
}
|
|
54248
|
+
} catch (err) {
|
|
54249
|
+
const error48 = err instanceof Error ? err : new Error(String(err));
|
|
54250
|
+
try {
|
|
54251
|
+
await plugin.onError?.({ ctx, phase: "request", error: error48 });
|
|
54252
|
+
} catch {}
|
|
54253
|
+
}
|
|
54254
|
+
}
|
|
54255
|
+
return { url: currentUrl, headers: currentHeaders, body: currentBody };
|
|
54256
|
+
}
|
|
54257
|
+
async function executeJsonResponsePlugins(plugins, ctx, status, headers, body) {
|
|
54258
|
+
let currentStatus = status;
|
|
54259
|
+
let currentHeaders = headers;
|
|
54260
|
+
let currentBody = body;
|
|
54261
|
+
for (let i = plugins.length - 1;i >= 0; i--) {
|
|
54262
|
+
const plugin = plugins[i];
|
|
54263
|
+
if (!plugin.onResponse)
|
|
54264
|
+
continue;
|
|
54265
|
+
try {
|
|
54266
|
+
const result = await plugin.onResponse({
|
|
54267
|
+
ctx,
|
|
54268
|
+
status: currentStatus,
|
|
54269
|
+
headers: currentHeaders,
|
|
54270
|
+
body: currentBody
|
|
54271
|
+
});
|
|
54272
|
+
if (result) {
|
|
54273
|
+
if (result.status !== undefined)
|
|
54274
|
+
currentStatus = result.status;
|
|
54275
|
+
if (result.headers !== undefined)
|
|
54276
|
+
currentHeaders = result.headers;
|
|
54277
|
+
if (result.body !== undefined)
|
|
54278
|
+
currentBody = result.body;
|
|
54279
|
+
}
|
|
54280
|
+
} catch (err) {
|
|
54281
|
+
const error48 = err instanceof Error ? err : new Error(String(err));
|
|
54282
|
+
try {
|
|
54283
|
+
await plugin.onError?.({ ctx, phase: "response", error: error48 });
|
|
54284
|
+
} catch {}
|
|
54285
|
+
}
|
|
54286
|
+
}
|
|
54287
|
+
return { status: currentStatus, headers: currentHeaders, body: currentBody };
|
|
54288
|
+
}
|
|
54289
|
+
async function createSSEPluginTransform(plugins, ctx, status, headers) {
|
|
54290
|
+
let currentStatus = status;
|
|
54291
|
+
let currentHeaders = headers;
|
|
54292
|
+
const transforms = [];
|
|
54293
|
+
for (let i = plugins.length - 1;i >= 0; i--) {
|
|
54294
|
+
const plugin = plugins[i];
|
|
54295
|
+
if (!plugin.onSSEResponse)
|
|
54296
|
+
continue;
|
|
54297
|
+
try {
|
|
54298
|
+
const result = await plugin.onSSEResponse({
|
|
54299
|
+
ctx,
|
|
54300
|
+
status: currentStatus,
|
|
54301
|
+
headers: currentHeaders
|
|
54302
|
+
});
|
|
54303
|
+
if (result) {
|
|
54304
|
+
if (result.status !== undefined)
|
|
54305
|
+
currentStatus = result.status;
|
|
54306
|
+
if (result.headers !== undefined)
|
|
54307
|
+
currentHeaders = result.headers;
|
|
54308
|
+
if (result.transform)
|
|
54309
|
+
transforms.push(result.transform);
|
|
54310
|
+
}
|
|
54311
|
+
} catch (err) {
|
|
54312
|
+
const error48 = err instanceof Error ? err : new Error(String(err));
|
|
54313
|
+
try {
|
|
54314
|
+
await plugin.onError?.({ ctx, phase: "response", error: error48 });
|
|
54315
|
+
} catch {}
|
|
54316
|
+
}
|
|
54317
|
+
}
|
|
54318
|
+
if (transforms.length === 0) {
|
|
54319
|
+
return { status: currentStatus, headers: currentHeaders, transform: null };
|
|
54320
|
+
}
|
|
54321
|
+
if (transforms.length === 1) {
|
|
54322
|
+
return { status: currentStatus, headers: currentHeaders, transform: transforms[0] };
|
|
54323
|
+
}
|
|
54324
|
+
const entry = new TransformStream;
|
|
54325
|
+
let stream = entry.readable;
|
|
54326
|
+
for (const t of transforms) {
|
|
54327
|
+
stream = stream.pipeThrough(t);
|
|
54328
|
+
}
|
|
54329
|
+
return {
|
|
54330
|
+
status: currentStatus,
|
|
54331
|
+
headers: currentHeaders,
|
|
54332
|
+
transform: { writable: entry.writable, readable: stream }
|
|
54333
|
+
};
|
|
54334
|
+
}
|
|
54335
|
+
|
|
54336
|
+
// src/proxy.ts
|
|
54139
54337
|
var HOP_BY_HOP_HEADERS = new Set([
|
|
54140
54338
|
"connection",
|
|
54141
54339
|
"keep-alive",
|
|
@@ -54195,14 +54393,14 @@ function buildLogEvent(logMeta, targetUrl, proxyUrl, tsEnd, overrides) {
|
|
|
54195
54393
|
provider: logMeta.provider,
|
|
54196
54394
|
model_in: logMeta.modelIn,
|
|
54197
54395
|
model_out: logMeta.modelOut,
|
|
54198
|
-
target_url:
|
|
54199
|
-
proxy_url: proxyUrl ?
|
|
54396
|
+
target_url: normalizeUrl(targetUrl),
|
|
54397
|
+
proxy_url: proxyUrl ? normalizeUrl(proxyUrl) : null,
|
|
54200
54398
|
is_stream: logMeta.isStream,
|
|
54201
54399
|
upstream_status: 0,
|
|
54202
54400
|
content_type_req: logMeta.contentTypeReq,
|
|
54203
54401
|
content_type_res: null,
|
|
54204
54402
|
user_agent: logMeta.userAgent,
|
|
54205
|
-
|
|
54403
|
+
request_headers: logMeta.requestHeaders,
|
|
54206
54404
|
response_headers: {},
|
|
54207
54405
|
request_bytes: logMeta.requestBytes,
|
|
54208
54406
|
response_bytes: null,
|
|
@@ -54236,33 +54434,63 @@ async function flushTempCaptureToLogger(tempPath, requestId, dateStr, logger) {
|
|
|
54236
54434
|
}
|
|
54237
54435
|
}
|
|
54238
54436
|
async function proxyRequest(c2, options) {
|
|
54239
|
-
const { logMeta } = options;
|
|
54437
|
+
const { logMeta, plugins, pluginConfigs } = options;
|
|
54240
54438
|
const logger = getLogger();
|
|
54241
54439
|
const shouldLog = logger?.enabled ?? false;
|
|
54242
|
-
const
|
|
54440
|
+
const hasPlugins = plugins && plugins.length > 0;
|
|
54441
|
+
let targetUrl = options.targetUrl;
|
|
54442
|
+
let headers = buildUpstreamHeaders(c2.req.raw.headers, options.apiKey, options.authType);
|
|
54443
|
+
let bodyStr = options.body;
|
|
54444
|
+
const pluginLogOverrides = {};
|
|
54445
|
+
if (hasPlugins) {
|
|
54446
|
+
const bodyObj = JSON.parse(bodyStr);
|
|
54447
|
+
const ctx = {
|
|
54448
|
+
requestId: logMeta.requestId,
|
|
54449
|
+
provider: logMeta.provider,
|
|
54450
|
+
modelIn: logMeta.modelIn,
|
|
54451
|
+
modelOut: logMeta.modelOut,
|
|
54452
|
+
routeType: logMeta.routeType,
|
|
54453
|
+
isStream: logMeta.isStream
|
|
54454
|
+
};
|
|
54455
|
+
const result = await executeRequestPlugins(plugins, ctx, targetUrl, headers, bodyObj);
|
|
54456
|
+
if (pluginConfigs) {
|
|
54457
|
+
pluginLogOverrides.plugins_request = pluginConfigs;
|
|
54458
|
+
}
|
|
54459
|
+
if (result.url !== targetUrl) {
|
|
54460
|
+
targetUrl = result.url;
|
|
54461
|
+
pluginLogOverrides.request_url_after_plugins = targetUrl;
|
|
54462
|
+
}
|
|
54463
|
+
headers = result.headers;
|
|
54464
|
+
const newBodyStr = JSON.stringify(result.body);
|
|
54465
|
+
if (newBodyStr !== bodyStr) {
|
|
54466
|
+
bodyStr = newBodyStr;
|
|
54467
|
+
pluginLogOverrides.request_body_after_plugins = result.body;
|
|
54468
|
+
}
|
|
54469
|
+
}
|
|
54243
54470
|
const requestBody = shouldLog && logger?.bodyPolicy !== "off" ? JSON.parse(options.body) : undefined;
|
|
54244
54471
|
const proxy = options.proxy?.trim() ? options.proxy.trim() : undefined;
|
|
54245
54472
|
let upstreamRes;
|
|
54246
54473
|
try {
|
|
54247
|
-
upstreamRes = await fetch(
|
|
54474
|
+
upstreamRes = await fetch(targetUrl, {
|
|
54248
54475
|
method: c2.req.method,
|
|
54249
54476
|
headers,
|
|
54250
|
-
body:
|
|
54477
|
+
body: bodyStr,
|
|
54251
54478
|
...proxy ? { proxy } : {},
|
|
54252
54479
|
decompress: true
|
|
54253
54480
|
});
|
|
54254
54481
|
} catch (err) {
|
|
54255
54482
|
if (shouldLog) {
|
|
54256
|
-
logger?.writeEvent(buildLogEvent(logMeta,
|
|
54483
|
+
logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), {
|
|
54257
54484
|
error_type: err instanceof Error ? err.constructor.name : "UnknownError",
|
|
54258
54485
|
error_message: err instanceof Error ? err.message : String(err),
|
|
54259
|
-
...requestBody !== undefined && { request_body: requestBody }
|
|
54486
|
+
...requestBody !== undefined && { request_body: requestBody },
|
|
54487
|
+
...pluginLogOverrides
|
|
54260
54488
|
}));
|
|
54261
54489
|
}
|
|
54262
54490
|
throw err;
|
|
54263
54491
|
}
|
|
54264
54492
|
const responseHeaders = buildResponseHeaders(upstreamRes.headers);
|
|
54265
|
-
if (!shouldLog) {
|
|
54493
|
+
if (!shouldLog && !hasPlugins) {
|
|
54266
54494
|
return new Response(upstreamRes.body, {
|
|
54267
54495
|
status: upstreamRes.status,
|
|
54268
54496
|
headers: responseHeaders
|
|
@@ -54272,6 +54500,33 @@ async function proxyRequest(c2, options) {
|
|
|
54272
54500
|
const providerRequestId = extractProviderRequestId(upstreamRes.headers);
|
|
54273
54501
|
const dateStr = new Date(logMeta.tsStart).toISOString().slice(0, 10);
|
|
54274
54502
|
if (logMeta.isStream && upstreamRes.body) {
|
|
54503
|
+
let sseStatus = upstreamRes.status;
|
|
54504
|
+
let sseHeaders = responseHeaders;
|
|
54505
|
+
let sseTransform = null;
|
|
54506
|
+
if (hasPlugins) {
|
|
54507
|
+
const ctx = {
|
|
54508
|
+
requestId: logMeta.requestId,
|
|
54509
|
+
provider: logMeta.provider,
|
|
54510
|
+
modelIn: logMeta.modelIn,
|
|
54511
|
+
modelOut: logMeta.modelOut,
|
|
54512
|
+
routeType: logMeta.routeType,
|
|
54513
|
+
isStream: logMeta.isStream
|
|
54514
|
+
};
|
|
54515
|
+
const sseResult = await createSSEPluginTransform(plugins, ctx, upstreamRes.status, responseHeaders);
|
|
54516
|
+
sseStatus = sseResult.status;
|
|
54517
|
+
sseHeaders = sseResult.headers;
|
|
54518
|
+
sseTransform = sseResult.transform;
|
|
54519
|
+
if (pluginConfigs) {
|
|
54520
|
+
pluginLogOverrides.plugins_response = pluginConfigs;
|
|
54521
|
+
}
|
|
54522
|
+
}
|
|
54523
|
+
if (!shouldLog) {
|
|
54524
|
+
const outputBody2 = sseTransform ? upstreamRes.body.pipeThrough(sseTransform) : upstreamRes.body;
|
|
54525
|
+
return new Response(outputBody2, {
|
|
54526
|
+
status: sseStatus,
|
|
54527
|
+
headers: sseHeaders
|
|
54528
|
+
});
|
|
54529
|
+
}
|
|
54275
54530
|
const [clientStream, logStream] = upstreamRes.body.tee();
|
|
54276
54531
|
(async () => {
|
|
54277
54532
|
const tempPath = createTempStreamCapturePath(logMeta.requestId);
|
|
@@ -54293,30 +54548,61 @@ async function proxyRequest(c2, options) {
|
|
|
54293
54548
|
});
|
|
54294
54549
|
console.error("[logger] \u6D41\u5F0F\u65E5\u5FD7\u5904\u7406\u5931\u8D25:", err);
|
|
54295
54550
|
} finally {
|
|
54296
|
-
logger?.writeEvent(buildLogEvent(logMeta,
|
|
54297
|
-
upstream_status:
|
|
54551
|
+
logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), {
|
|
54552
|
+
upstream_status: sseStatus,
|
|
54298
54553
|
content_type_res: contentTypeRes,
|
|
54299
|
-
response_headers:
|
|
54554
|
+
response_headers: sseHeaders,
|
|
54300
54555
|
stream_bytes: streamBytes,
|
|
54301
54556
|
provider_request_id: providerRequestId,
|
|
54302
54557
|
...streamFile != null && { stream_file: streamFile },
|
|
54303
|
-
...requestBody !== undefined && { request_body: requestBody }
|
|
54558
|
+
...requestBody !== undefined && { request_body: requestBody },
|
|
54559
|
+
...pluginLogOverrides
|
|
54304
54560
|
}));
|
|
54305
54561
|
}
|
|
54306
54562
|
})();
|
|
54307
|
-
|
|
54308
|
-
|
|
54309
|
-
|
|
54563
|
+
const outputBody = sseTransform ? clientStream.pipeThrough(sseTransform) : clientStream;
|
|
54564
|
+
return new Response(outputBody, {
|
|
54565
|
+
status: sseStatus,
|
|
54566
|
+
headers: sseHeaders
|
|
54567
|
+
});
|
|
54568
|
+
}
|
|
54569
|
+
let responseText = await upstreamRes.text();
|
|
54570
|
+
let responseStatus = upstreamRes.status;
|
|
54571
|
+
let finalResponseHeaders = responseHeaders;
|
|
54572
|
+
if (hasPlugins) {
|
|
54573
|
+
const ctx = {
|
|
54574
|
+
requestId: logMeta.requestId,
|
|
54575
|
+
provider: logMeta.provider,
|
|
54576
|
+
modelIn: logMeta.modelIn,
|
|
54577
|
+
modelOut: logMeta.modelOut,
|
|
54578
|
+
routeType: logMeta.routeType,
|
|
54579
|
+
isStream: logMeta.isStream
|
|
54580
|
+
};
|
|
54581
|
+
const result = await executeJsonResponsePlugins(plugins, ctx, upstreamRes.status, responseHeaders, responseText);
|
|
54582
|
+
if (pluginConfigs) {
|
|
54583
|
+
pluginLogOverrides.plugins_response = pluginConfigs;
|
|
54584
|
+
}
|
|
54585
|
+
if (result.body !== responseText) {
|
|
54586
|
+
pluginLogOverrides.response_body_after_plugins = result.body;
|
|
54587
|
+
}
|
|
54588
|
+
responseStatus = result.status;
|
|
54589
|
+
finalResponseHeaders = result.headers;
|
|
54590
|
+
responseText = result.body;
|
|
54591
|
+
}
|
|
54592
|
+
if (!shouldLog) {
|
|
54593
|
+
return new Response(responseText, {
|
|
54594
|
+
status: responseStatus,
|
|
54595
|
+
headers: finalResponseHeaders
|
|
54310
54596
|
});
|
|
54311
54597
|
}
|
|
54312
|
-
const responseText = await upstreamRes.text();
|
|
54313
54598
|
const responseBytes = Buffer.byteLength(responseText, "utf-8");
|
|
54314
54599
|
const eventOverrides = {
|
|
54315
54600
|
upstream_status: upstreamRes.status,
|
|
54316
54601
|
content_type_res: contentTypeRes,
|
|
54317
|
-
response_headers:
|
|
54602
|
+
response_headers: finalResponseHeaders,
|
|
54318
54603
|
response_bytes: responseBytes,
|
|
54319
|
-
provider_request_id: providerRequestId
|
|
54604
|
+
provider_request_id: providerRequestId,
|
|
54605
|
+
...pluginLogOverrides
|
|
54320
54606
|
};
|
|
54321
54607
|
if (requestBody !== undefined) {
|
|
54322
54608
|
eventOverrides.request_body = requestBody;
|
|
@@ -54324,10 +54610,10 @@ async function proxyRequest(c2, options) {
|
|
|
54324
54610
|
if (logger?.bodyPolicy !== "off") {
|
|
54325
54611
|
eventOverrides.response_body = responseText;
|
|
54326
54612
|
}
|
|
54327
|
-
logger?.writeEvent(buildLogEvent(logMeta,
|
|
54613
|
+
logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), eventOverrides));
|
|
54328
54614
|
return new Response(responseText, {
|
|
54329
|
-
status:
|
|
54330
|
-
headers:
|
|
54615
|
+
status: responseStatus,
|
|
54616
|
+
headers: finalResponseHeaders
|
|
54331
54617
|
});
|
|
54332
54618
|
}
|
|
54333
54619
|
|
|
@@ -54342,7 +54628,7 @@ function resolveRoute(modelMap, incomingModel) {
|
|
|
54342
54628
|
return;
|
|
54343
54629
|
}
|
|
54344
54630
|
function createModelRoutingHandler(options) {
|
|
54345
|
-
const { routeType, store, authType, buildTargetUrl } = options;
|
|
54631
|
+
const { routeType, store, authType, buildTargetUrl, pluginManager } = options;
|
|
54346
54632
|
return async (c2) => {
|
|
54347
54633
|
const config2 = store.get();
|
|
54348
54634
|
const modelMap = config2.routes[routeType];
|
|
@@ -54382,51 +54668,62 @@ function createModelRoutingHandler(options) {
|
|
|
54382
54668
|
contentTypeReq: c2.req.header("content-type") ?? null,
|
|
54383
54669
|
userAgent: c2.req.header("user-agent") ?? null,
|
|
54384
54670
|
requestBytes: Buffer.byteLength(body, "utf-8"),
|
|
54385
|
-
|
|
54671
|
+
requestHeaders: collectHeaders(c2.req.raw.headers)
|
|
54386
54672
|
};
|
|
54673
|
+
const plugins = pluginManager?.getPlugins(target.provider) ?? [];
|
|
54674
|
+
const pluginConfigs = pluginManager?.getLoadedPlugins(target.provider) ?? [];
|
|
54387
54675
|
return proxyRequest(c2, {
|
|
54388
54676
|
targetUrl,
|
|
54389
54677
|
apiKey: provider.apiKey,
|
|
54390
54678
|
proxy: provider.proxy,
|
|
54391
54679
|
authType,
|
|
54392
54680
|
body,
|
|
54393
|
-
logMeta
|
|
54681
|
+
logMeta,
|
|
54682
|
+
plugins: plugins.length > 0 ? plugins : undefined,
|
|
54683
|
+
pluginConfigs: pluginConfigs.length > 0 ? pluginConfigs.map((lp) => ({
|
|
54684
|
+
name: lp.definition.name,
|
|
54685
|
+
package: lp.config.package,
|
|
54686
|
+
params: lp.config.params ?? {}
|
|
54687
|
+
})) : undefined
|
|
54394
54688
|
});
|
|
54395
54689
|
};
|
|
54396
54690
|
}
|
|
54397
54691
|
|
|
54398
54692
|
// src/routes/anthropic-messages.ts
|
|
54399
|
-
function createAnthropicMessagesRoutes(routeType, store) {
|
|
54693
|
+
function createAnthropicMessagesRoutes(routeType, store, pluginManager) {
|
|
54400
54694
|
const routes = new Hono2;
|
|
54401
54695
|
routes.post("/v1/messages", createModelRoutingHandler({
|
|
54402
54696
|
routeType,
|
|
54403
54697
|
store,
|
|
54404
54698
|
authType: "x-api-key",
|
|
54405
|
-
buildTargetUrl: (base) => `${base}/v1/messages
|
|
54699
|
+
buildTargetUrl: (base) => `${base}/v1/messages`,
|
|
54700
|
+
pluginManager
|
|
54406
54701
|
}));
|
|
54407
54702
|
return routes;
|
|
54408
54703
|
}
|
|
54409
54704
|
|
|
54410
54705
|
// src/routes/openai-completions.ts
|
|
54411
|
-
function createOpenaiCompletionsRoutes(routeType, store) {
|
|
54706
|
+
function createOpenaiCompletionsRoutes(routeType, store, pluginManager) {
|
|
54412
54707
|
const routes = new Hono2;
|
|
54413
54708
|
routes.post("/v1/chat/completions", createModelRoutingHandler({
|
|
54414
54709
|
routeType,
|
|
54415
54710
|
store,
|
|
54416
54711
|
authType: "bearer",
|
|
54417
|
-
buildTargetUrl: (base) => `${base}/v1/chat/completions
|
|
54712
|
+
buildTargetUrl: (base) => `${base}/v1/chat/completions`,
|
|
54713
|
+
pluginManager
|
|
54418
54714
|
}));
|
|
54419
54715
|
return routes;
|
|
54420
54716
|
}
|
|
54421
54717
|
|
|
54422
54718
|
// src/routes/openai-responses.ts
|
|
54423
|
-
function createOpenaiResponsesRoutes(routeType, store) {
|
|
54719
|
+
function createOpenaiResponsesRoutes(routeType, store, pluginManager) {
|
|
54424
54720
|
const routes = new Hono2;
|
|
54425
54721
|
routes.post("/v1/responses", createModelRoutingHandler({
|
|
54426
54722
|
routeType,
|
|
54427
54723
|
store,
|
|
54428
54724
|
authType: "bearer",
|
|
54429
|
-
buildTargetUrl: (base) => `${base}/v1/responses
|
|
54725
|
+
buildTargetUrl: (base) => `${base}/v1/responses`,
|
|
54726
|
+
pluginManager
|
|
54430
54727
|
}));
|
|
54431
54728
|
return routes;
|
|
54432
54729
|
}
|
|
@@ -54587,7 +54884,7 @@ function createChatProxyModel(providerName, providerConfig, model) {
|
|
|
54587
54884
|
throw new Error(`\u6682\u4E0D\u652F\u6301\u7684 provider \u7C7B\u578B: ${providerConfig.type}`);
|
|
54588
54885
|
}
|
|
54589
54886
|
}
|
|
54590
|
-
function createAdminApiRoutes(store, registerCleanup) {
|
|
54887
|
+
function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
54591
54888
|
const api2 = new Hono2;
|
|
54592
54889
|
const cryptoSessions = new Map;
|
|
54593
54890
|
const CRYPTO_SESSION_TTL_MS = 2 * 60 * 1000;
|
|
@@ -54698,18 +54995,22 @@ function createAdminApiRoutes(store, registerCleanup) {
|
|
|
54698
54995
|
session.dispose();
|
|
54699
54996
|
}
|
|
54700
54997
|
});
|
|
54701
|
-
api2.post("/config/apply", (_c) => {
|
|
54998
|
+
api2.post("/config/apply", async (_c) => {
|
|
54702
54999
|
try {
|
|
54703
55000
|
const config2 = store.reload();
|
|
54704
55001
|
if (config2.log) {
|
|
54705
55002
|
const logBaseDir = resolveLogBaseDir(config2.log);
|
|
54706
55003
|
initLogger(logBaseDir, config2.log);
|
|
54707
55004
|
}
|
|
55005
|
+
const pluginResult = await pluginManager.reloadAll(config2.providers);
|
|
54708
55006
|
return _c.json({
|
|
54709
55007
|
ok: true,
|
|
54710
55008
|
summary: {
|
|
54711
55009
|
providers: Object.keys(config2.providers).length,
|
|
54712
55010
|
routes: Object.keys(config2.routes).length
|
|
55011
|
+
},
|
|
55012
|
+
...pluginResult.failures.length > 0 && {
|
|
55013
|
+
pluginWarnings: pluginResult.failures
|
|
54713
55014
|
}
|
|
54714
55015
|
});
|
|
54715
55016
|
} catch (err) {
|
|
@@ -55134,7 +55435,7 @@ async function proxyAdminToDevServer(c2, origin) {
|
|
|
55134
55435
|
headers: buildProxyResponseHeaders(upstreamRes.headers)
|
|
55135
55436
|
});
|
|
55136
55437
|
}
|
|
55137
|
-
function createApp(store, options) {
|
|
55438
|
+
async function createApp(store, options) {
|
|
55138
55439
|
const config2 = store.get();
|
|
55139
55440
|
console.log(`\u5DF2\u52A0\u8F7D\u914D\u7F6E: ${store.getPath()}`);
|
|
55140
55441
|
if (config2.log) {
|
|
@@ -55145,15 +55446,24 @@ function createApp(store, options) {
|
|
|
55145
55446
|
}
|
|
55146
55447
|
const stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
|
|
55147
55448
|
options?.registerCleanup?.(stopLogStorageTask);
|
|
55449
|
+
const configDir = dirname3(resolve6(store.getPath()));
|
|
55450
|
+
const pluginManager = new PluginManager(configDir);
|
|
55451
|
+
const reloadResult = await pluginManager.reloadAll(config2.providers);
|
|
55452
|
+
if (!reloadResult.ok) {
|
|
55453
|
+
console.warn(`[plugin] \u63D2\u4EF6\u521D\u59CB\u5316\u5B8C\u6210\uFF0C\u4F46\u6709 ${reloadResult.failures.length} \u4E2A\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25`);
|
|
55454
|
+
}
|
|
55455
|
+
options?.registerCleanup?.(() => {
|
|
55456
|
+
pluginManager.disposeAll().catch(() => {});
|
|
55457
|
+
});
|
|
55148
55458
|
printIntegrationGuide(config2);
|
|
55149
55459
|
const app = new Hono2;
|
|
55150
55460
|
app.get("/", (c2) => c2.text("local-router is running"));
|
|
55151
55461
|
for (const [routeType, entry] of Object.entries(ROUTE_REGISTRY)) {
|
|
55152
|
-
const subApp = entry.create(routeType, store);
|
|
55462
|
+
const subApp = entry.create(routeType, store, pluginManager);
|
|
55153
55463
|
app.route(entry.mountPrefix, subApp);
|
|
55154
55464
|
console.log(`\u5DF2\u6CE8\u518C\u8DEF\u7531: ${routeType} -> ${entry.mountPrefix}`);
|
|
55155
55465
|
}
|
|
55156
|
-
app.route("/api", createAdminApiRoutes(store, options?.registerCleanup));
|
|
55466
|
+
app.route("/api", createAdminApiRoutes(store, pluginManager, options?.registerCleanup));
|
|
55157
55467
|
console.log("\u5DF2\u6CE8\u518C\u7BA1\u7406 API: /api");
|
|
55158
55468
|
app.get("/api/docs", middleware({ url: "/api/openapi.json" }));
|
|
55159
55469
|
app.get("/api/openapi.json", (c2) => c2.json(openAPISpec));
|
|
@@ -55182,10 +55492,10 @@ function createApp(store, options) {
|
|
|
55182
55492
|
}
|
|
55183
55493
|
return app;
|
|
55184
55494
|
}
|
|
55185
|
-
function createAppRuntimeFromConfigPath(configPath) {
|
|
55495
|
+
async function createAppRuntimeFromConfigPath(configPath) {
|
|
55186
55496
|
const store = new ConfigStore(configPath);
|
|
55187
55497
|
const cleanups = [];
|
|
55188
|
-
const app = createApp(store, {
|
|
55498
|
+
const app = await createApp(store, {
|
|
55189
55499
|
registerCleanup: (cleanup) => {
|
|
55190
55500
|
cleanups.push(cleanup);
|
|
55191
55501
|
}
|
|
@@ -55203,12 +55513,29 @@ function createAppRuntimeFromConfigPath(configPath) {
|
|
|
55203
55513
|
}
|
|
55204
55514
|
|
|
55205
55515
|
// src/server.ts
|
|
55206
|
-
|
|
55207
|
-
|
|
55516
|
+
var DEFAULT_IDLE_TIMEOUT_SECONDS = 600;
|
|
55517
|
+
function resolveIdleTimeoutSeconds(explicit) {
|
|
55518
|
+
if (typeof explicit === "number" && Number.isFinite(explicit) && explicit >= 0) {
|
|
55519
|
+
return explicit;
|
|
55520
|
+
}
|
|
55521
|
+
const fromEnv = process.env.LOCAL_ROUTER_IDLE_TIMEOUT;
|
|
55522
|
+
if (!fromEnv) {
|
|
55523
|
+
return DEFAULT_IDLE_TIMEOUT_SECONDS;
|
|
55524
|
+
}
|
|
55525
|
+
const parsed = Number.parseInt(fromEnv, 10);
|
|
55526
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
55527
|
+
return parsed;
|
|
55528
|
+
}
|
|
55529
|
+
return DEFAULT_IDLE_TIMEOUT_SECONDS;
|
|
55530
|
+
}
|
|
55531
|
+
async function startServer(options) {
|
|
55532
|
+
const runtime = await createAppRuntimeFromConfigPath(options.configPath);
|
|
55533
|
+
const idleTimeout = resolveIdleTimeoutSeconds(options.idleTimeoutSeconds);
|
|
55208
55534
|
const server = Bun.serve({
|
|
55209
55535
|
fetch: runtime.app.fetch,
|
|
55210
55536
|
hostname: options.host,
|
|
55211
|
-
port: options.port
|
|
55537
|
+
port: options.port,
|
|
55538
|
+
idleTimeout
|
|
55212
55539
|
});
|
|
55213
55540
|
const host = server.hostname;
|
|
55214
55541
|
const port = server.port;
|
|
@@ -55227,7 +55554,7 @@ function startServer(options) {
|
|
|
55227
55554
|
// src/cli/runtime.ts
|
|
55228
55555
|
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
55229
55556
|
import { homedir as homedir2 } from "os";
|
|
55230
|
-
import { join as join9, resolve as
|
|
55557
|
+
import { join as join9, resolve as resolve7 } from "path";
|
|
55231
55558
|
function getRuntimeDirs() {
|
|
55232
55559
|
const root2 = join9(homedir2(), ".local-router");
|
|
55233
55560
|
return {
|
|
@@ -55274,7 +55601,7 @@ function clearRuntimeFiles() {
|
|
|
55274
55601
|
rmSync(files.state, { force: true });
|
|
55275
55602
|
}
|
|
55276
55603
|
function resolveConfigArgPath(pathValue) {
|
|
55277
|
-
return
|
|
55604
|
+
return resolve7(pathValue);
|
|
55278
55605
|
}
|
|
55279
55606
|
|
|
55280
55607
|
// src/cli/process.ts
|
|
@@ -55284,7 +55611,8 @@ function parseSharedFlags(args) {
|
|
|
55284
55611
|
options: {
|
|
55285
55612
|
config: { type: "string" },
|
|
55286
55613
|
host: { type: "string" },
|
|
55287
|
-
port: { type: "string" }
|
|
55614
|
+
port: { type: "string" },
|
|
55615
|
+
"idle-timeout": { type: "string" }
|
|
55288
55616
|
},
|
|
55289
55617
|
allowPositionals: true,
|
|
55290
55618
|
strict: false
|
|
@@ -55294,10 +55622,16 @@ function parseSharedFlags(args) {
|
|
|
55294
55622
|
if (portRaw && !Number.isFinite(port)) {
|
|
55295
55623
|
throw new Error(`\u65E0\u6548\u7AEF\u53E3: ${portRaw}`);
|
|
55296
55624
|
}
|
|
55625
|
+
const idleTimeoutRaw = parsed.values["idle-timeout"];
|
|
55626
|
+
const idleTimeoutSeconds = idleTimeoutRaw ? Number.parseInt(idleTimeoutRaw, 10) : undefined;
|
|
55627
|
+
if (idleTimeoutRaw && (!Number.isFinite(idleTimeoutSeconds) || idleTimeoutSeconds < 0)) {
|
|
55628
|
+
throw new Error(`\u65E0\u6548 idle-timeout: ${idleTimeoutRaw}`);
|
|
55629
|
+
}
|
|
55297
55630
|
return {
|
|
55298
55631
|
config: parsed.values.config,
|
|
55299
55632
|
host: parsed.values.host,
|
|
55300
|
-
port
|
|
55633
|
+
port,
|
|
55634
|
+
idleTimeoutSeconds
|
|
55301
55635
|
};
|
|
55302
55636
|
}
|
|
55303
55637
|
async function checkHealth(baseUrl, timeoutMs = 1500) {
|
|
@@ -55343,12 +55677,14 @@ async function runServerProcess(opts) {
|
|
|
55343
55677
|
if (!Number.isFinite(port)) {
|
|
55344
55678
|
throw new Error(`\u65E0\u6548\u7AEF\u53E3: ${opts.port ?? process.env.PORT}`);
|
|
55345
55679
|
}
|
|
55680
|
+
const idleTimeoutSeconds = opts.idleTimeoutSeconds ?? Number.parseInt(process.env.LOCAL_ROUTER_IDLE_TIMEOUT ?? "", 10);
|
|
55346
55681
|
let running;
|
|
55347
55682
|
try {
|
|
55348
|
-
running = startServer({
|
|
55683
|
+
running = await startServer({
|
|
55349
55684
|
configPath: ensured.path,
|
|
55350
55685
|
host,
|
|
55351
|
-
port
|
|
55686
|
+
port,
|
|
55687
|
+
idleTimeoutSeconds: Number.isFinite(idleTimeoutSeconds) ? idleTimeoutSeconds : undefined
|
|
55352
55688
|
});
|
|
55353
55689
|
} catch (err) {
|
|
55354
55690
|
const code = err?.code;
|
|
@@ -55422,6 +55758,9 @@ async function startDaemon(flags) {
|
|
|
55422
55758
|
if (typeof flags.port === "number") {
|
|
55423
55759
|
childArgs.push("--port", String(flags.port));
|
|
55424
55760
|
}
|
|
55761
|
+
if (typeof flags.idleTimeoutSeconds === "number") {
|
|
55762
|
+
childArgs.push("--idle-timeout", String(flags.idleTimeoutSeconds));
|
|
55763
|
+
}
|
|
55425
55764
|
childArgs.push("--log-file", files.daemonLog);
|
|
55426
55765
|
const child = Bun.spawn([process.execPath, ...childArgs], {
|
|
55427
55766
|
stdin: "ignore",
|
|
@@ -55493,7 +55832,7 @@ function readLogDelta(filePath, offset) {
|
|
|
55493
55832
|
// src/cli/config-command.ts
|
|
55494
55833
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
55495
55834
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
55496
|
-
import { dirname as
|
|
55835
|
+
import { dirname as dirname4, join as join10 } from "path";
|
|
55497
55836
|
import { parseArgs as parseArgs2 } from "util";
|
|
55498
55837
|
function readConfig(configArg) {
|
|
55499
55838
|
const path = resolveConfigPath(configArg);
|
|
@@ -55501,7 +55840,7 @@ function readConfig(configArg) {
|
|
|
55501
55840
|
}
|
|
55502
55841
|
function saveConfig(path, config2) {
|
|
55503
55842
|
validateConfigOrThrow(config2);
|
|
55504
|
-
const backupDir = join10(
|
|
55843
|
+
const backupDir = join10(dirname4(path), ".backups");
|
|
55505
55844
|
mkdirSync4(backupDir, { recursive: true });
|
|
55506
55845
|
const backupPath = join10(backupDir, `config-${Date.now()}.json5`);
|
|
55507
55846
|
writeFileSync5(backupPath, readFileSync7(path, "utf-8"), "utf-8");
|
|
@@ -55947,9 +56286,9 @@ Usage:
|
|
|
55947
56286
|
local-router <command> [options]
|
|
55948
56287
|
|
|
55949
56288
|
Commands:
|
|
55950
|
-
start [--daemon] [--config <path>] [--host <host>] [--port <port>]
|
|
56289
|
+
start [--daemon] [--config <path>] [--host <host>] [--port <port>] [--idle-timeout <sec>]
|
|
55951
56290
|
stop
|
|
55952
|
-
restart [--daemon] [--config <path>] [--host <host>] [--port <port>]
|
|
56291
|
+
restart [--daemon] [--config <path>] [--host <host>] [--port <port>] [--idle-timeout <sec>]
|
|
55953
56292
|
status [--json]
|
|
55954
56293
|
logs [--follow] [--lines <n>]
|
|
55955
56294
|
init [--config <path>] [--force]
|
|
@@ -55959,7 +56298,7 @@ Commands:
|
|
|
55959
56298
|
version
|
|
55960
56299
|
|
|
55961
56300
|
Hidden commands:
|
|
55962
|
-
__run-server --mode <daemon|foreground> [--config] [--host] [--port] [--log-file]
|
|
56301
|
+
__run-server --mode <daemon|foreground> [--config] [--host] [--port] [--idle-timeout] [--log-file]
|
|
55963
56302
|
`);
|
|
55964
56303
|
}
|
|
55965
56304
|
async function printVersion() {
|
|
@@ -55989,7 +56328,8 @@ async function cmdStart(args) {
|
|
|
55989
56328
|
mode: "foreground",
|
|
55990
56329
|
config: flags.config,
|
|
55991
56330
|
host: flags.host,
|
|
55992
|
-
port: flags.port
|
|
56331
|
+
port: flags.port,
|
|
56332
|
+
idleTimeoutSeconds: flags.idleTimeoutSeconds
|
|
55993
56333
|
});
|
|
55994
56334
|
}
|
|
55995
56335
|
async function cmdStop() {
|
|
@@ -56188,6 +56528,7 @@ async function cmdRunServer(args) {
|
|
|
56188
56528
|
config: { type: "string" },
|
|
56189
56529
|
host: { type: "string" },
|
|
56190
56530
|
port: { type: "string" },
|
|
56531
|
+
"idle-timeout": { type: "string" },
|
|
56191
56532
|
"log-file": { type: "string" }
|
|
56192
56533
|
},
|
|
56193
56534
|
allowPositionals: true,
|
|
@@ -56196,11 +56537,14 @@ async function cmdRunServer(args) {
|
|
|
56196
56537
|
const mode = parsed.values.mode === "daemon" ? "daemon" : "foreground";
|
|
56197
56538
|
const portRaw = parsed.values.port;
|
|
56198
56539
|
const port = portRaw ? Number.parseInt(portRaw, 10) : undefined;
|
|
56540
|
+
const idleTimeoutRaw = parsed.values["idle-timeout"];
|
|
56541
|
+
const idleTimeoutSeconds = idleTimeoutRaw ? Number.parseInt(idleTimeoutRaw, 10) : undefined;
|
|
56199
56542
|
await runServerProcess({
|
|
56200
56543
|
mode,
|
|
56201
56544
|
config: parsed.values.config,
|
|
56202
56545
|
host: parsed.values.host,
|
|
56203
56546
|
port,
|
|
56547
|
+
idleTimeoutSeconds: Number.isFinite(idleTimeoutSeconds) ? idleTimeoutSeconds : undefined,
|
|
56204
56548
|
logFile: parsed.values["log-file"]
|
|
56205
56549
|
});
|
|
56206
56550
|
}
|