@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/dist/entry.js CHANGED
@@ -4934,7 +4934,7 @@ var require_compile = __commonJS((exports) => {
4934
4934
  const schOrFunc = root2.refs[ref];
4935
4935
  if (schOrFunc)
4936
4936
  return schOrFunc;
4937
- let _sch = resolve5.call(this, root2, ref);
4937
+ let _sch = resolve6.call(this, root2, ref);
4938
4938
  if (_sch === undefined) {
4939
4939
  const schema = (_a21 = root2.localRefs) === null || _a21 === undefined ? undefined : _a21[ref];
4940
4940
  const { schemaId } = this.opts;
@@ -4961,7 +4961,7 @@ var require_compile = __commonJS((exports) => {
4961
4961
  function sameSchemaEnv(s1, s2) {
4962
4962
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
4963
4963
  }
4964
- function resolve5(root2, ref) {
4964
+ function resolve6(root2, ref) {
4965
4965
  let sch;
4966
4966
  while (typeof (sch = this.refs[ref]) == "string")
4967
4967
  ref = sch;
@@ -5491,7 +5491,7 @@ var require_fast_uri = __commonJS((exports, module) => {
5491
5491
  }
5492
5492
  return uri;
5493
5493
  }
5494
- function resolve5(baseURI, relativeURI, options) {
5494
+ function resolve6(baseURI, relativeURI, options) {
5495
5495
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
5496
5496
  const resolved = resolveComponent(parse7(baseURI, schemelessOptions), parse7(relativeURI, schemelessOptions), schemelessOptions, true);
5497
5497
  schemelessOptions.skipEscape = true;
@@ -5719,7 +5719,7 @@ var require_fast_uri = __commonJS((exports, module) => {
5719
5719
  var fastUri = {
5720
5720
  SCHEMES,
5721
5721
  normalize,
5722
- resolve: resolve5,
5722
+ resolve: resolve6,
5723
5723
  resolveComponent,
5724
5724
  equal,
5725
5725
  serialize,
@@ -9285,6 +9285,7 @@ var require_dist2 = __commonJS((exports, module) => {
9285
9285
 
9286
9286
  // src/index.ts
9287
9287
  import { readFileSync as readFileSync4 } from "fs";
9288
+ import { dirname as dirname3, resolve as resolve6 } from "path";
9288
9289
 
9289
9290
  // node_modules/.bun/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/dist/index.mjs
9290
9291
  var marker = "vercel.ai.error";
@@ -51279,7 +51280,7 @@ var DEFAULT_CONFIG = `{
51279
51280
  // \u65E5\u5FD7\u914D\u7F6E\uFF08\u53EF\u9009\uFF0C\u4E0D\u914D\u7F6E\u5219\u4E0D\u542F\u7528\u65E5\u5FD7\u8BB0\u5F55\uFF09
51280
51281
  // log: {
51281
51282
  // enabled: true,
51282
- // bodyPolicy: "off", // off | masked | full
51283
+ // bodyPolicy: "off", // off | full
51283
51284
  // streams: {
51284
51285
  // enabled: true,
51285
51286
  // maxBytesPerRequest: 10485760, // 10MB
@@ -51807,7 +51808,6 @@ var MAX_QUERY_LIMIT = 200;
51807
51808
  var DEFAULT_QUERY_LIMIT = 50;
51808
51809
  var MAX_EXPORT_ROWS = 5000;
51809
51810
  var MAX_Q_LENGTH = 200;
51810
- var SENSITIVE_FIELD_PATTERN = /(authorization|token|cookie|password|passphrase|secret|api[_-]?key)/i;
51811
51811
  function encodeBase64Url(value) {
51812
51812
  return Buffer.from(value, "utf-8").toString("base64url");
51813
51813
  }
@@ -52023,34 +52023,6 @@ function eventToSummary(item) {
52023
52023
  sessionId: identity.sessionId
52024
52024
  };
52025
52025
  }
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
52026
  function detectBodyPolicy(event) {
52055
52027
  const hasRequestBody = event.request_body !== undefined;
52056
52028
  const hasResponseBody = event.response_body !== undefined;
@@ -52123,53 +52095,61 @@ function readStreamContent(baseDir, streamFile) {
52123
52095
  }
52124
52096
  }
52125
52097
  async function buildLogEventDetail(id, parsed, location, context2) {
52126
- const maskedEvent = maskSensitiveDeep(parsed);
52127
- const level = getLevel(maskedEvent);
52128
- const statusClass = getStatusClass2(maskedEvent);
52129
- const bodyPolicy = detectBodyPolicy(maskedEvent);
52130
- const requestBodyAvailable = maskedEvent.request_body !== undefined;
52131
- const responseBodyAvailable = maskedEvent.response_body !== undefined;
52132
- const streamCaptured = Boolean(maskedEvent.stream_file);
52133
- const { content: streamContent, warning: streamWarning } = readStreamContent(resolveLogBaseDir(context2.logConfig), maskedEvent.stream_file);
52098
+ const event = parsed;
52099
+ const level = getLevel(event);
52100
+ const statusClass = getStatusClass2(event);
52101
+ const bodyPolicy = detectBodyPolicy(event);
52102
+ const requestBodyAvailable = event.request_body !== undefined;
52103
+ const responseBodyAvailable = event.response_body !== undefined;
52104
+ const streamCaptured = Boolean(event.stream_file);
52105
+ const { content: streamContent, warning: streamWarning } = readStreamContent(resolveLogBaseDir(context2.logConfig), event.stream_file);
52106
+ 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;
52107
+ const pluginsSection = hasPluginData ? {
52108
+ request: event.plugins_request,
52109
+ response: event.plugins_response,
52110
+ requestBodyAfterPlugins: event.request_body_after_plugins,
52111
+ requestUrlAfterPlugins: event.request_url_after_plugins,
52112
+ responseBodyAfterPlugins: event.response_body_after_plugins
52113
+ } : undefined;
52134
52114
  return {
52135
52115
  id,
52136
52116
  summary: {
52137
52117
  id,
52138
- ts: maskedEvent.ts_start,
52118
+ ts: event.ts_start,
52139
52119
  level,
52140
- provider: maskedEvent.provider,
52141
- routeType: maskedEvent.route_type,
52142
- routeRuleKey: maskedEvent.route_rule_key,
52143
- requestId: maskedEvent.request_id,
52144
- latencyMs: Math.max(0, maskedEvent.latency_ms ?? 0),
52145
- upstreamStatus: maskedEvent.upstream_status ?? 0,
52120
+ provider: event.provider,
52121
+ routeType: event.route_type,
52122
+ routeRuleKey: event.route_rule_key,
52123
+ requestId: event.request_id,
52124
+ latencyMs: Math.max(0, event.latency_ms ?? 0),
52125
+ upstreamStatus: event.upstream_status ?? 0,
52146
52126
  statusClass,
52147
52127
  hasError: level === "error",
52148
- model: maskedEvent.model_out || maskedEvent.model_in,
52149
- modelIn: maskedEvent.model_in,
52150
- modelOut: maskedEvent.model_out
52128
+ model: event.model_out || event.model_in,
52129
+ modelIn: event.model_in,
52130
+ modelOut: event.model_out
52151
52131
  },
52152
52132
  request: {
52153
- method: maskedEvent.method,
52154
- path: maskedEvent.path,
52155
- contentType: maskedEvent.content_type_req,
52156
- requestHeadersMasked: maskedEvent.request_headers_masked,
52157
- requestBody: maskedEvent.request_body ?? null
52133
+ method: event.method,
52134
+ path: event.path,
52135
+ contentType: event.content_type_req,
52136
+ requestHeaders: event.request_headers,
52137
+ requestBody: event.request_body ?? null
52158
52138
  },
52159
52139
  response: {
52160
- upstreamStatus: maskedEvent.upstream_status ?? 0,
52161
- contentType: maskedEvent.content_type_res,
52162
- responseHeaders: maskedEvent.response_headers,
52163
- responseBody: maskedEvent.response_body ?? null
52140
+ upstreamStatus: event.upstream_status ?? 0,
52141
+ contentType: event.content_type_res,
52142
+ responseHeaders: event.response_headers,
52143
+ responseBody: event.response_body ?? null
52164
52144
  },
52165
52145
  upstream: {
52166
- targetUrl: maskedEvent.target_url,
52167
- proxyUrl: maskedEvent.proxy_url ?? null,
52168
- providerRequestId: maskedEvent.provider_request_id,
52169
- errorType: maskedEvent.error_type,
52170
- errorMessage: maskedEvent.error_message,
52171
- isStream: maskedEvent.is_stream,
52172
- streamFile: maskedEvent.stream_file ?? null,
52146
+ targetUrl: event.target_url,
52147
+ proxyUrl: event.proxy_url ?? null,
52148
+ providerRequestId: event.provider_request_id,
52149
+ errorType: event.error_type,
52150
+ errorMessage: event.error_message,
52151
+ isStream: event.is_stream,
52152
+ streamFile: event.stream_file ?? null,
52173
52153
  streamContent
52174
52154
  },
52175
52155
  capture: {
@@ -52178,11 +52158,12 @@ async function buildLogEventDetail(id, parsed, location, context2) {
52178
52158
  responseBodyAvailable,
52179
52159
  streamCaptured,
52180
52160
  truncatedHints: [
52181
- ...buildTruncatedHints(maskedEvent, context2.logConfig?.bodyPolicy ?? bodyPolicy),
52161
+ ...buildTruncatedHints(event, context2.logConfig?.bodyPolicy ?? bodyPolicy),
52182
52162
  ...streamWarning ? [streamWarning] : []
52183
52163
  ]
52184
52164
  },
52185
- rawEvent: maskedEvent,
52165
+ ...pluginsSection && { plugins: pluginsSection },
52166
+ rawEvent: event,
52186
52167
  location
52187
52168
  };
52188
52169
  }
@@ -52894,13 +52875,6 @@ function startLogStorageBackgroundTask(logConfig) {
52894
52875
  // src/logger.ts
52895
52876
  import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
52896
52877
  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
52878
 
52905
52879
  class Logger {
52906
52880
  baseDir;
@@ -52981,14 +52955,10 @@ function getLogger() {
52981
52955
  function resetLogger() {
52982
52956
  instance = null;
52983
52957
  }
52984
- function maskHeaders(headers) {
52958
+ function collectHeaders(headers) {
52985
52959
  const result = {};
52986
52960
  headers.forEach((value, key2) => {
52987
- if (SENSITIVE_HEADERS.has(key2.toLowerCase())) {
52988
- result[key2] = value.length > 4 ? `${value.slice(0, 4)}****` : "****";
52989
- } else {
52990
- result[key2] = value;
52991
- }
52961
+ result[key2] = value;
52992
52962
  });
52993
52963
  return result;
52994
52964
  }
@@ -53000,19 +52970,8 @@ function extractProviderRequestId(headers) {
53000
52970
  }
53001
52971
  return null;
53002
52972
  }
53003
- function maskUrlCredentials(rawUrl) {
53004
- try {
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
- }
52973
+ function normalizeUrl(rawUrl) {
52974
+ return rawUrl;
53016
52975
  }
53017
52976
 
53018
52977
  // src/openapi.ts
@@ -53459,7 +53418,7 @@ var openAPISpec = {
53459
53418
  method: { type: "string" },
53460
53419
  path: { type: "string" },
53461
53420
  contentType: { type: ["string", "null"] },
53462
- requestHeadersMasked: {
53421
+ requestHeaders: {
53463
53422
  type: "object",
53464
53423
  additionalProperties: { type: "string" }
53465
53424
  },
@@ -54116,10 +54075,249 @@ var openAPISpec = {
54116
54075
  }
54117
54076
  };
54118
54077
 
54078
+ // src/plugin-loader.ts
54079
+ import { resolve as resolve5 } from "path";
54080
+ function isLocalPath(pkg) {
54081
+ return pkg.startsWith("./") || pkg.startsWith("../") || pkg.startsWith("/") || /^[A-Za-z]:[\\/]/.test(pkg);
54082
+ }
54083
+ async function importPlugin(pkg, configDir) {
54084
+ let modulePath;
54085
+ if (isLocalPath(pkg)) {
54086
+ const absolutePath = resolve5(configDir, pkg);
54087
+ modulePath = `${absolutePath}?t=${Date.now()}`;
54088
+ } else {
54089
+ modulePath = pkg;
54090
+ }
54091
+ const mod = await import(modulePath);
54092
+ const definition = mod.default ?? mod;
54093
+ if (!definition || typeof definition.name !== "string" || typeof definition.create !== "function") {
54094
+ throw new Error(`\u63D2\u4EF6 "${pkg}" \u5BFC\u51FA\u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u9700\u5BFC\u51FA\u5305\u542B name \u548C create \u7684 PluginDefinition`);
54095
+ }
54096
+ return definition;
54097
+ }
54098
+
54099
+ class PluginManager {
54100
+ plugins = new Map;
54101
+ configDir;
54102
+ constructor(configDir) {
54103
+ this.configDir = configDir;
54104
+ }
54105
+ async loadPluginsForProvider(providerName, pluginConfigs) {
54106
+ const loaded = [];
54107
+ const failures = [];
54108
+ for (const config2 of pluginConfigs) {
54109
+ try {
54110
+ const definition = await importPlugin(config2.package, this.configDir);
54111
+ const instance2 = await definition.create(config2.params ?? {});
54112
+ loaded.push({ config: config2, definition, instance: instance2 });
54113
+ } catch (err) {
54114
+ const errorMsg = err instanceof Error ? err.message : String(err);
54115
+ console.error(`[plugin] \u52A0\u8F7D\u63D2\u4EF6 "${config2.package}" \u5931\u8D25 (provider: ${providerName}):`, errorMsg);
54116
+ failures.push({ provider: providerName, package: config2.package, error: errorMsg });
54117
+ }
54118
+ }
54119
+ return { loaded, failures };
54120
+ }
54121
+ async reloadAll(providers) {
54122
+ const newPlugins = new Map;
54123
+ const allFailures = [];
54124
+ const oldPluginsToDispose = [];
54125
+ for (const [providerName, providerConfig] of Object.entries(providers)) {
54126
+ if (providerConfig.plugins && providerConfig.plugins.length > 0) {
54127
+ const { loaded, failures } = await this.loadPluginsForProvider(providerName, providerConfig.plugins);
54128
+ allFailures.push(...failures);
54129
+ if (failures.length > 0) {
54130
+ console.warn(`[plugin] provider "${providerName}" \u6709\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25\uFF0C\u4FDD\u7559\u65E7\u63D2\u4EF6\u94FE`);
54131
+ const oldLoaded = this.plugins.get(providerName);
54132
+ if (oldLoaded) {
54133
+ newPlugins.set(providerName, oldLoaded);
54134
+ }
54135
+ for (const { instance: instance2, config: config2 } of loaded) {
54136
+ try {
54137
+ await instance2.dispose?.();
54138
+ } catch (err) {
54139
+ console.error(`[plugin] \u56DE\u6EDA\u9500\u6BC1\u63D2\u4EF6 "${config2.package}" \u5931\u8D25:`, err instanceof Error ? err.message : err);
54140
+ }
54141
+ }
54142
+ } else {
54143
+ newPlugins.set(providerName, loaded);
54144
+ const oldLoaded = this.plugins.get(providerName);
54145
+ if (oldLoaded) {
54146
+ oldPluginsToDispose.push(...oldLoaded);
54147
+ }
54148
+ }
54149
+ }
54150
+ }
54151
+ for (const [providerName, oldLoaded] of this.plugins) {
54152
+ if (!newPlugins.has(providerName)) {
54153
+ oldPluginsToDispose.push(...oldLoaded);
54154
+ }
54155
+ }
54156
+ this.plugins = newPlugins;
54157
+ if (oldPluginsToDispose.length > 0) {
54158
+ setTimeout(() => {
54159
+ this.disposePluginList(oldPluginsToDispose).catch((err) => {
54160
+ console.error("[plugin] \u65E7\u63D2\u4EF6\u9500\u6BC1\u5931\u8D25:", err);
54161
+ });
54162
+ }, 5000);
54163
+ }
54164
+ if (allFailures.length > 0) {
54165
+ 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(", "));
54166
+ }
54167
+ return { ok: allFailures.length === 0, failures: allFailures };
54168
+ }
54169
+ getPlugins(providerName) {
54170
+ const loaded = this.plugins.get(providerName);
54171
+ if (!loaded)
54172
+ return [];
54173
+ return loaded.map((l) => l.instance);
54174
+ }
54175
+ getLoadedPlugins(providerName) {
54176
+ return this.plugins.get(providerName) ?? [];
54177
+ }
54178
+ async disposeAll() {
54179
+ const allPlugins = [];
54180
+ for (const [, loadedPlugins] of this.plugins) {
54181
+ allPlugins.push(...loadedPlugins);
54182
+ }
54183
+ this.plugins.clear();
54184
+ await this.disposePluginList(allPlugins);
54185
+ }
54186
+ async disposePluginList(plugins) {
54187
+ for (const { instance: instance2, config: config2 } of plugins) {
54188
+ try {
54189
+ await instance2.dispose?.();
54190
+ } catch (err) {
54191
+ console.error(`[plugin] \u9500\u6BC1\u63D2\u4EF6 "${config2.package}" \u5931\u8D25:`, err instanceof Error ? err.message : err);
54192
+ }
54193
+ }
54194
+ }
54195
+ async disposePluginMap(pluginMap) {
54196
+ const allPlugins = [];
54197
+ for (const [, loadedPlugins] of pluginMap) {
54198
+ allPlugins.push(...loadedPlugins);
54199
+ }
54200
+ await this.disposePluginList(allPlugins);
54201
+ }
54202
+ }
54203
+
54119
54204
  // src/proxy.ts
54120
54205
  import { appendFile, readFile, unlink } from "fs/promises";
54121
54206
  import { join as join8 } from "path";
54122
54207
  import { tmpdir } from "os";
54208
+
54209
+ // src/plugin-engine.ts
54210
+ async function executeRequestPlugins(plugins, ctx, url2, headers, body) {
54211
+ let currentUrl = url2;
54212
+ let currentHeaders = headers;
54213
+ let currentBody = body;
54214
+ for (const plugin of plugins) {
54215
+ if (!plugin.onRequest)
54216
+ continue;
54217
+ try {
54218
+ const result = await plugin.onRequest({
54219
+ ctx,
54220
+ url: currentUrl,
54221
+ headers: currentHeaders,
54222
+ body: currentBody
54223
+ });
54224
+ if (result) {
54225
+ if (result.url !== undefined)
54226
+ currentUrl = result.url;
54227
+ if (result.headers !== undefined)
54228
+ currentHeaders = result.headers;
54229
+ if (result.body !== undefined)
54230
+ currentBody = result.body;
54231
+ }
54232
+ } catch (err) {
54233
+ const error48 = err instanceof Error ? err : new Error(String(err));
54234
+ try {
54235
+ await plugin.onError?.({ ctx, phase: "request", error: error48 });
54236
+ } catch {}
54237
+ }
54238
+ }
54239
+ return { url: currentUrl, headers: currentHeaders, body: currentBody };
54240
+ }
54241
+ async function executeJsonResponsePlugins(plugins, ctx, status, headers, body) {
54242
+ let currentStatus = status;
54243
+ let currentHeaders = headers;
54244
+ let currentBody = body;
54245
+ for (let i = plugins.length - 1;i >= 0; i--) {
54246
+ const plugin = plugins[i];
54247
+ if (!plugin.onResponse)
54248
+ continue;
54249
+ try {
54250
+ const result = await plugin.onResponse({
54251
+ ctx,
54252
+ status: currentStatus,
54253
+ headers: currentHeaders,
54254
+ body: currentBody
54255
+ });
54256
+ if (result) {
54257
+ if (result.status !== undefined)
54258
+ currentStatus = result.status;
54259
+ if (result.headers !== undefined)
54260
+ currentHeaders = result.headers;
54261
+ if (result.body !== undefined)
54262
+ currentBody = result.body;
54263
+ }
54264
+ } catch (err) {
54265
+ const error48 = err instanceof Error ? err : new Error(String(err));
54266
+ try {
54267
+ await plugin.onError?.({ ctx, phase: "response", error: error48 });
54268
+ } catch {}
54269
+ }
54270
+ }
54271
+ return { status: currentStatus, headers: currentHeaders, body: currentBody };
54272
+ }
54273
+ async function createSSEPluginTransform(plugins, ctx, status, headers) {
54274
+ let currentStatus = status;
54275
+ let currentHeaders = headers;
54276
+ const transforms = [];
54277
+ for (let i = plugins.length - 1;i >= 0; i--) {
54278
+ const plugin = plugins[i];
54279
+ if (!plugin.onSSEResponse)
54280
+ continue;
54281
+ try {
54282
+ const result = await plugin.onSSEResponse({
54283
+ ctx,
54284
+ status: currentStatus,
54285
+ headers: currentHeaders
54286
+ });
54287
+ if (result) {
54288
+ if (result.status !== undefined)
54289
+ currentStatus = result.status;
54290
+ if (result.headers !== undefined)
54291
+ currentHeaders = result.headers;
54292
+ if (result.transform)
54293
+ transforms.push(result.transform);
54294
+ }
54295
+ } catch (err) {
54296
+ const error48 = err instanceof Error ? err : new Error(String(err));
54297
+ try {
54298
+ await plugin.onError?.({ ctx, phase: "response", error: error48 });
54299
+ } catch {}
54300
+ }
54301
+ }
54302
+ if (transforms.length === 0) {
54303
+ return { status: currentStatus, headers: currentHeaders, transform: null };
54304
+ }
54305
+ if (transforms.length === 1) {
54306
+ return { status: currentStatus, headers: currentHeaders, transform: transforms[0] };
54307
+ }
54308
+ const entry = new TransformStream;
54309
+ let stream = entry.readable;
54310
+ for (const t of transforms) {
54311
+ stream = stream.pipeThrough(t);
54312
+ }
54313
+ return {
54314
+ status: currentStatus,
54315
+ headers: currentHeaders,
54316
+ transform: { writable: entry.writable, readable: stream }
54317
+ };
54318
+ }
54319
+
54320
+ // src/proxy.ts
54123
54321
  var HOP_BY_HOP_HEADERS = new Set([
54124
54322
  "connection",
54125
54323
  "keep-alive",
@@ -54179,14 +54377,14 @@ function buildLogEvent(logMeta, targetUrl, proxyUrl, tsEnd, overrides) {
54179
54377
  provider: logMeta.provider,
54180
54378
  model_in: logMeta.modelIn,
54181
54379
  model_out: logMeta.modelOut,
54182
- target_url: maskUrlCredentials(targetUrl),
54183
- proxy_url: proxyUrl ? maskUrlCredentials(proxyUrl) : null,
54380
+ target_url: normalizeUrl(targetUrl),
54381
+ proxy_url: proxyUrl ? normalizeUrl(proxyUrl) : null,
54184
54382
  is_stream: logMeta.isStream,
54185
54383
  upstream_status: 0,
54186
54384
  content_type_req: logMeta.contentTypeReq,
54187
54385
  content_type_res: null,
54188
54386
  user_agent: logMeta.userAgent,
54189
- request_headers_masked: logMeta.requestHeadersMasked,
54387
+ request_headers: logMeta.requestHeaders,
54190
54388
  response_headers: {},
54191
54389
  request_bytes: logMeta.requestBytes,
54192
54390
  response_bytes: null,
@@ -54220,33 +54418,63 @@ async function flushTempCaptureToLogger(tempPath, requestId, dateStr, logger) {
54220
54418
  }
54221
54419
  }
54222
54420
  async function proxyRequest(c2, options) {
54223
- const { logMeta } = options;
54421
+ const { logMeta, plugins, pluginConfigs } = options;
54224
54422
  const logger = getLogger();
54225
54423
  const shouldLog = logger?.enabled ?? false;
54226
- const headers = buildUpstreamHeaders(c2.req.raw.headers, options.apiKey, options.authType);
54424
+ const hasPlugins = plugins && plugins.length > 0;
54425
+ let targetUrl = options.targetUrl;
54426
+ let headers = buildUpstreamHeaders(c2.req.raw.headers, options.apiKey, options.authType);
54427
+ let bodyStr = options.body;
54428
+ const pluginLogOverrides = {};
54429
+ if (hasPlugins) {
54430
+ const bodyObj = JSON.parse(bodyStr);
54431
+ const ctx = {
54432
+ requestId: logMeta.requestId,
54433
+ provider: logMeta.provider,
54434
+ modelIn: logMeta.modelIn,
54435
+ modelOut: logMeta.modelOut,
54436
+ routeType: logMeta.routeType,
54437
+ isStream: logMeta.isStream
54438
+ };
54439
+ const result = await executeRequestPlugins(plugins, ctx, targetUrl, headers, bodyObj);
54440
+ if (pluginConfigs) {
54441
+ pluginLogOverrides.plugins_request = pluginConfigs;
54442
+ }
54443
+ if (result.url !== targetUrl) {
54444
+ targetUrl = result.url;
54445
+ pluginLogOverrides.request_url_after_plugins = targetUrl;
54446
+ }
54447
+ headers = result.headers;
54448
+ const newBodyStr = JSON.stringify(result.body);
54449
+ if (newBodyStr !== bodyStr) {
54450
+ bodyStr = newBodyStr;
54451
+ pluginLogOverrides.request_body_after_plugins = result.body;
54452
+ }
54453
+ }
54227
54454
  const requestBody = shouldLog && logger?.bodyPolicy !== "off" ? JSON.parse(options.body) : undefined;
54228
54455
  const proxy = options.proxy?.trim() ? options.proxy.trim() : undefined;
54229
54456
  let upstreamRes;
54230
54457
  try {
54231
- upstreamRes = await fetch(options.targetUrl, {
54458
+ upstreamRes = await fetch(targetUrl, {
54232
54459
  method: c2.req.method,
54233
54460
  headers,
54234
- body: options.body,
54461
+ body: bodyStr,
54235
54462
  ...proxy ? { proxy } : {},
54236
54463
  decompress: true
54237
54464
  });
54238
54465
  } catch (err) {
54239
54466
  if (shouldLog) {
54240
- logger?.writeEvent(buildLogEvent(logMeta, options.targetUrl, proxy, Date.now(), {
54467
+ logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), {
54241
54468
  error_type: err instanceof Error ? err.constructor.name : "UnknownError",
54242
54469
  error_message: err instanceof Error ? err.message : String(err),
54243
- ...requestBody !== undefined && { request_body: requestBody }
54470
+ ...requestBody !== undefined && { request_body: requestBody },
54471
+ ...pluginLogOverrides
54244
54472
  }));
54245
54473
  }
54246
54474
  throw err;
54247
54475
  }
54248
54476
  const responseHeaders = buildResponseHeaders(upstreamRes.headers);
54249
- if (!shouldLog) {
54477
+ if (!shouldLog && !hasPlugins) {
54250
54478
  return new Response(upstreamRes.body, {
54251
54479
  status: upstreamRes.status,
54252
54480
  headers: responseHeaders
@@ -54256,6 +54484,33 @@ async function proxyRequest(c2, options) {
54256
54484
  const providerRequestId = extractProviderRequestId(upstreamRes.headers);
54257
54485
  const dateStr = new Date(logMeta.tsStart).toISOString().slice(0, 10);
54258
54486
  if (logMeta.isStream && upstreamRes.body) {
54487
+ let sseStatus = upstreamRes.status;
54488
+ let sseHeaders = responseHeaders;
54489
+ let sseTransform = null;
54490
+ if (hasPlugins) {
54491
+ const ctx = {
54492
+ requestId: logMeta.requestId,
54493
+ provider: logMeta.provider,
54494
+ modelIn: logMeta.modelIn,
54495
+ modelOut: logMeta.modelOut,
54496
+ routeType: logMeta.routeType,
54497
+ isStream: logMeta.isStream
54498
+ };
54499
+ const sseResult = await createSSEPluginTransform(plugins, ctx, upstreamRes.status, responseHeaders);
54500
+ sseStatus = sseResult.status;
54501
+ sseHeaders = sseResult.headers;
54502
+ sseTransform = sseResult.transform;
54503
+ if (pluginConfigs) {
54504
+ pluginLogOverrides.plugins_response = pluginConfigs;
54505
+ }
54506
+ }
54507
+ if (!shouldLog) {
54508
+ const outputBody2 = sseTransform ? upstreamRes.body.pipeThrough(sseTransform) : upstreamRes.body;
54509
+ return new Response(outputBody2, {
54510
+ status: sseStatus,
54511
+ headers: sseHeaders
54512
+ });
54513
+ }
54259
54514
  const [clientStream, logStream] = upstreamRes.body.tee();
54260
54515
  (async () => {
54261
54516
  const tempPath = createTempStreamCapturePath(logMeta.requestId);
@@ -54277,30 +54532,61 @@ async function proxyRequest(c2, options) {
54277
54532
  });
54278
54533
  console.error("[logger] \u6D41\u5F0F\u65E5\u5FD7\u5904\u7406\u5931\u8D25:", err);
54279
54534
  } finally {
54280
- logger?.writeEvent(buildLogEvent(logMeta, options.targetUrl, proxy, Date.now(), {
54281
- upstream_status: upstreamRes.status,
54535
+ logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), {
54536
+ upstream_status: sseStatus,
54282
54537
  content_type_res: contentTypeRes,
54283
- response_headers: responseHeaders,
54538
+ response_headers: sseHeaders,
54284
54539
  stream_bytes: streamBytes,
54285
54540
  provider_request_id: providerRequestId,
54286
54541
  ...streamFile != null && { stream_file: streamFile },
54287
- ...requestBody !== undefined && { request_body: requestBody }
54542
+ ...requestBody !== undefined && { request_body: requestBody },
54543
+ ...pluginLogOverrides
54288
54544
  }));
54289
54545
  }
54290
54546
  })();
54291
- return new Response(clientStream, {
54292
- status: upstreamRes.status,
54293
- headers: responseHeaders
54547
+ const outputBody = sseTransform ? clientStream.pipeThrough(sseTransform) : clientStream;
54548
+ return new Response(outputBody, {
54549
+ status: sseStatus,
54550
+ headers: sseHeaders
54551
+ });
54552
+ }
54553
+ let responseText = await upstreamRes.text();
54554
+ let responseStatus = upstreamRes.status;
54555
+ let finalResponseHeaders = responseHeaders;
54556
+ if (hasPlugins) {
54557
+ const ctx = {
54558
+ requestId: logMeta.requestId,
54559
+ provider: logMeta.provider,
54560
+ modelIn: logMeta.modelIn,
54561
+ modelOut: logMeta.modelOut,
54562
+ routeType: logMeta.routeType,
54563
+ isStream: logMeta.isStream
54564
+ };
54565
+ const result = await executeJsonResponsePlugins(plugins, ctx, upstreamRes.status, responseHeaders, responseText);
54566
+ if (pluginConfigs) {
54567
+ pluginLogOverrides.plugins_response = pluginConfigs;
54568
+ }
54569
+ if (result.body !== responseText) {
54570
+ pluginLogOverrides.response_body_after_plugins = result.body;
54571
+ }
54572
+ responseStatus = result.status;
54573
+ finalResponseHeaders = result.headers;
54574
+ responseText = result.body;
54575
+ }
54576
+ if (!shouldLog) {
54577
+ return new Response(responseText, {
54578
+ status: responseStatus,
54579
+ headers: finalResponseHeaders
54294
54580
  });
54295
54581
  }
54296
- const responseText = await upstreamRes.text();
54297
54582
  const responseBytes = Buffer.byteLength(responseText, "utf-8");
54298
54583
  const eventOverrides = {
54299
54584
  upstream_status: upstreamRes.status,
54300
54585
  content_type_res: contentTypeRes,
54301
- response_headers: responseHeaders,
54586
+ response_headers: finalResponseHeaders,
54302
54587
  response_bytes: responseBytes,
54303
- provider_request_id: providerRequestId
54588
+ provider_request_id: providerRequestId,
54589
+ ...pluginLogOverrides
54304
54590
  };
54305
54591
  if (requestBody !== undefined) {
54306
54592
  eventOverrides.request_body = requestBody;
@@ -54308,10 +54594,10 @@ async function proxyRequest(c2, options) {
54308
54594
  if (logger?.bodyPolicy !== "off") {
54309
54595
  eventOverrides.response_body = responseText;
54310
54596
  }
54311
- logger?.writeEvent(buildLogEvent(logMeta, options.targetUrl, proxy, Date.now(), eventOverrides));
54597
+ logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), eventOverrides));
54312
54598
  return new Response(responseText, {
54313
- status: upstreamRes.status,
54314
- headers: responseHeaders
54599
+ status: responseStatus,
54600
+ headers: finalResponseHeaders
54315
54601
  });
54316
54602
  }
54317
54603
 
@@ -54326,7 +54612,7 @@ function resolveRoute(modelMap, incomingModel) {
54326
54612
  return;
54327
54613
  }
54328
54614
  function createModelRoutingHandler(options) {
54329
- const { routeType, store, authType, buildTargetUrl } = options;
54615
+ const { routeType, store, authType, buildTargetUrl, pluginManager } = options;
54330
54616
  return async (c2) => {
54331
54617
  const config2 = store.get();
54332
54618
  const modelMap = config2.routes[routeType];
@@ -54366,51 +54652,62 @@ function createModelRoutingHandler(options) {
54366
54652
  contentTypeReq: c2.req.header("content-type") ?? null,
54367
54653
  userAgent: c2.req.header("user-agent") ?? null,
54368
54654
  requestBytes: Buffer.byteLength(body, "utf-8"),
54369
- requestHeadersMasked: maskHeaders(c2.req.raw.headers)
54655
+ requestHeaders: collectHeaders(c2.req.raw.headers)
54370
54656
  };
54657
+ const plugins = pluginManager?.getPlugins(target.provider) ?? [];
54658
+ const pluginConfigs = pluginManager?.getLoadedPlugins(target.provider) ?? [];
54371
54659
  return proxyRequest(c2, {
54372
54660
  targetUrl,
54373
54661
  apiKey: provider.apiKey,
54374
54662
  proxy: provider.proxy,
54375
54663
  authType,
54376
54664
  body,
54377
- logMeta
54665
+ logMeta,
54666
+ plugins: plugins.length > 0 ? plugins : undefined,
54667
+ pluginConfigs: pluginConfigs.length > 0 ? pluginConfigs.map((lp) => ({
54668
+ name: lp.definition.name,
54669
+ package: lp.config.package,
54670
+ params: lp.config.params ?? {}
54671
+ })) : undefined
54378
54672
  });
54379
54673
  };
54380
54674
  }
54381
54675
 
54382
54676
  // src/routes/anthropic-messages.ts
54383
- function createAnthropicMessagesRoutes(routeType, store) {
54677
+ function createAnthropicMessagesRoutes(routeType, store, pluginManager) {
54384
54678
  const routes = new Hono2;
54385
54679
  routes.post("/v1/messages", createModelRoutingHandler({
54386
54680
  routeType,
54387
54681
  store,
54388
54682
  authType: "x-api-key",
54389
- buildTargetUrl: (base) => `${base}/v1/messages`
54683
+ buildTargetUrl: (base) => `${base}/v1/messages`,
54684
+ pluginManager
54390
54685
  }));
54391
54686
  return routes;
54392
54687
  }
54393
54688
 
54394
54689
  // src/routes/openai-completions.ts
54395
- function createOpenaiCompletionsRoutes(routeType, store) {
54690
+ function createOpenaiCompletionsRoutes(routeType, store, pluginManager) {
54396
54691
  const routes = new Hono2;
54397
54692
  routes.post("/v1/chat/completions", createModelRoutingHandler({
54398
54693
  routeType,
54399
54694
  store,
54400
54695
  authType: "bearer",
54401
- buildTargetUrl: (base) => `${base}/v1/chat/completions`
54696
+ buildTargetUrl: (base) => `${base}/v1/chat/completions`,
54697
+ pluginManager
54402
54698
  }));
54403
54699
  return routes;
54404
54700
  }
54405
54701
 
54406
54702
  // src/routes/openai-responses.ts
54407
- function createOpenaiResponsesRoutes(routeType, store) {
54703
+ function createOpenaiResponsesRoutes(routeType, store, pluginManager) {
54408
54704
  const routes = new Hono2;
54409
54705
  routes.post("/v1/responses", createModelRoutingHandler({
54410
54706
  routeType,
54411
54707
  store,
54412
54708
  authType: "bearer",
54413
- buildTargetUrl: (base) => `${base}/v1/responses`
54709
+ buildTargetUrl: (base) => `${base}/v1/responses`,
54710
+ pluginManager
54414
54711
  }));
54415
54712
  return routes;
54416
54713
  }
@@ -54571,7 +54868,7 @@ function createChatProxyModel(providerName, providerConfig, model) {
54571
54868
  throw new Error(`\u6682\u4E0D\u652F\u6301\u7684 provider \u7C7B\u578B: ${providerConfig.type}`);
54572
54869
  }
54573
54870
  }
54574
- function createAdminApiRoutes(store, registerCleanup) {
54871
+ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
54575
54872
  const api2 = new Hono2;
54576
54873
  const cryptoSessions = new Map;
54577
54874
  const CRYPTO_SESSION_TTL_MS = 2 * 60 * 1000;
@@ -54682,18 +54979,22 @@ function createAdminApiRoutes(store, registerCleanup) {
54682
54979
  session.dispose();
54683
54980
  }
54684
54981
  });
54685
- api2.post("/config/apply", (_c) => {
54982
+ api2.post("/config/apply", async (_c) => {
54686
54983
  try {
54687
54984
  const config2 = store.reload();
54688
54985
  if (config2.log) {
54689
54986
  const logBaseDir = resolveLogBaseDir(config2.log);
54690
54987
  initLogger(logBaseDir, config2.log);
54691
54988
  }
54989
+ const pluginResult = await pluginManager.reloadAll(config2.providers);
54692
54990
  return _c.json({
54693
54991
  ok: true,
54694
54992
  summary: {
54695
54993
  providers: Object.keys(config2.providers).length,
54696
54994
  routes: Object.keys(config2.routes).length
54995
+ },
54996
+ ...pluginResult.failures.length > 0 && {
54997
+ pluginWarnings: pluginResult.failures
54697
54998
  }
54698
54999
  });
54699
55000
  } catch (err) {
@@ -55118,7 +55419,7 @@ async function proxyAdminToDevServer(c2, origin) {
55118
55419
  headers: buildProxyResponseHeaders(upstreamRes.headers)
55119
55420
  });
55120
55421
  }
55121
- function createApp(store, options) {
55422
+ async function createApp(store, options) {
55122
55423
  const config2 = store.get();
55123
55424
  console.log(`\u5DF2\u52A0\u8F7D\u914D\u7F6E: ${store.getPath()}`);
55124
55425
  if (config2.log) {
@@ -55129,15 +55430,24 @@ function createApp(store, options) {
55129
55430
  }
55130
55431
  const stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
55131
55432
  options?.registerCleanup?.(stopLogStorageTask);
55433
+ const configDir = dirname3(resolve6(store.getPath()));
55434
+ const pluginManager = new PluginManager(configDir);
55435
+ const reloadResult = await pluginManager.reloadAll(config2.providers);
55436
+ if (!reloadResult.ok) {
55437
+ console.warn(`[plugin] \u63D2\u4EF6\u521D\u59CB\u5316\u5B8C\u6210\uFF0C\u4F46\u6709 ${reloadResult.failures.length} \u4E2A\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25`);
55438
+ }
55439
+ options?.registerCleanup?.(() => {
55440
+ pluginManager.disposeAll().catch(() => {});
55441
+ });
55132
55442
  printIntegrationGuide(config2);
55133
55443
  const app = new Hono2;
55134
55444
  app.get("/", (c2) => c2.text("local-router is running"));
55135
55445
  for (const [routeType, entry] of Object.entries(ROUTE_REGISTRY)) {
55136
- const subApp = entry.create(routeType, store);
55446
+ const subApp = entry.create(routeType, store, pluginManager);
55137
55447
  app.route(entry.mountPrefix, subApp);
55138
55448
  console.log(`\u5DF2\u6CE8\u518C\u8DEF\u7531: ${routeType} -> ${entry.mountPrefix}`);
55139
55449
  }
55140
- app.route("/api", createAdminApiRoutes(store, options?.registerCleanup));
55450
+ app.route("/api", createAdminApiRoutes(store, pluginManager, options?.registerCleanup));
55141
55451
  console.log("\u5DF2\u6CE8\u518C\u7BA1\u7406 API: /api");
55142
55452
  app.get("/api/docs", middleware({ url: "/api/openapi.json" }));
55143
55453
  app.get("/api/openapi.json", (c2) => c2.json(openAPISpec));
@@ -55166,14 +55476,14 @@ function createApp(store, options) {
55166
55476
  }
55167
55477
  return app;
55168
55478
  }
55169
- function createDefaultAppFromProcessArgs() {
55479
+ async function createDefaultAppFromProcessArgs() {
55170
55480
  const configPath = parseConfigPath();
55171
55481
  const store = new ConfigStore(configPath);
55172
55482
  return createApp(store);
55173
55483
  }
55174
55484
 
55175
55485
  // src/entry.ts
55176
- var app = createDefaultAppFromProcessArgs();
55486
+ var app = await createDefaultAppFromProcessArgs();
55177
55487
  var entry_default = app;
55178
55488
  export {
55179
55489
  entry_default as default