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

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";
@@ -54092,13 +54092,64 @@ var openAPISpec = {
54092
54092
  };
54093
54093
 
54094
54094
  // src/plugin-loader.ts
54095
- import { resolve as resolve5 } from "path";
54095
+ import { resolve as resolve5, join as join8 } from "path";
54096
+ import { tmpdir } from "os";
54097
+ import { mkdtemp, writeFile, rm } from "fs/promises";
54096
54098
  function isLocalPath(pkg) {
54097
54099
  return pkg.startsWith("./") || pkg.startsWith("../") || pkg.startsWith("/") || /^[A-Za-z]:[\\/]/.test(pkg);
54098
54100
  }
54101
+ function isRemoteUrl(pkg) {
54102
+ return pkg.startsWith("http://") || pkg.startsWith("https://");
54103
+ }
54104
+ function inferExtension(url2, contentType) {
54105
+ const pathname = new URL(url2).pathname;
54106
+ if (pathname.endsWith(".ts") || pathname.endsWith(".tsx"))
54107
+ return ".ts";
54108
+ if (pathname.endsWith(".mjs"))
54109
+ return ".mjs";
54110
+ if (pathname.endsWith(".cjs"))
54111
+ return ".cjs";
54112
+ if (contentType?.includes("typescript"))
54113
+ return ".ts";
54114
+ return ".js";
54115
+ }
54116
+ var remoteTmpDir = null;
54117
+ var remoteTmpFiles = [];
54118
+ async function ensureRemoteTmpDir() {
54119
+ if (!remoteTmpDir) {
54120
+ remoteTmpDir = await mkdtemp(join8(tmpdir(), "local-router-plugins-"));
54121
+ }
54122
+ return remoteTmpDir;
54123
+ }
54124
+ async function fetchRemotePlugin(url2) {
54125
+ const response = await fetch(url2);
54126
+ if (!response.ok) {
54127
+ throw new Error(`\u4E0B\u8F7D\u8FDC\u7A0B\u63D2\u4EF6\u5931\u8D25: HTTP ${response.status} ${response.statusText} (${url2})`);
54128
+ }
54129
+ const content = await response.text();
54130
+ const ext = inferExtension(url2, response.headers.get("content-type"));
54131
+ const dir = await ensureRemoteTmpDir();
54132
+ const fileName = `plugin_${Date.now()}_${Math.random().toString(36).slice(2, 8)}${ext}`;
54133
+ const filePath = join8(dir, fileName);
54134
+ await writeFile(filePath, content, "utf-8");
54135
+ remoteTmpFiles.push(filePath);
54136
+ return filePath;
54137
+ }
54138
+ async function cleanupRemoteTmpFiles() {
54139
+ if (remoteTmpDir) {
54140
+ try {
54141
+ await rm(remoteTmpDir, { recursive: true, force: true });
54142
+ } catch {}
54143
+ remoteTmpDir = null;
54144
+ remoteTmpFiles.length = 0;
54145
+ }
54146
+ }
54099
54147
  async function importPlugin(pkg, configDir) {
54100
54148
  let modulePath;
54101
- if (isLocalPath(pkg)) {
54149
+ if (isRemoteUrl(pkg)) {
54150
+ const localPath = await fetchRemotePlugin(pkg);
54151
+ modulePath = `${localPath}?t=${Date.now()}`;
54152
+ } else if (isLocalPath(pkg)) {
54102
54153
  const absolutePath = resolve5(configDir, pkg);
54103
54154
  modulePath = `${absolutePath}?t=${Date.now()}`;
54104
54155
  } else {
@@ -54198,6 +54249,7 @@ class PluginManager {
54198
54249
  }
54199
54250
  this.plugins.clear();
54200
54251
  await this.disposePluginList(allPlugins);
54252
+ await cleanupRemoteTmpFiles();
54201
54253
  }
54202
54254
  async disposePluginList(plugins) {
54203
54255
  for (const { instance: instance2, config: config2 } of plugins) {
@@ -54219,8 +54271,8 @@ class PluginManager {
54219
54271
 
54220
54272
  // src/proxy.ts
54221
54273
  import { appendFile, readFile, unlink } from "fs/promises";
54222
- import { join as join8 } from "path";
54223
- import { tmpdir } from "os";
54274
+ import { join as join9 } from "path";
54275
+ import { tmpdir as tmpdir2 } from "os";
54224
54276
 
54225
54277
  // src/plugin-engine.ts
54226
54278
  async function executeRequestPlugins(plugins, ctx, url2, headers, body) {
@@ -54412,7 +54464,7 @@ function buildLogEvent(logMeta, targetUrl, proxyUrl, tsEnd, overrides) {
54412
54464
  };
54413
54465
  }
54414
54466
  function createTempStreamCapturePath(requestId) {
54415
- return join8(tmpdir(), `local-router-stream-${requestId}-${Date.now()}.sse.raw`);
54467
+ return join9(tmpdir2(), `local-router-stream-${requestId}-${Date.now()}.sse.raw`);
54416
54468
  }
54417
54469
  async function appendTempStreamCapture(filePath, chunk) {
54418
54470
  await appendFile(filePath, chunk);
@@ -55446,7 +55498,7 @@ async function createApp(store, options) {
55446
55498
  }
55447
55499
  const stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
55448
55500
  options?.registerCleanup?.(stopLogStorageTask);
55449
- const configDir = dirname3(resolve6(store.getPath()));
55501
+ const configDir = dirname2(resolve6(store.getPath()));
55450
55502
  const pluginManager = new PluginManager(configDir);
55451
55503
  const reloadResult = await pluginManager.reloadAll(config2.providers);
55452
55504
  if (!reloadResult.ok) {
@@ -55513,7 +55565,7 @@ async function createAppRuntimeFromConfigPath(configPath) {
55513
55565
  }
55514
55566
 
55515
55567
  // src/server.ts
55516
- var DEFAULT_IDLE_TIMEOUT_SECONDS = 600;
55568
+ var DEFAULT_IDLE_TIMEOUT_SECONDS = 0;
55517
55569
  function resolveIdleTimeoutSeconds(explicit) {
55518
55570
  if (typeof explicit === "number" && Number.isFinite(explicit) && explicit >= 0) {
55519
55571
  return explicit;
@@ -55554,21 +55606,21 @@ async function startServer(options) {
55554
55606
  // src/cli/runtime.ts
55555
55607
  import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync4 } from "fs";
55556
55608
  import { homedir as homedir2 } from "os";
55557
- import { join as join9, resolve as resolve7 } from "path";
55609
+ import { join as join10, resolve as resolve7 } from "path";
55558
55610
  function getRuntimeDirs() {
55559
- const root2 = join9(homedir2(), ".local-router");
55611
+ const root2 = join10(homedir2(), ".local-router");
55560
55612
  return {
55561
55613
  root: root2,
55562
- run: join9(root2, "run"),
55563
- logs: join9(root2, "logs")
55614
+ run: join10(root2, "run"),
55615
+ logs: join10(root2, "logs")
55564
55616
  };
55565
55617
  }
55566
55618
  function getRuntimeFiles() {
55567
55619
  const dirs = getRuntimeDirs();
55568
55620
  return {
55569
- pid: join9(dirs.run, "local-router.pid"),
55570
- state: join9(dirs.run, "status.json"),
55571
- daemonLog: join9(dirs.logs, "daemon.log")
55621
+ pid: join10(dirs.run, "local-router.pid"),
55622
+ state: join10(dirs.run, "status.json"),
55623
+ daemonLog: join10(dirs.logs, "daemon.log")
55572
55624
  };
55573
55625
  }
55574
55626
  function ensureRuntimeDirs() {
@@ -55832,7 +55884,7 @@ function readLogDelta(filePath, offset) {
55832
55884
  // src/cli/config-command.ts
55833
55885
  import { createInterface as createInterface4 } from "readline/promises";
55834
55886
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
55835
- import { dirname as dirname4, join as join10 } from "path";
55887
+ import { dirname as dirname3, join as join11 } from "path";
55836
55888
  import { parseArgs as parseArgs2 } from "util";
55837
55889
  function readConfig(configArg) {
55838
55890
  const path = resolveConfigPath(configArg);
@@ -55840,9 +55892,9 @@ function readConfig(configArg) {
55840
55892
  }
55841
55893
  function saveConfig(path, config2) {
55842
55894
  validateConfigOrThrow(config2);
55843
- const backupDir = join10(dirname4(path), ".backups");
55895
+ const backupDir = join11(dirname3(path), ".backups");
55844
55896
  mkdirSync4(backupDir, { recursive: true });
55845
- const backupPath = join10(backupDir, `config-${Date.now()}.json5`);
55897
+ const backupPath = join11(backupDir, `config-${Date.now()}.json5`);
55846
55898
  writeFileSync5(backupPath, readFileSync7(path, "utf-8"), "utf-8");
55847
55899
  const content = dist_default.stringify(config2, { space: 2, quote: '"' });
55848
55900
  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";
@@ -54076,13 +54076,64 @@ var openAPISpec = {
54076
54076
  };
54077
54077
 
54078
54078
  // src/plugin-loader.ts
54079
- import { resolve as resolve5 } from "path";
54079
+ import { resolve as resolve5, join as join8 } from "path";
54080
+ import { tmpdir } from "os";
54081
+ import { mkdtemp, writeFile, rm } from "fs/promises";
54080
54082
  function isLocalPath(pkg) {
54081
54083
  return pkg.startsWith("./") || pkg.startsWith("../") || pkg.startsWith("/") || /^[A-Za-z]:[\\/]/.test(pkg);
54082
54084
  }
54085
+ function isRemoteUrl(pkg) {
54086
+ return pkg.startsWith("http://") || pkg.startsWith("https://");
54087
+ }
54088
+ function inferExtension(url2, contentType) {
54089
+ const pathname = new URL(url2).pathname;
54090
+ if (pathname.endsWith(".ts") || pathname.endsWith(".tsx"))
54091
+ return ".ts";
54092
+ if (pathname.endsWith(".mjs"))
54093
+ return ".mjs";
54094
+ if (pathname.endsWith(".cjs"))
54095
+ return ".cjs";
54096
+ if (contentType?.includes("typescript"))
54097
+ return ".ts";
54098
+ return ".js";
54099
+ }
54100
+ var remoteTmpDir = null;
54101
+ var remoteTmpFiles = [];
54102
+ async function ensureRemoteTmpDir() {
54103
+ if (!remoteTmpDir) {
54104
+ remoteTmpDir = await mkdtemp(join8(tmpdir(), "local-router-plugins-"));
54105
+ }
54106
+ return remoteTmpDir;
54107
+ }
54108
+ async function fetchRemotePlugin(url2) {
54109
+ const response = await fetch(url2);
54110
+ if (!response.ok) {
54111
+ throw new Error(`\u4E0B\u8F7D\u8FDC\u7A0B\u63D2\u4EF6\u5931\u8D25: HTTP ${response.status} ${response.statusText} (${url2})`);
54112
+ }
54113
+ const content = await response.text();
54114
+ const ext = inferExtension(url2, response.headers.get("content-type"));
54115
+ const dir = await ensureRemoteTmpDir();
54116
+ const fileName = `plugin_${Date.now()}_${Math.random().toString(36).slice(2, 8)}${ext}`;
54117
+ const filePath = join8(dir, fileName);
54118
+ await writeFile(filePath, content, "utf-8");
54119
+ remoteTmpFiles.push(filePath);
54120
+ return filePath;
54121
+ }
54122
+ async function cleanupRemoteTmpFiles() {
54123
+ if (remoteTmpDir) {
54124
+ try {
54125
+ await rm(remoteTmpDir, { recursive: true, force: true });
54126
+ } catch {}
54127
+ remoteTmpDir = null;
54128
+ remoteTmpFiles.length = 0;
54129
+ }
54130
+ }
54083
54131
  async function importPlugin(pkg, configDir) {
54084
54132
  let modulePath;
54085
- if (isLocalPath(pkg)) {
54133
+ if (isRemoteUrl(pkg)) {
54134
+ const localPath = await fetchRemotePlugin(pkg);
54135
+ modulePath = `${localPath}?t=${Date.now()}`;
54136
+ } else if (isLocalPath(pkg)) {
54086
54137
  const absolutePath = resolve5(configDir, pkg);
54087
54138
  modulePath = `${absolutePath}?t=${Date.now()}`;
54088
54139
  } else {
@@ -54182,6 +54233,7 @@ class PluginManager {
54182
54233
  }
54183
54234
  this.plugins.clear();
54184
54235
  await this.disposePluginList(allPlugins);
54236
+ await cleanupRemoteTmpFiles();
54185
54237
  }
54186
54238
  async disposePluginList(plugins) {
54187
54239
  for (const { instance: instance2, config: config2 } of plugins) {
@@ -54203,8 +54255,8 @@ class PluginManager {
54203
54255
 
54204
54256
  // src/proxy.ts
54205
54257
  import { appendFile, readFile, unlink } from "fs/promises";
54206
- import { join as join8 } from "path";
54207
- import { tmpdir } from "os";
54258
+ import { join as join9 } from "path";
54259
+ import { tmpdir as tmpdir2 } from "os";
54208
54260
 
54209
54261
  // src/plugin-engine.ts
54210
54262
  async function executeRequestPlugins(plugins, ctx, url2, headers, body) {
@@ -54396,7 +54448,7 @@ function buildLogEvent(logMeta, targetUrl, proxyUrl, tsEnd, overrides) {
54396
54448
  };
54397
54449
  }
54398
54450
  function createTempStreamCapturePath(requestId) {
54399
- return join8(tmpdir(), `local-router-stream-${requestId}-${Date.now()}.sse.raw`);
54451
+ return join9(tmpdir2(), `local-router-stream-${requestId}-${Date.now()}.sse.raw`);
54400
54452
  }
54401
54453
  async function appendTempStreamCapture(filePath, chunk) {
54402
54454
  await appendFile(filePath, chunk);
@@ -55430,7 +55482,7 @@ async function createApp(store, options) {
55430
55482
  }
55431
55483
  const stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
55432
55484
  options?.registerCleanup?.(stopLogStorageTask);
55433
- const configDir = dirname3(resolve6(store.getPath()));
55485
+ const configDir = dirname2(resolve6(store.getPath()));
55434
55486
  const pluginManager = new PluginManager(configDir);
55435
55487
  const reloadResult = await pluginManager.reloadAll(config2.providers);
55436
55488
  if (!reloadResult.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lakphy/local-router",
3
- "version": "0.4.0-beta.1",
3
+ "version": "0.4.0-beta.2",
4
4
  "packageManager": "bun@1.2.0",
5
5
  "workspaces": [
6
6
  "web"