@lakphy/local-router 0.3.3 → 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";
@@ -52102,6 +52103,14 @@ async function buildLogEventDetail(id, parsed, location, context2) {
52102
52103
  const responseBodyAvailable = event.response_body !== undefined;
52103
52104
  const streamCaptured = Boolean(event.stream_file);
52104
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;
52105
52114
  return {
52106
52115
  id,
52107
52116
  summary: {
@@ -52153,6 +52162,7 @@ async function buildLogEventDetail(id, parsed, location, context2) {
52153
52162
  ...streamWarning ? [streamWarning] : []
52154
52163
  ]
52155
52164
  },
52165
+ ...pluginsSection && { plugins: pluginsSection },
52156
52166
  rawEvent: event,
52157
52167
  location
52158
52168
  };
@@ -54065,10 +54075,249 @@ var openAPISpec = {
54065
54075
  }
54066
54076
  };
54067
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
+
54068
54204
  // src/proxy.ts
54069
54205
  import { appendFile, readFile, unlink } from "fs/promises";
54070
54206
  import { join as join8 } from "path";
54071
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
54072
54321
  var HOP_BY_HOP_HEADERS = new Set([
54073
54322
  "connection",
54074
54323
  "keep-alive",
@@ -54169,33 +54418,63 @@ async function flushTempCaptureToLogger(tempPath, requestId, dateStr, logger) {
54169
54418
  }
54170
54419
  }
54171
54420
  async function proxyRequest(c2, options) {
54172
- const { logMeta } = options;
54421
+ const { logMeta, plugins, pluginConfigs } = options;
54173
54422
  const logger = getLogger();
54174
54423
  const shouldLog = logger?.enabled ?? false;
54175
- 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
+ }
54176
54454
  const requestBody = shouldLog && logger?.bodyPolicy !== "off" ? JSON.parse(options.body) : undefined;
54177
54455
  const proxy = options.proxy?.trim() ? options.proxy.trim() : undefined;
54178
54456
  let upstreamRes;
54179
54457
  try {
54180
- upstreamRes = await fetch(options.targetUrl, {
54458
+ upstreamRes = await fetch(targetUrl, {
54181
54459
  method: c2.req.method,
54182
54460
  headers,
54183
- body: options.body,
54461
+ body: bodyStr,
54184
54462
  ...proxy ? { proxy } : {},
54185
54463
  decompress: true
54186
54464
  });
54187
54465
  } catch (err) {
54188
54466
  if (shouldLog) {
54189
- logger?.writeEvent(buildLogEvent(logMeta, options.targetUrl, proxy, Date.now(), {
54467
+ logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), {
54190
54468
  error_type: err instanceof Error ? err.constructor.name : "UnknownError",
54191
54469
  error_message: err instanceof Error ? err.message : String(err),
54192
- ...requestBody !== undefined && { request_body: requestBody }
54470
+ ...requestBody !== undefined && { request_body: requestBody },
54471
+ ...pluginLogOverrides
54193
54472
  }));
54194
54473
  }
54195
54474
  throw err;
54196
54475
  }
54197
54476
  const responseHeaders = buildResponseHeaders(upstreamRes.headers);
54198
- if (!shouldLog) {
54477
+ if (!shouldLog && !hasPlugins) {
54199
54478
  return new Response(upstreamRes.body, {
54200
54479
  status: upstreamRes.status,
54201
54480
  headers: responseHeaders
@@ -54205,6 +54484,33 @@ async function proxyRequest(c2, options) {
54205
54484
  const providerRequestId = extractProviderRequestId(upstreamRes.headers);
54206
54485
  const dateStr = new Date(logMeta.tsStart).toISOString().slice(0, 10);
54207
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
+ }
54208
54514
  const [clientStream, logStream] = upstreamRes.body.tee();
54209
54515
  (async () => {
54210
54516
  const tempPath = createTempStreamCapturePath(logMeta.requestId);
@@ -54226,30 +54532,61 @@ async function proxyRequest(c2, options) {
54226
54532
  });
54227
54533
  console.error("[logger] \u6D41\u5F0F\u65E5\u5FD7\u5904\u7406\u5931\u8D25:", err);
54228
54534
  } finally {
54229
- logger?.writeEvent(buildLogEvent(logMeta, options.targetUrl, proxy, Date.now(), {
54230
- upstream_status: upstreamRes.status,
54535
+ logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), {
54536
+ upstream_status: sseStatus,
54231
54537
  content_type_res: contentTypeRes,
54232
- response_headers: responseHeaders,
54538
+ response_headers: sseHeaders,
54233
54539
  stream_bytes: streamBytes,
54234
54540
  provider_request_id: providerRequestId,
54235
54541
  ...streamFile != null && { stream_file: streamFile },
54236
- ...requestBody !== undefined && { request_body: requestBody }
54542
+ ...requestBody !== undefined && { request_body: requestBody },
54543
+ ...pluginLogOverrides
54237
54544
  }));
54238
54545
  }
54239
54546
  })();
54240
- return new Response(clientStream, {
54241
- status: upstreamRes.status,
54242
- 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
54243
54580
  });
54244
54581
  }
54245
- const responseText = await upstreamRes.text();
54246
54582
  const responseBytes = Buffer.byteLength(responseText, "utf-8");
54247
54583
  const eventOverrides = {
54248
54584
  upstream_status: upstreamRes.status,
54249
54585
  content_type_res: contentTypeRes,
54250
- response_headers: responseHeaders,
54586
+ response_headers: finalResponseHeaders,
54251
54587
  response_bytes: responseBytes,
54252
- provider_request_id: providerRequestId
54588
+ provider_request_id: providerRequestId,
54589
+ ...pluginLogOverrides
54253
54590
  };
54254
54591
  if (requestBody !== undefined) {
54255
54592
  eventOverrides.request_body = requestBody;
@@ -54257,10 +54594,10 @@ async function proxyRequest(c2, options) {
54257
54594
  if (logger?.bodyPolicy !== "off") {
54258
54595
  eventOverrides.response_body = responseText;
54259
54596
  }
54260
- logger?.writeEvent(buildLogEvent(logMeta, options.targetUrl, proxy, Date.now(), eventOverrides));
54597
+ logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), eventOverrides));
54261
54598
  return new Response(responseText, {
54262
- status: upstreamRes.status,
54263
- headers: responseHeaders
54599
+ status: responseStatus,
54600
+ headers: finalResponseHeaders
54264
54601
  });
54265
54602
  }
54266
54603
 
@@ -54275,7 +54612,7 @@ function resolveRoute(modelMap, incomingModel) {
54275
54612
  return;
54276
54613
  }
54277
54614
  function createModelRoutingHandler(options) {
54278
- const { routeType, store, authType, buildTargetUrl } = options;
54615
+ const { routeType, store, authType, buildTargetUrl, pluginManager } = options;
54279
54616
  return async (c2) => {
54280
54617
  const config2 = store.get();
54281
54618
  const modelMap = config2.routes[routeType];
@@ -54317,49 +54654,60 @@ function createModelRoutingHandler(options) {
54317
54654
  requestBytes: Buffer.byteLength(body, "utf-8"),
54318
54655
  requestHeaders: collectHeaders(c2.req.raw.headers)
54319
54656
  };
54657
+ const plugins = pluginManager?.getPlugins(target.provider) ?? [];
54658
+ const pluginConfigs = pluginManager?.getLoadedPlugins(target.provider) ?? [];
54320
54659
  return proxyRequest(c2, {
54321
54660
  targetUrl,
54322
54661
  apiKey: provider.apiKey,
54323
54662
  proxy: provider.proxy,
54324
54663
  authType,
54325
54664
  body,
54326
- 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
54327
54672
  });
54328
54673
  };
54329
54674
  }
54330
54675
 
54331
54676
  // src/routes/anthropic-messages.ts
54332
- function createAnthropicMessagesRoutes(routeType, store) {
54677
+ function createAnthropicMessagesRoutes(routeType, store, pluginManager) {
54333
54678
  const routes = new Hono2;
54334
54679
  routes.post("/v1/messages", createModelRoutingHandler({
54335
54680
  routeType,
54336
54681
  store,
54337
54682
  authType: "x-api-key",
54338
- buildTargetUrl: (base) => `${base}/v1/messages`
54683
+ buildTargetUrl: (base) => `${base}/v1/messages`,
54684
+ pluginManager
54339
54685
  }));
54340
54686
  return routes;
54341
54687
  }
54342
54688
 
54343
54689
  // src/routes/openai-completions.ts
54344
- function createOpenaiCompletionsRoutes(routeType, store) {
54690
+ function createOpenaiCompletionsRoutes(routeType, store, pluginManager) {
54345
54691
  const routes = new Hono2;
54346
54692
  routes.post("/v1/chat/completions", createModelRoutingHandler({
54347
54693
  routeType,
54348
54694
  store,
54349
54695
  authType: "bearer",
54350
- buildTargetUrl: (base) => `${base}/v1/chat/completions`
54696
+ buildTargetUrl: (base) => `${base}/v1/chat/completions`,
54697
+ pluginManager
54351
54698
  }));
54352
54699
  return routes;
54353
54700
  }
54354
54701
 
54355
54702
  // src/routes/openai-responses.ts
54356
- function createOpenaiResponsesRoutes(routeType, store) {
54703
+ function createOpenaiResponsesRoutes(routeType, store, pluginManager) {
54357
54704
  const routes = new Hono2;
54358
54705
  routes.post("/v1/responses", createModelRoutingHandler({
54359
54706
  routeType,
54360
54707
  store,
54361
54708
  authType: "bearer",
54362
- buildTargetUrl: (base) => `${base}/v1/responses`
54709
+ buildTargetUrl: (base) => `${base}/v1/responses`,
54710
+ pluginManager
54363
54711
  }));
54364
54712
  return routes;
54365
54713
  }
@@ -54520,7 +54868,7 @@ function createChatProxyModel(providerName, providerConfig, model) {
54520
54868
  throw new Error(`\u6682\u4E0D\u652F\u6301\u7684 provider \u7C7B\u578B: ${providerConfig.type}`);
54521
54869
  }
54522
54870
  }
54523
- function createAdminApiRoutes(store, registerCleanup) {
54871
+ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
54524
54872
  const api2 = new Hono2;
54525
54873
  const cryptoSessions = new Map;
54526
54874
  const CRYPTO_SESSION_TTL_MS = 2 * 60 * 1000;
@@ -54631,18 +54979,22 @@ function createAdminApiRoutes(store, registerCleanup) {
54631
54979
  session.dispose();
54632
54980
  }
54633
54981
  });
54634
- api2.post("/config/apply", (_c) => {
54982
+ api2.post("/config/apply", async (_c) => {
54635
54983
  try {
54636
54984
  const config2 = store.reload();
54637
54985
  if (config2.log) {
54638
54986
  const logBaseDir = resolveLogBaseDir(config2.log);
54639
54987
  initLogger(logBaseDir, config2.log);
54640
54988
  }
54989
+ const pluginResult = await pluginManager.reloadAll(config2.providers);
54641
54990
  return _c.json({
54642
54991
  ok: true,
54643
54992
  summary: {
54644
54993
  providers: Object.keys(config2.providers).length,
54645
54994
  routes: Object.keys(config2.routes).length
54995
+ },
54996
+ ...pluginResult.failures.length > 0 && {
54997
+ pluginWarnings: pluginResult.failures
54646
54998
  }
54647
54999
  });
54648
55000
  } catch (err) {
@@ -55067,7 +55419,7 @@ async function proxyAdminToDevServer(c2, origin) {
55067
55419
  headers: buildProxyResponseHeaders(upstreamRes.headers)
55068
55420
  });
55069
55421
  }
55070
- function createApp(store, options) {
55422
+ async function createApp(store, options) {
55071
55423
  const config2 = store.get();
55072
55424
  console.log(`\u5DF2\u52A0\u8F7D\u914D\u7F6E: ${store.getPath()}`);
55073
55425
  if (config2.log) {
@@ -55078,15 +55430,24 @@ function createApp(store, options) {
55078
55430
  }
55079
55431
  const stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
55080
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
+ });
55081
55442
  printIntegrationGuide(config2);
55082
55443
  const app = new Hono2;
55083
55444
  app.get("/", (c2) => c2.text("local-router is running"));
55084
55445
  for (const [routeType, entry] of Object.entries(ROUTE_REGISTRY)) {
55085
- const subApp = entry.create(routeType, store);
55446
+ const subApp = entry.create(routeType, store, pluginManager);
55086
55447
  app.route(entry.mountPrefix, subApp);
55087
55448
  console.log(`\u5DF2\u6CE8\u518C\u8DEF\u7531: ${routeType} -> ${entry.mountPrefix}`);
55088
55449
  }
55089
- app.route("/api", createAdminApiRoutes(store, options?.registerCleanup));
55450
+ app.route("/api", createAdminApiRoutes(store, pluginManager, options?.registerCleanup));
55090
55451
  console.log("\u5DF2\u6CE8\u518C\u7BA1\u7406 API: /api");
55091
55452
  app.get("/api/docs", middleware({ url: "/api/openapi.json" }));
55092
55453
  app.get("/api/openapi.json", (c2) => c2.json(openAPISpec));
@@ -55115,14 +55476,14 @@ function createApp(store, options) {
55115
55476
  }
55116
55477
  return app;
55117
55478
  }
55118
- function createDefaultAppFromProcessArgs() {
55479
+ async function createDefaultAppFromProcessArgs() {
55119
55480
  const configPath = parseConfigPath();
55120
55481
  const store = new ConfigStore(configPath);
55121
55482
  return createApp(store);
55122
55483
  }
55123
55484
 
55124
55485
  // src/entry.ts
55125
- var app = createDefaultAppFromProcessArgs();
55486
+ var app = await createDefaultAppFromProcessArgs();
55126
55487
  var entry_default = app;
55127
55488
  export {
55128
55489
  entry_default as default