@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.
- package/config.schema.json +3 -3
- package/dist/cli.js +70 -18
- package/dist/entry.js +59 -7
- package/package.json +1 -1
package/config.schema.json
CHANGED
|
@@ -269,15 +269,15 @@
|
|
|
269
269
|
},
|
|
270
270
|
"pluginConfig": {
|
|
271
271
|
"type": "object",
|
|
272
|
-
"description": "单个插件的配置。package 指定 npm
|
|
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": "
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
55609
|
+
import { join as join10, resolve as resolve7 } from "path";
|
|
55558
55610
|
function getRuntimeDirs() {
|
|
55559
|
-
const root2 =
|
|
55611
|
+
const root2 = join10(homedir2(), ".local-router");
|
|
55560
55612
|
return {
|
|
55561
55613
|
root: root2,
|
|
55562
|
-
run:
|
|
55563
|
-
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:
|
|
55570
|
-
state:
|
|
55571
|
-
daemonLog:
|
|
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
|
|
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 =
|
|
55895
|
+
const backupDir = join11(dirname3(path), ".backups");
|
|
55844
55896
|
mkdirSync4(backupDir, { recursive: true });
|
|
55845
|
-
const backupPath =
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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 =
|
|
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) {
|