@lakphy/local-router 0.4.0-beta.1 → 0.4.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -269,15 +269,15 @@
269
269
  },
270
270
  "pluginConfig": {
271
271
  "type": "object",
272
- "description": "单个插件的配置。package 指定 npm 包名或本地路径,params 为传递给 create() 的参数对象。",
272
+ "description": "单个插件的配置。package 指定 npm 包名、本地路径或远程 URL,params 为传递给 create() 的参数对象。",
273
273
  "additionalProperties": false,
274
274
  "required": ["package"],
275
275
  "properties": {
276
276
  "package": {
277
277
  "type": "string",
278
278
  "minLength": 1,
279
- "description": "插件包名或本地路径。以 ./ 或 ../ 或 / 开头视为本地文件(相对于配置文件目录解析),否则作为 npm 包名通过 import() 加载。",
280
- "examples": ["local-router-plugin-audit", "./plugins/content-filter.ts"]
279
+ "description": "插件包名、本地路径或远程 URL。以 ./ 或 ../ 或 / 开头视为本地文件(相对于配置文件目录解析);以 http:// 或 https:// 开头视为远程 URL(运行时下载后加载);否则作为 npm 包名通过 import() 加载。支持 .js 和 .ts 文件。",
280
+ "examples": ["local-router-plugin-audit", "./plugins/content-filter.ts", "https://example.com/plugins/my-plugin.js"]
281
281
  },
282
282
  "params": {
283
283
  "type": "object",
package/dist/cli.js CHANGED
@@ -10518,7 +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
+ import { dirname as dirname2, resolve as resolve6 } from "path";
10522
10522
 
10523
10523
  // node_modules/.bun/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/dist/index.mjs
10524
10524
  var marker = "vercel.ai.error";
@@ -52119,12 +52119,13 @@ async function buildLogEventDetail(id, parsed, location, context2) {
52119
52119
  const responseBodyAvailable = event.response_body !== undefined;
52120
52120
  const streamCaptured = Boolean(event.stream_file);
52121
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;
52122
+ const hasPluginData = event.plugins_request || event.plugins_response || event.request_body_after_plugins !== undefined || event.request_url_after_plugins !== undefined || event.response_body_before_plugins !== undefined || event.response_body_after_plugins !== undefined;
52123
52123
  const pluginsSection = hasPluginData ? {
52124
52124
  request: event.plugins_request,
52125
52125
  response: event.plugins_response,
52126
52126
  requestBodyAfterPlugins: event.request_body_after_plugins,
52127
52127
  requestUrlAfterPlugins: event.request_url_after_plugins,
52128
+ responseBodyBeforePlugins: event.response_body_before_plugins,
52128
52129
  responseBodyAfterPlugins: event.response_body_after_plugins
52129
52130
  } : undefined;
52130
52131
  return {
@@ -54092,13 +54093,64 @@ var openAPISpec = {
54092
54093
  };
54093
54094
 
54094
54095
  // src/plugin-loader.ts
54095
- import { resolve as resolve5 } from "path";
54096
+ import { resolve as resolve5, join as join8 } from "path";
54097
+ import { tmpdir } from "os";
54098
+ import { mkdtemp, writeFile, rm } from "fs/promises";
54096
54099
  function isLocalPath(pkg) {
54097
54100
  return pkg.startsWith("./") || pkg.startsWith("../") || pkg.startsWith("/") || /^[A-Za-z]:[\\/]/.test(pkg);
54098
54101
  }
54102
+ function isRemoteUrl(pkg) {
54103
+ return pkg.startsWith("http://") || pkg.startsWith("https://");
54104
+ }
54105
+ function inferExtension(url2, contentType) {
54106
+ const pathname = new URL(url2).pathname;
54107
+ if (pathname.endsWith(".ts") || pathname.endsWith(".tsx"))
54108
+ return ".ts";
54109
+ if (pathname.endsWith(".mjs"))
54110
+ return ".mjs";
54111
+ if (pathname.endsWith(".cjs"))
54112
+ return ".cjs";
54113
+ if (contentType?.includes("typescript"))
54114
+ return ".ts";
54115
+ return ".js";
54116
+ }
54117
+ var remoteTmpDir = null;
54118
+ var remoteTmpFiles = [];
54119
+ async function ensureRemoteTmpDir() {
54120
+ if (!remoteTmpDir) {
54121
+ remoteTmpDir = await mkdtemp(join8(tmpdir(), "local-router-plugins-"));
54122
+ }
54123
+ return remoteTmpDir;
54124
+ }
54125
+ async function fetchRemotePlugin(url2) {
54126
+ const response = await fetch(url2);
54127
+ if (!response.ok) {
54128
+ throw new Error(`\u4E0B\u8F7D\u8FDC\u7A0B\u63D2\u4EF6\u5931\u8D25: HTTP ${response.status} ${response.statusText} (${url2})`);
54129
+ }
54130
+ const content = await response.text();
54131
+ const ext = inferExtension(url2, response.headers.get("content-type"));
54132
+ const dir = await ensureRemoteTmpDir();
54133
+ const fileName = `plugin_${Date.now()}_${Math.random().toString(36).slice(2, 8)}${ext}`;
54134
+ const filePath = join8(dir, fileName);
54135
+ await writeFile(filePath, content, "utf-8");
54136
+ remoteTmpFiles.push(filePath);
54137
+ return filePath;
54138
+ }
54139
+ async function cleanupRemoteTmpFiles() {
54140
+ if (remoteTmpDir) {
54141
+ try {
54142
+ await rm(remoteTmpDir, { recursive: true, force: true });
54143
+ } catch {}
54144
+ remoteTmpDir = null;
54145
+ remoteTmpFiles.length = 0;
54146
+ }
54147
+ }
54099
54148
  async function importPlugin(pkg, configDir) {
54100
54149
  let modulePath;
54101
- if (isLocalPath(pkg)) {
54150
+ if (isRemoteUrl(pkg)) {
54151
+ const localPath = await fetchRemotePlugin(pkg);
54152
+ modulePath = `${localPath}?t=${Date.now()}`;
54153
+ } else if (isLocalPath(pkg)) {
54102
54154
  const absolutePath = resolve5(configDir, pkg);
54103
54155
  modulePath = `${absolutePath}?t=${Date.now()}`;
54104
54156
  } else {
@@ -54198,6 +54250,7 @@ class PluginManager {
54198
54250
  }
54199
54251
  this.plugins.clear();
54200
54252
  await this.disposePluginList(allPlugins);
54253
+ await cleanupRemoteTmpFiles();
54201
54254
  }
54202
54255
  async disposePluginList(plugins) {
54203
54256
  for (const { instance: instance2, config: config2 } of plugins) {
@@ -54219,8 +54272,8 @@ class PluginManager {
54219
54272
 
54220
54273
  // src/proxy.ts
54221
54274
  import { appendFile, readFile, unlink } from "fs/promises";
54222
- import { join as join8 } from "path";
54223
- import { tmpdir } from "os";
54275
+ import { join as join9 } from "path";
54276
+ import { tmpdir as tmpdir2 } from "os";
54224
54277
 
54225
54278
  // src/plugin-engine.ts
54226
54279
  async function executeRequestPlugins(plugins, ctx, url2, headers, body) {
@@ -54412,7 +54465,7 @@ function buildLogEvent(logMeta, targetUrl, proxyUrl, tsEnd, overrides) {
54412
54465
  };
54413
54466
  }
54414
54467
  function createTempStreamCapturePath(requestId) {
54415
- return join8(tmpdir(), `local-router-stream-${requestId}-${Date.now()}.sse.raw`);
54468
+ return join9(tmpdir2(), `local-router-stream-${requestId}-${Date.now()}.sse.raw`);
54416
54469
  }
54417
54470
  async function appendTempStreamCapture(filePath, chunk) {
54418
54471
  await appendFile(filePath, chunk);
@@ -54583,6 +54636,9 @@ async function proxyRequest(c2, options) {
54583
54636
  pluginLogOverrides.plugins_response = pluginConfigs;
54584
54637
  }
54585
54638
  if (result.body !== responseText) {
54639
+ if (shouldLog && logger?.bodyPolicy !== "off") {
54640
+ pluginLogOverrides.response_body_before_plugins = responseText;
54641
+ }
54586
54642
  pluginLogOverrides.response_body_after_plugins = result.body;
54587
54643
  }
54588
54644
  responseStatus = result.status;
@@ -55446,7 +55502,7 @@ async function createApp(store, options) {
55446
55502
  }
55447
55503
  const stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
55448
55504
  options?.registerCleanup?.(stopLogStorageTask);
55449
- const configDir = dirname3(resolve6(store.getPath()));
55505
+ const configDir = dirname2(resolve6(store.getPath()));
55450
55506
  const pluginManager = new PluginManager(configDir);
55451
55507
  const reloadResult = await pluginManager.reloadAll(config2.providers);
55452
55508
  if (!reloadResult.ok) {
@@ -55513,7 +55569,7 @@ async function createAppRuntimeFromConfigPath(configPath) {
55513
55569
  }
55514
55570
 
55515
55571
  // src/server.ts
55516
- var DEFAULT_IDLE_TIMEOUT_SECONDS = 600;
55572
+ var DEFAULT_IDLE_TIMEOUT_SECONDS = 0;
55517
55573
  function resolveIdleTimeoutSeconds(explicit) {
55518
55574
  if (typeof explicit === "number" && Number.isFinite(explicit) && explicit >= 0) {
55519
55575
  return explicit;
@@ -55554,21 +55610,21 @@ async function startServer(options) {
55554
55610
  // src/cli/runtime.ts
55555
55611
  import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
55556
55612
  import { homedir as homedir2 } from "os";
55557
- import { join as join9, resolve as resolve7 } from "path";
55613
+ import { join as join10, resolve as resolve7 } from "path";
55558
55614
  function getRuntimeDirs() {
55559
- const root2 = join9(homedir2(), ".local-router");
55615
+ const root2 = join10(homedir2(), ".local-router");
55560
55616
  return {
55561
55617
  root: root2,
55562
- run: join9(root2, "run"),
55563
- logs: join9(root2, "logs")
55618
+ run: join10(root2, "run"),
55619
+ logs: join10(root2, "logs")
55564
55620
  };
55565
55621
  }
55566
55622
  function getRuntimeFiles() {
55567
55623
  const dirs = getRuntimeDirs();
55568
55624
  return {
55569
- pid: join9(dirs.run, "local-router.pid"),
55570
- state: join9(dirs.run, "status.json"),
55571
- daemonLog: join9(dirs.logs, "daemon.log")
55625
+ pid: join10(dirs.run, "local-router.pid"),
55626
+ state: join10(dirs.run, "status.json"),
55627
+ daemonLog: join10(dirs.logs, "daemon.log")
55572
55628
  };
55573
55629
  }
55574
55630
  function ensureRuntimeDirs() {
@@ -55832,7 +55888,7 @@ function readLogDelta(filePath, offset) {
55832
55888
  // src/cli/config-command.ts
55833
55889
  import { createInterface as createInterface4 } from "readline/promises";
55834
55890
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
55835
- import { dirname as dirname4, join as join10 } from "path";
55891
+ import { dirname as dirname3, join as join11 } from "path";
55836
55892
  import { parseArgs as parseArgs2 } from "util";
55837
55893
  function readConfig(configArg) {
55838
55894
  const path = resolveConfigPath(configArg);
@@ -55840,9 +55896,9 @@ function readConfig(configArg) {
55840
55896
  }
55841
55897
  function saveConfig(path, config2) {
55842
55898
  validateConfigOrThrow(config2);
55843
- const backupDir = join10(dirname4(path), ".backups");
55899
+ const backupDir = join11(dirname3(path), ".backups");
55844
55900
  mkdirSync4(backupDir, { recursive: true });
55845
- const backupPath = join10(backupDir, `config-${Date.now()}.json5`);
55901
+ const backupPath = join11(backupDir, `config-${Date.now()}.json5`);
55846
55902
  writeFileSync5(backupPath, readFileSync7(path, "utf-8"), "utf-8");
55847
55903
  const content = dist_default.stringify(config2, { space: 2, quote: '"' });
55848
55904
  writeFileSync5(path, content, "utf-8");
package/dist/entry.js CHANGED
@@ -9285,7 +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
+ import { dirname as dirname2, resolve as resolve6 } from "path";
9289
9289
 
9290
9290
  // node_modules/.bun/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/dist/index.mjs
9291
9291
  var marker = "vercel.ai.error";
@@ -52103,12 +52103,13 @@ async function buildLogEventDetail(id, parsed, location, context2) {
52103
52103
  const responseBodyAvailable = event.response_body !== undefined;
52104
52104
  const streamCaptured = Boolean(event.stream_file);
52105
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;
52106
+ const hasPluginData = event.plugins_request || event.plugins_response || event.request_body_after_plugins !== undefined || event.request_url_after_plugins !== undefined || event.response_body_before_plugins !== undefined || event.response_body_after_plugins !== undefined;
52107
52107
  const pluginsSection = hasPluginData ? {
52108
52108
  request: event.plugins_request,
52109
52109
  response: event.plugins_response,
52110
52110
  requestBodyAfterPlugins: event.request_body_after_plugins,
52111
52111
  requestUrlAfterPlugins: event.request_url_after_plugins,
52112
+ responseBodyBeforePlugins: event.response_body_before_plugins,
52112
52113
  responseBodyAfterPlugins: event.response_body_after_plugins
52113
52114
  } : undefined;
52114
52115
  return {
@@ -54076,13 +54077,64 @@ var openAPISpec = {
54076
54077
  };
54077
54078
 
54078
54079
  // src/plugin-loader.ts
54079
- import { resolve as resolve5 } from "path";
54080
+ import { resolve as resolve5, join as join8 } from "path";
54081
+ import { tmpdir } from "os";
54082
+ import { mkdtemp, writeFile, rm } from "fs/promises";
54080
54083
  function isLocalPath(pkg) {
54081
54084
  return pkg.startsWith("./") || pkg.startsWith("../") || pkg.startsWith("/") || /^[A-Za-z]:[\\/]/.test(pkg);
54082
54085
  }
54086
+ function isRemoteUrl(pkg) {
54087
+ return pkg.startsWith("http://") || pkg.startsWith("https://");
54088
+ }
54089
+ function inferExtension(url2, contentType) {
54090
+ const pathname = new URL(url2).pathname;
54091
+ if (pathname.endsWith(".ts") || pathname.endsWith(".tsx"))
54092
+ return ".ts";
54093
+ if (pathname.endsWith(".mjs"))
54094
+ return ".mjs";
54095
+ if (pathname.endsWith(".cjs"))
54096
+ return ".cjs";
54097
+ if (contentType?.includes("typescript"))
54098
+ return ".ts";
54099
+ return ".js";
54100
+ }
54101
+ var remoteTmpDir = null;
54102
+ var remoteTmpFiles = [];
54103
+ async function ensureRemoteTmpDir() {
54104
+ if (!remoteTmpDir) {
54105
+ remoteTmpDir = await mkdtemp(join8(tmpdir(), "local-router-plugins-"));
54106
+ }
54107
+ return remoteTmpDir;
54108
+ }
54109
+ async function fetchRemotePlugin(url2) {
54110
+ const response = await fetch(url2);
54111
+ if (!response.ok) {
54112
+ throw new Error(`\u4E0B\u8F7D\u8FDC\u7A0B\u63D2\u4EF6\u5931\u8D25: HTTP ${response.status} ${response.statusText} (${url2})`);
54113
+ }
54114
+ const content = await response.text();
54115
+ const ext = inferExtension(url2, response.headers.get("content-type"));
54116
+ const dir = await ensureRemoteTmpDir();
54117
+ const fileName = `plugin_${Date.now()}_${Math.random().toString(36).slice(2, 8)}${ext}`;
54118
+ const filePath = join8(dir, fileName);
54119
+ await writeFile(filePath, content, "utf-8");
54120
+ remoteTmpFiles.push(filePath);
54121
+ return filePath;
54122
+ }
54123
+ async function cleanupRemoteTmpFiles() {
54124
+ if (remoteTmpDir) {
54125
+ try {
54126
+ await rm(remoteTmpDir, { recursive: true, force: true });
54127
+ } catch {}
54128
+ remoteTmpDir = null;
54129
+ remoteTmpFiles.length = 0;
54130
+ }
54131
+ }
54083
54132
  async function importPlugin(pkg, configDir) {
54084
54133
  let modulePath;
54085
- if (isLocalPath(pkg)) {
54134
+ if (isRemoteUrl(pkg)) {
54135
+ const localPath = await fetchRemotePlugin(pkg);
54136
+ modulePath = `${localPath}?t=${Date.now()}`;
54137
+ } else if (isLocalPath(pkg)) {
54086
54138
  const absolutePath = resolve5(configDir, pkg);
54087
54139
  modulePath = `${absolutePath}?t=${Date.now()}`;
54088
54140
  } else {
@@ -54182,6 +54234,7 @@ class PluginManager {
54182
54234
  }
54183
54235
  this.plugins.clear();
54184
54236
  await this.disposePluginList(allPlugins);
54237
+ await cleanupRemoteTmpFiles();
54185
54238
  }
54186
54239
  async disposePluginList(plugins) {
54187
54240
  for (const { instance: instance2, config: config2 } of plugins) {
@@ -54203,8 +54256,8 @@ class PluginManager {
54203
54256
 
54204
54257
  // src/proxy.ts
54205
54258
  import { appendFile, readFile, unlink } from "fs/promises";
54206
- import { join as join8 } from "path";
54207
- import { tmpdir } from "os";
54259
+ import { join as join9 } from "path";
54260
+ import { tmpdir as tmpdir2 } from "os";
54208
54261
 
54209
54262
  // src/plugin-engine.ts
54210
54263
  async function executeRequestPlugins(plugins, ctx, url2, headers, body) {
@@ -54396,7 +54449,7 @@ function buildLogEvent(logMeta, targetUrl, proxyUrl, tsEnd, overrides) {
54396
54449
  };
54397
54450
  }
54398
54451
  function createTempStreamCapturePath(requestId) {
54399
- return join8(tmpdir(), `local-router-stream-${requestId}-${Date.now()}.sse.raw`);
54452
+ return join9(tmpdir2(), `local-router-stream-${requestId}-${Date.now()}.sse.raw`);
54400
54453
  }
54401
54454
  async function appendTempStreamCapture(filePath, chunk) {
54402
54455
  await appendFile(filePath, chunk);
@@ -54567,6 +54620,9 @@ async function proxyRequest(c2, options) {
54567
54620
  pluginLogOverrides.plugins_response = pluginConfigs;
54568
54621
  }
54569
54622
  if (result.body !== responseText) {
54623
+ if (shouldLog && logger?.bodyPolicy !== "off") {
54624
+ pluginLogOverrides.response_body_before_plugins = responseText;
54625
+ }
54570
54626
  pluginLogOverrides.response_body_after_plugins = result.body;
54571
54627
  }
54572
54628
  responseStatus = result.status;
@@ -55430,7 +55486,7 @@ async function createApp(store, options) {
55430
55486
  }
55431
55487
  const stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
55432
55488
  options?.registerCleanup?.(stopLogStorageTask);
55433
- const configDir = dirname3(resolve6(store.getPath()));
55489
+ const configDir = dirname2(resolve6(store.getPath()));
55434
55490
  const pluginManager = new PluginManager(configDir);
55435
55491
  const reloadResult = await pluginManager.reloadAll(config2.providers);
55436
55492
  if (!reloadResult.ok) {