@lakphy/local-router 0.5.3 → 0.5.5
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 +22 -0
- package/dist/cli.js +43829 -40355
- package/dist/entry.js +1102 -241
- package/dist/web/assets/index-BxjdLPIh.js +190 -0
- package/dist/web/assets/index-Dc2P7nS-.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-CQ4HNKVY.css +0 -2
- package/dist/web/assets/index-HmBORmZo.js +0 -190
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 =
|
|
4937
|
+
let _sch = resolve5.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
|
|
4964
|
+
function resolve5(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
|
|
5494
|
+
function resolve5(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:
|
|
5722
|
+
resolve: resolve5,
|
|
5723
5723
|
resolveComponent,
|
|
5724
5724
|
equal,
|
|
5725
5725
|
serialize,
|
|
@@ -9284,8 +9284,218 @@ var require_dist2 = __commonJS((exports, module) => {
|
|
|
9284
9284
|
});
|
|
9285
9285
|
|
|
9286
9286
|
// src/index.ts
|
|
9287
|
-
import { readFileSync as
|
|
9288
|
-
import { dirname as
|
|
9287
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
9288
|
+
import { dirname as dirname3, resolve as resolve8 } from "path";
|
|
9289
|
+
|
|
9290
|
+
// src/cli/autostart.ts
|
|
9291
|
+
import { execSync } from "child_process";
|
|
9292
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
9293
|
+
import { homedir as homedir2, platform } from "os";
|
|
9294
|
+
import { dirname, join as join2 } from "path";
|
|
9295
|
+
|
|
9296
|
+
// src/cli/runtime.ts
|
|
9297
|
+
import { homedir } from "os";
|
|
9298
|
+
import { join, resolve } from "path";
|
|
9299
|
+
function getRuntimeDirs() {
|
|
9300
|
+
const override = process.env.LOCAL_ROUTER_RUNTIME_DIR;
|
|
9301
|
+
const root = override?.trim() ? override.trim() : join(homedir(), ".local-router");
|
|
9302
|
+
return {
|
|
9303
|
+
root,
|
|
9304
|
+
run: join(root, "run"),
|
|
9305
|
+
logs: join(root, "logs")
|
|
9306
|
+
};
|
|
9307
|
+
}
|
|
9308
|
+
|
|
9309
|
+
// src/cli/autostart.ts
|
|
9310
|
+
var LABEL = "com.lakphy.local-router";
|
|
9311
|
+
function getDaemonLogPath() {
|
|
9312
|
+
return getRuntimeDirs().logs + "/daemon.log";
|
|
9313
|
+
}
|
|
9314
|
+
function getLaunchAgentPath() {
|
|
9315
|
+
return join2(homedir2(), "Library", "LaunchAgents", `${LABEL}.plist`);
|
|
9316
|
+
}
|
|
9317
|
+
function buildPlist(opts) {
|
|
9318
|
+
const logPath = getDaemonLogPath();
|
|
9319
|
+
const args = [opts.execPath, ...opts.args].map((a) => ` <string>${escapeXml(a)}</string>`).join(`
|
|
9320
|
+
`);
|
|
9321
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
9322
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
9323
|
+
<plist version="1.0">
|
|
9324
|
+
<dict>
|
|
9325
|
+
<key>Label</key>
|
|
9326
|
+
<string>${escapeXml(opts.label)}</string>
|
|
9327
|
+
<key>ProgramArguments</key>
|
|
9328
|
+
<array>
|
|
9329
|
+
${args}
|
|
9330
|
+
</array>
|
|
9331
|
+
<key>RunAtLoad</key>
|
|
9332
|
+
<true/>
|
|
9333
|
+
<key>KeepAlive</key>
|
|
9334
|
+
<false/>
|
|
9335
|
+
<key>StandardOutPath</key>
|
|
9336
|
+
<string>${escapeXml(logPath)}</string>
|
|
9337
|
+
<key>StandardErrorPath</key>
|
|
9338
|
+
<string>${escapeXml(logPath)}</string>
|
|
9339
|
+
</dict>
|
|
9340
|
+
</plist>
|
|
9341
|
+
`;
|
|
9342
|
+
}
|
|
9343
|
+
function escapeXml(s) {
|
|
9344
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
9345
|
+
}
|
|
9346
|
+
function createMacosManager() {
|
|
9347
|
+
const plistPath = getLaunchAgentPath();
|
|
9348
|
+
return {
|
|
9349
|
+
platform: "macos",
|
|
9350
|
+
async isInstalled() {
|
|
9351
|
+
return existsSync(plistPath);
|
|
9352
|
+
},
|
|
9353
|
+
async install(opts) {
|
|
9354
|
+
const dir = dirname(plistPath);
|
|
9355
|
+
if (!existsSync(dir))
|
|
9356
|
+
mkdirSync(dir, { recursive: true });
|
|
9357
|
+
writeFileSync(plistPath, buildPlist(opts), "utf-8");
|
|
9358
|
+
try {
|
|
9359
|
+
execSync(`launchctl bootout gui/$(id -u) ${plistPath} 2>/dev/null`, { stdio: "ignore" });
|
|
9360
|
+
} catch {}
|
|
9361
|
+
execSync(`launchctl bootstrap gui/$(id -u) ${plistPath}`, { stdio: "ignore" });
|
|
9362
|
+
},
|
|
9363
|
+
async uninstall() {
|
|
9364
|
+
if (!existsSync(plistPath))
|
|
9365
|
+
return;
|
|
9366
|
+
try {
|
|
9367
|
+
execSync(`launchctl bootout gui/$(id -u) ${plistPath}`, { stdio: "ignore" });
|
|
9368
|
+
} catch {}
|
|
9369
|
+
rmSync(plistPath, { force: true });
|
|
9370
|
+
},
|
|
9371
|
+
getServicePath() {
|
|
9372
|
+
return plistPath;
|
|
9373
|
+
}
|
|
9374
|
+
};
|
|
9375
|
+
}
|
|
9376
|
+
function getSystemdUnitPath() {
|
|
9377
|
+
return join2(homedir2(), ".config", "systemd", "user", "local-router.service");
|
|
9378
|
+
}
|
|
9379
|
+
function buildUnit(opts) {
|
|
9380
|
+
const logPath = getDaemonLogPath();
|
|
9381
|
+
const execStart = [opts.execPath, ...opts.args].join(" ");
|
|
9382
|
+
return `[Unit]
|
|
9383
|
+
Description=Local Router API Gateway
|
|
9384
|
+
After=network-online.target
|
|
9385
|
+
|
|
9386
|
+
[Service]
|
|
9387
|
+
Type=simple
|
|
9388
|
+
ExecStart=${execStart}
|
|
9389
|
+
Restart=on-failure
|
|
9390
|
+
RestartSec=5
|
|
9391
|
+
StandardOutput=append:${logPath}
|
|
9392
|
+
StandardError=append:${logPath}
|
|
9393
|
+
|
|
9394
|
+
[Install]
|
|
9395
|
+
WantedBy=default.target
|
|
9396
|
+
`;
|
|
9397
|
+
}
|
|
9398
|
+
function createLinuxManager() {
|
|
9399
|
+
const unitPath = getSystemdUnitPath();
|
|
9400
|
+
return {
|
|
9401
|
+
platform: "linux",
|
|
9402
|
+
async isInstalled() {
|
|
9403
|
+
if (!existsSync(unitPath))
|
|
9404
|
+
return false;
|
|
9405
|
+
try {
|
|
9406
|
+
const out = execSync("systemctl --user is-enabled local-router 2>/dev/null", {
|
|
9407
|
+
encoding: "utf-8"
|
|
9408
|
+
}).trim();
|
|
9409
|
+
return out === "enabled";
|
|
9410
|
+
} catch {
|
|
9411
|
+
return false;
|
|
9412
|
+
}
|
|
9413
|
+
},
|
|
9414
|
+
async install(opts) {
|
|
9415
|
+
const dir = dirname(unitPath);
|
|
9416
|
+
if (!existsSync(dir))
|
|
9417
|
+
mkdirSync(dir, { recursive: true });
|
|
9418
|
+
writeFileSync(unitPath, buildUnit(opts), "utf-8");
|
|
9419
|
+
execSync("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
9420
|
+
execSync("systemctl --user enable local-router", { stdio: "ignore" });
|
|
9421
|
+
},
|
|
9422
|
+
async uninstall() {
|
|
9423
|
+
try {
|
|
9424
|
+
execSync("systemctl --user disable local-router", { stdio: "ignore" });
|
|
9425
|
+
} catch {}
|
|
9426
|
+
rmSync(unitPath, { force: true });
|
|
9427
|
+
try {
|
|
9428
|
+
execSync("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
9429
|
+
} catch {}
|
|
9430
|
+
},
|
|
9431
|
+
getServicePath() {
|
|
9432
|
+
return unitPath;
|
|
9433
|
+
}
|
|
9434
|
+
};
|
|
9435
|
+
}
|
|
9436
|
+
var WIN_REG_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
9437
|
+
var WIN_REG_VALUE = "LocalRouter";
|
|
9438
|
+
function createWindowsManager() {
|
|
9439
|
+
return {
|
|
9440
|
+
platform: "windows",
|
|
9441
|
+
async isInstalled() {
|
|
9442
|
+
try {
|
|
9443
|
+
execSync(`reg query "${WIN_REG_KEY}" /v ${WIN_REG_VALUE}`, { stdio: "ignore" });
|
|
9444
|
+
return true;
|
|
9445
|
+
} catch {
|
|
9446
|
+
return false;
|
|
9447
|
+
}
|
|
9448
|
+
},
|
|
9449
|
+
async install(opts) {
|
|
9450
|
+
const cmd = [opts.execPath, ...opts.args].map((a) => `"${a}"`).join(" ");
|
|
9451
|
+
execSync(`reg add "${WIN_REG_KEY}" /v ${WIN_REG_VALUE} /t REG_SZ /d "${cmd}" /f`, {
|
|
9452
|
+
stdio: "ignore"
|
|
9453
|
+
});
|
|
9454
|
+
},
|
|
9455
|
+
async uninstall() {
|
|
9456
|
+
try {
|
|
9457
|
+
execSync(`reg delete "${WIN_REG_KEY}" /v ${WIN_REG_VALUE} /f`, { stdio: "ignore" });
|
|
9458
|
+
} catch {}
|
|
9459
|
+
},
|
|
9460
|
+
getServicePath() {
|
|
9461
|
+
return `${WIN_REG_KEY}\\${WIN_REG_VALUE}`;
|
|
9462
|
+
}
|
|
9463
|
+
};
|
|
9464
|
+
}
|
|
9465
|
+
function createUnsupportedManager() {
|
|
9466
|
+
return {
|
|
9467
|
+
platform: "unsupported",
|
|
9468
|
+
async isInstalled() {
|
|
9469
|
+
return false;
|
|
9470
|
+
},
|
|
9471
|
+
async install() {
|
|
9472
|
+
throw new Error("\u5F53\u524D\u5E73\u53F0\u4E0D\u652F\u6301\u81EA\u542F\u52A8");
|
|
9473
|
+
},
|
|
9474
|
+
async uninstall() {
|
|
9475
|
+
throw new Error("\u5F53\u524D\u5E73\u53F0\u4E0D\u652F\u6301\u81EA\u542F\u52A8");
|
|
9476
|
+
},
|
|
9477
|
+
getServicePath() {
|
|
9478
|
+
return "";
|
|
9479
|
+
}
|
|
9480
|
+
};
|
|
9481
|
+
}
|
|
9482
|
+
function createAutostartManager() {
|
|
9483
|
+
const p = platform();
|
|
9484
|
+
if (p === "darwin")
|
|
9485
|
+
return createMacosManager();
|
|
9486
|
+
if (p === "linux")
|
|
9487
|
+
return createLinuxManager();
|
|
9488
|
+
if (p === "win32")
|
|
9489
|
+
return createWindowsManager();
|
|
9490
|
+
return createUnsupportedManager();
|
|
9491
|
+
}
|
|
9492
|
+
function getAutostartExecArgs() {
|
|
9493
|
+
const script = process.argv[1] ?? "dist/cli.js";
|
|
9494
|
+
return {
|
|
9495
|
+
execPath: process.execPath,
|
|
9496
|
+
args: [script, "__run-server", "--mode", "daemon"]
|
|
9497
|
+
};
|
|
9498
|
+
}
|
|
9289
9499
|
|
|
9290
9500
|
// node_modules/.bun/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/dist/index.mjs
|
|
9291
9501
|
var marker = "vercel.ai.error";
|
|
@@ -29133,7 +29343,7 @@ function createProviderToolFactoryWithOutputSchema({
|
|
|
29133
29343
|
supportsDeferredResults
|
|
29134
29344
|
});
|
|
29135
29345
|
}
|
|
29136
|
-
async function
|
|
29346
|
+
async function resolve2(value) {
|
|
29137
29347
|
if (typeof value === "function") {
|
|
29138
29348
|
value = value();
|
|
29139
29349
|
}
|
|
@@ -32078,11 +32288,11 @@ var AnthropicMessagesLanguageModel = class {
|
|
|
32078
32288
|
betas,
|
|
32079
32289
|
headers
|
|
32080
32290
|
}) {
|
|
32081
|
-
return combineHeaders(await
|
|
32291
|
+
return combineHeaders(await resolve2(this.config.headers), headers, betas.size > 0 ? { "anthropic-beta": Array.from(betas).join(",") } : {});
|
|
32082
32292
|
}
|
|
32083
32293
|
async getBetasFromHeaders(requestHeaders) {
|
|
32084
32294
|
var _a16, _b16;
|
|
32085
|
-
const configHeaders = await
|
|
32295
|
+
const configHeaders = await resolve2(this.config.headers);
|
|
32086
32296
|
const configBetaHeader = (_a16 = configHeaders["anthropic-beta"]) != null ? _a16 : "";
|
|
32087
32297
|
const requestBetaHeader = (_b16 = requestHeaders == null ? undefined : requestHeaders["anthropic-beta"]) != null ? _b16 : "";
|
|
32088
32298
|
return new Set([
|
|
@@ -41267,7 +41477,7 @@ var GatewayFetchMetadata = class {
|
|
|
41267
41477
|
try {
|
|
41268
41478
|
const { value } = await getFromApi({
|
|
41269
41479
|
url: `${this.config.baseURL}/config`,
|
|
41270
|
-
headers: await
|
|
41480
|
+
headers: await resolve2(this.config.headers()),
|
|
41271
41481
|
successfulResponseHandler: createJsonResponseHandler(gatewayAvailableModelsResponseSchema),
|
|
41272
41482
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
41273
41483
|
errorSchema: exports_external.any(),
|
|
@@ -41285,7 +41495,7 @@ var GatewayFetchMetadata = class {
|
|
|
41285
41495
|
const baseUrl = new URL(this.config.baseURL);
|
|
41286
41496
|
const { value } = await getFromApi({
|
|
41287
41497
|
url: `${baseUrl.origin}/v1/credits`,
|
|
41288
|
-
headers: await
|
|
41498
|
+
headers: await resolve2(this.config.headers()),
|
|
41289
41499
|
successfulResponseHandler: createJsonResponseHandler(gatewayCreditsResponseSchema),
|
|
41290
41500
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
41291
41501
|
errorSchema: exports_external.any(),
|
|
@@ -41350,7 +41560,7 @@ var GatewayLanguageModel = class {
|
|
|
41350
41560
|
async doGenerate(options) {
|
|
41351
41561
|
const { args, warnings } = await this.getArgs(options);
|
|
41352
41562
|
const { abortSignal } = options;
|
|
41353
|
-
const resolvedHeaders = await
|
|
41563
|
+
const resolvedHeaders = await resolve2(this.config.headers());
|
|
41354
41564
|
try {
|
|
41355
41565
|
const {
|
|
41356
41566
|
responseHeaders,
|
|
@@ -41358,7 +41568,7 @@ var GatewayLanguageModel = class {
|
|
|
41358
41568
|
rawValue: rawResponse
|
|
41359
41569
|
} = await postJsonToApi({
|
|
41360
41570
|
url: this.getUrl(),
|
|
41361
|
-
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await
|
|
41571
|
+
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await resolve2(this.config.o11yHeaders)),
|
|
41362
41572
|
body: args,
|
|
41363
41573
|
successfulResponseHandler: createJsonResponseHandler(exports_external.any()),
|
|
41364
41574
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
@@ -41381,11 +41591,11 @@ var GatewayLanguageModel = class {
|
|
|
41381
41591
|
async doStream(options) {
|
|
41382
41592
|
const { args, warnings } = await this.getArgs(options);
|
|
41383
41593
|
const { abortSignal } = options;
|
|
41384
|
-
const resolvedHeaders = await
|
|
41594
|
+
const resolvedHeaders = await resolve2(this.config.headers());
|
|
41385
41595
|
try {
|
|
41386
41596
|
const { value: response, responseHeaders } = await postJsonToApi({
|
|
41387
41597
|
url: this.getUrl(),
|
|
41388
|
-
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await
|
|
41598
|
+
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await resolve2(this.config.o11yHeaders)),
|
|
41389
41599
|
body: args,
|
|
41390
41600
|
successfulResponseHandler: createEventSourceResponseHandler(exports_external.any()),
|
|
41391
41601
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
@@ -41471,7 +41681,7 @@ var GatewayEmbeddingModel = class {
|
|
|
41471
41681
|
providerOptions
|
|
41472
41682
|
}) {
|
|
41473
41683
|
var _a92;
|
|
41474
|
-
const resolvedHeaders = await
|
|
41684
|
+
const resolvedHeaders = await resolve2(this.config.headers());
|
|
41475
41685
|
try {
|
|
41476
41686
|
const {
|
|
41477
41687
|
responseHeaders,
|
|
@@ -41479,7 +41689,7 @@ var GatewayEmbeddingModel = class {
|
|
|
41479
41689
|
rawValue
|
|
41480
41690
|
} = await postJsonToApi({
|
|
41481
41691
|
url: this.getUrl(),
|
|
41482
|
-
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await
|
|
41692
|
+
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve2(this.config.o11yHeaders)),
|
|
41483
41693
|
body: {
|
|
41484
41694
|
values,
|
|
41485
41695
|
...providerOptions ? { providerOptions } : {}
|
|
@@ -41541,7 +41751,7 @@ var GatewayImageModel = class {
|
|
|
41541
41751
|
abortSignal
|
|
41542
41752
|
}) {
|
|
41543
41753
|
var _a92, _b92, _c, _d;
|
|
41544
|
-
const resolvedHeaders = await
|
|
41754
|
+
const resolvedHeaders = await resolve2(this.config.headers());
|
|
41545
41755
|
try {
|
|
41546
41756
|
const {
|
|
41547
41757
|
responseHeaders,
|
|
@@ -41549,7 +41759,7 @@ var GatewayImageModel = class {
|
|
|
41549
41759
|
rawValue
|
|
41550
41760
|
} = await postJsonToApi({
|
|
41551
41761
|
url: this.getUrl(),
|
|
41552
|
-
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await
|
|
41762
|
+
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve2(this.config.o11yHeaders)),
|
|
41553
41763
|
body: {
|
|
41554
41764
|
prompt,
|
|
41555
41765
|
n,
|
|
@@ -41664,11 +41874,11 @@ var GatewayVideoModel = class {
|
|
|
41664
41874
|
abortSignal
|
|
41665
41875
|
}) {
|
|
41666
41876
|
var _a92;
|
|
41667
|
-
const resolvedHeaders = await
|
|
41877
|
+
const resolvedHeaders = await resolve2(this.config.headers());
|
|
41668
41878
|
try {
|
|
41669
41879
|
const { responseHeaders, value: responseBody } = await postJsonToApi({
|
|
41670
41880
|
url: this.getUrl(),
|
|
41671
|
-
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await
|
|
41881
|
+
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve2(this.config.o11yHeaders), { accept: "text/event-stream" }),
|
|
41672
41882
|
body: {
|
|
41673
41883
|
prompt,
|
|
41674
41884
|
n,
|
|
@@ -44539,7 +44749,7 @@ var object2 = ({
|
|
|
44539
44749
|
const schema = asSchema(inputSchema);
|
|
44540
44750
|
return {
|
|
44541
44751
|
name: "object",
|
|
44542
|
-
responseFormat:
|
|
44752
|
+
responseFormat: resolve2(schema.jsonSchema).then((jsonSchema2) => ({
|
|
44543
44753
|
type: "json",
|
|
44544
44754
|
schema: jsonSchema2,
|
|
44545
44755
|
...name21 != null && { name: name21 },
|
|
@@ -44601,7 +44811,7 @@ var array2 = ({
|
|
|
44601
44811
|
const elementSchema = asSchema(inputElementSchema);
|
|
44602
44812
|
return {
|
|
44603
44813
|
name: "array",
|
|
44604
|
-
responseFormat:
|
|
44814
|
+
responseFormat: resolve2(elementSchema.jsonSchema).then((jsonSchema2) => {
|
|
44605
44815
|
const { $schema, ...itemSchema } = jsonSchema2;
|
|
44606
44816
|
return {
|
|
44607
44817
|
type: "json",
|
|
@@ -49860,7 +50070,7 @@ var Hono2 = class extends Hono {
|
|
|
49860
50070
|
|
|
49861
50071
|
// node_modules/.bun/hono@4.12.5/node_modules/hono/dist/adapter/bun/serve-static.js
|
|
49862
50072
|
import { stat } from "fs/promises";
|
|
49863
|
-
import { join } from "path";
|
|
50073
|
+
import { join as join3 } from "path";
|
|
49864
50074
|
|
|
49865
50075
|
// node_modules/.bun/hono@4.12.5/node_modules/hono/dist/utils/compress.js
|
|
49866
50076
|
var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/(?!event-stream(?:[;\s]|$))[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
|
|
@@ -49965,7 +50175,7 @@ var DEFAULT_DOCUMENT = "index.html";
|
|
|
49965
50175
|
var serveStatic = (options) => {
|
|
49966
50176
|
const root = options.root ?? "./";
|
|
49967
50177
|
const optionPath = options.path;
|
|
49968
|
-
const
|
|
50178
|
+
const join3 = options.join ?? defaultJoin;
|
|
49969
50179
|
return async (c, next) => {
|
|
49970
50180
|
if (c.finalized) {
|
|
49971
50181
|
return next();
|
|
@@ -49984,9 +50194,9 @@ var serveStatic = (options) => {
|
|
|
49984
50194
|
return next();
|
|
49985
50195
|
}
|
|
49986
50196
|
}
|
|
49987
|
-
let path =
|
|
50197
|
+
let path = join3(root, !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename) : filename);
|
|
49988
50198
|
if (options.isDir && await options.isDir(path)) {
|
|
49989
|
-
path =
|
|
50199
|
+
path = join3(path, DEFAULT_DOCUMENT);
|
|
49990
50200
|
}
|
|
49991
50201
|
const getContent = options.getContent;
|
|
49992
50202
|
let content = await getContent(path, c);
|
|
@@ -50038,7 +50248,7 @@ var serveStatic2 = (options) => {
|
|
|
50038
50248
|
return serveStatic({
|
|
50039
50249
|
...options,
|
|
50040
50250
|
getContent,
|
|
50041
|
-
join,
|
|
50251
|
+
join: join3,
|
|
50042
50252
|
isDir
|
|
50043
50253
|
})(c, next);
|
|
50044
50254
|
};
|
|
@@ -50130,9 +50340,9 @@ var upgradeWebSocket = defineWebSocketHelper((c, events) => {
|
|
|
50130
50340
|
});
|
|
50131
50341
|
|
|
50132
50342
|
// src/config.ts
|
|
50133
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
50134
|
-
import { homedir } from "os";
|
|
50135
|
-
import { join as
|
|
50343
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
50344
|
+
import { homedir as homedir3 } from "os";
|
|
50345
|
+
import { join as join4, resolve as resolve3 } from "path";
|
|
50136
50346
|
|
|
50137
50347
|
// node_modules/.bun/json5@2.2.3/node_modules/json5/dist/index.mjs
|
|
50138
50348
|
var Space_Separator = /[\u1680\u2000-\u200A\u202F\u205F\u3000]/;
|
|
@@ -51300,8 +51510,8 @@ var DEFAULT_CONFIG = `{
|
|
|
51300
51510
|
// },
|
|
51301
51511
|
}`;
|
|
51302
51512
|
function loadConfig(configPath) {
|
|
51303
|
-
const absolutePath =
|
|
51304
|
-
const content =
|
|
51513
|
+
const absolutePath = resolve3(configPath);
|
|
51514
|
+
const content = readFileSync2(absolutePath, "utf-8");
|
|
51305
51515
|
const config2 = dist_default.parse(content);
|
|
51306
51516
|
for (const [routeType, modelMap] of Object.entries(config2.routes)) {
|
|
51307
51517
|
if (!modelMap["*"]) {
|
|
@@ -51316,21 +51526,21 @@ function loadConfig(configPath) {
|
|
|
51316
51526
|
return config2;
|
|
51317
51527
|
}
|
|
51318
51528
|
function createDefaultConfig(configPath) {
|
|
51319
|
-
const configDir =
|
|
51320
|
-
if (!
|
|
51321
|
-
|
|
51529
|
+
const configDir = resolve3(configPath, "..");
|
|
51530
|
+
if (!existsSync2(configDir)) {
|
|
51531
|
+
mkdirSync2(configDir, { recursive: true });
|
|
51322
51532
|
}
|
|
51323
|
-
|
|
51533
|
+
writeFileSync2(configPath, DEFAULT_CONFIG, "utf-8");
|
|
51324
51534
|
console.log(`\u5DF2\u521B\u5EFA\u9ED8\u8BA4\u914D\u7F6E\u6587\u4EF6: ${configPath}`);
|
|
51325
51535
|
}
|
|
51326
51536
|
function resolveDefaultConfigPath() {
|
|
51327
51537
|
const localConfig = "config.json5";
|
|
51328
|
-
if (
|
|
51538
|
+
if (existsSync2(localConfig)) {
|
|
51329
51539
|
return localConfig;
|
|
51330
51540
|
}
|
|
51331
|
-
const globalConfigDir =
|
|
51332
|
-
const globalConfig2 =
|
|
51333
|
-
if (!
|
|
51541
|
+
const globalConfigDir = join4(homedir3(), ".local-router");
|
|
51542
|
+
const globalConfig2 = join4(globalConfigDir, "config.json5");
|
|
51543
|
+
if (!existsSync2(globalConfig2)) {
|
|
51334
51544
|
createDefaultConfig(globalConfig2);
|
|
51335
51545
|
}
|
|
51336
51546
|
return globalConfig2;
|
|
@@ -51349,19 +51559,19 @@ function parseConfigPath() {
|
|
|
51349
51559
|
}
|
|
51350
51560
|
function resolveLogBaseDir(logConfig) {
|
|
51351
51561
|
if (logConfig?.baseDir) {
|
|
51352
|
-
return
|
|
51562
|
+
return resolve3(logConfig.baseDir);
|
|
51353
51563
|
}
|
|
51354
|
-
return
|
|
51564
|
+
return join4(homedir3(), ".local-router", "logs");
|
|
51355
51565
|
}
|
|
51356
51566
|
|
|
51357
51567
|
// src/config-store.ts
|
|
51358
|
-
import { writeFileSync as
|
|
51359
|
-
import { resolve as
|
|
51568
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
51569
|
+
import { resolve as resolve4 } from "path";
|
|
51360
51570
|
class ConfigStore {
|
|
51361
51571
|
config;
|
|
51362
51572
|
absolutePath;
|
|
51363
51573
|
constructor(configPath) {
|
|
51364
|
-
this.absolutePath =
|
|
51574
|
+
this.absolutePath = resolve4(configPath);
|
|
51365
51575
|
this.config = loadConfig(this.absolutePath);
|
|
51366
51576
|
}
|
|
51367
51577
|
get() {
|
|
@@ -51376,7 +51586,7 @@ class ConfigStore {
|
|
|
51376
51586
|
}
|
|
51377
51587
|
save(newConfig) {
|
|
51378
51588
|
const content = dist_default.stringify(newConfig, { space: 2, quote: '"' });
|
|
51379
|
-
|
|
51589
|
+
writeFileSync3(this.absolutePath, content, "utf-8");
|
|
51380
51590
|
}
|
|
51381
51591
|
validate(config2) {
|
|
51382
51592
|
for (const [routeType, modelMap] of Object.entries(config2.routes)) {
|
|
@@ -51395,7 +51605,7 @@ class ConfigStore {
|
|
|
51395
51605
|
// src/config-validate.ts
|
|
51396
51606
|
var import__2020 = __toESM(require_2020(), 1);
|
|
51397
51607
|
var import_ajv_formats = __toESM(require_dist2(), 1);
|
|
51398
|
-
import { readFileSync as
|
|
51608
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
51399
51609
|
|
|
51400
51610
|
// src/runtime-assets.ts
|
|
51401
51611
|
import { fileURLToPath } from "url";
|
|
@@ -51426,7 +51636,7 @@ function validateConfigOrThrow(config2) {
|
|
|
51426
51636
|
validateBusinessRules(config2);
|
|
51427
51637
|
const ajv = new import__2020.default({ allErrors: true, strict: false });
|
|
51428
51638
|
import_ajv_formats.default(ajv);
|
|
51429
|
-
const schemaJson = JSON.parse(
|
|
51639
|
+
const schemaJson = JSON.parse(readFileSync3(getBundledSchemaPath(), "utf-8"));
|
|
51430
51640
|
const validateBySchema = ajv.compile(schemaJson);
|
|
51431
51641
|
const valid = validateBySchema(config2);
|
|
51432
51642
|
if (!valid) {
|
|
@@ -51487,8 +51697,8 @@ class CryptoSession {
|
|
|
51487
51697
|
}
|
|
51488
51698
|
|
|
51489
51699
|
// src/log-metrics.ts
|
|
51490
|
-
import { createReadStream, existsSync as
|
|
51491
|
-
import { join as
|
|
51700
|
+
import { createReadStream, existsSync as existsSync3 } from "fs";
|
|
51701
|
+
import { join as join5 } from "path";
|
|
51492
51702
|
import { createInterface } from "readline";
|
|
51493
51703
|
var WINDOW_MS = {
|
|
51494
51704
|
"1h": 60 * 60 * 1000,
|
|
@@ -51605,8 +51815,8 @@ async function getLogMetrics(options) {
|
|
|
51605
51815
|
if (!refresh && cached2 && cached2.expiresAt > nowMs) {
|
|
51606
51816
|
return cached2.value;
|
|
51607
51817
|
}
|
|
51608
|
-
const eventsDir =
|
|
51609
|
-
if (!
|
|
51818
|
+
const eventsDir = join5(baseDir, "events");
|
|
51819
|
+
if (!existsSync3(eventsDir)) {
|
|
51610
51820
|
const empty = createEmptyMetrics(window2, nowMs, {
|
|
51611
51821
|
logEnabled: true,
|
|
51612
51822
|
baseDir,
|
|
@@ -51652,8 +51862,8 @@ async function getLogMetrics(options) {
|
|
|
51652
51862
|
partial2 = true;
|
|
51653
51863
|
break;
|
|
51654
51864
|
}
|
|
51655
|
-
const filePath =
|
|
51656
|
-
if (!
|
|
51865
|
+
const filePath = join5(eventsDir, `${dateStr}.jsonl`);
|
|
51866
|
+
if (!existsSync3(filePath))
|
|
51657
51867
|
continue;
|
|
51658
51868
|
filesScanned += 1;
|
|
51659
51869
|
try {
|
|
@@ -51789,8 +51999,8 @@ async function getLogMetrics(options) {
|
|
|
51789
51999
|
}
|
|
51790
52000
|
|
|
51791
52001
|
// src/log-query.ts
|
|
51792
|
-
import { createReadStream as createReadStream3, existsSync as
|
|
51793
|
-
import { join as
|
|
52002
|
+
import { createReadStream as createReadStream3, existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
52003
|
+
import { join as join7, resolve as resolve6 } from "path";
|
|
51794
52004
|
import { createInterface as createInterface2 } from "readline";
|
|
51795
52005
|
|
|
51796
52006
|
// src/log-index.ts
|
|
@@ -51798,13 +52008,13 @@ import { Database } from "bun:sqlite";
|
|
|
51798
52008
|
import {
|
|
51799
52009
|
closeSync,
|
|
51800
52010
|
createReadStream as createReadStream2,
|
|
51801
|
-
existsSync as
|
|
51802
|
-
mkdirSync as
|
|
52011
|
+
existsSync as existsSync5,
|
|
52012
|
+
mkdirSync as mkdirSync3,
|
|
51803
52013
|
openSync,
|
|
51804
52014
|
readSync,
|
|
51805
52015
|
statSync as statSync2
|
|
51806
52016
|
} from "fs";
|
|
51807
|
-
import { join as
|
|
52017
|
+
import { join as join6 } from "path";
|
|
51808
52018
|
|
|
51809
52019
|
// src/log-session-identity.ts
|
|
51810
52020
|
var USER_SESSION_DELIMITER = "_account__session_";
|
|
@@ -51883,8 +52093,8 @@ function resolveLogSessionIdentity(requestBody) {
|
|
|
51883
52093
|
}
|
|
51884
52094
|
|
|
51885
52095
|
// src/token-usage.ts
|
|
51886
|
-
import { existsSync as
|
|
51887
|
-
import { resolve as
|
|
52096
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, statSync } from "fs";
|
|
52097
|
+
import { resolve as resolve5 } from "path";
|
|
51888
52098
|
var MAX_STREAM_USAGE_BYTES = 25 * 1024 * 1024;
|
|
51889
52099
|
function asRecord(value) {
|
|
51890
52100
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -52401,12 +52611,12 @@ function safeReadStreamFile(streamFile, baseDir) {
|
|
|
52401
52611
|
try {
|
|
52402
52612
|
const candidates = [streamFile];
|
|
52403
52613
|
if (baseDir)
|
|
52404
|
-
candidates.push(
|
|
52614
|
+
candidates.push(resolve5(baseDir, streamFile));
|
|
52405
52615
|
for (const candidate of candidates) {
|
|
52406
|
-
const resolved =
|
|
52616
|
+
const resolved = resolve5(candidate);
|
|
52407
52617
|
if (!resolved.endsWith(".sse.raw"))
|
|
52408
52618
|
continue;
|
|
52409
|
-
if (!
|
|
52619
|
+
if (!existsSync4(resolved))
|
|
52410
52620
|
continue;
|
|
52411
52621
|
const stats = statSync(resolved);
|
|
52412
52622
|
if (stats.size > MAX_STREAM_USAGE_BYTES) {
|
|
@@ -52415,7 +52625,7 @@ function safeReadStreamFile(streamFile, baseDir) {
|
|
|
52415
52625
|
warning: `stream_file \u8D85\u8FC7 ${MAX_STREAM_USAGE_BYTES} \u5B57\u8282\uFF0C\u5DF2\u8DF3\u8FC7 token usage \u56DE\u586B`
|
|
52416
52626
|
};
|
|
52417
52627
|
}
|
|
52418
|
-
return { content:
|
|
52628
|
+
return { content: readFileSync4(resolved, "utf-8"), warning: null };
|
|
52419
52629
|
}
|
|
52420
52630
|
} catch (err) {
|
|
52421
52631
|
return {
|
|
@@ -52866,8 +53076,8 @@ class LogIndex {
|
|
|
52866
53076
|
constructor(baseDir, config2) {
|
|
52867
53077
|
this.baseDir = baseDir;
|
|
52868
53078
|
this.config = config2;
|
|
52869
|
-
|
|
52870
|
-
const dbPath =
|
|
53079
|
+
mkdirSync3(baseDir, { recursive: true });
|
|
53080
|
+
const dbPath = join6(baseDir, "logs-index.sqlite");
|
|
52871
53081
|
this.db = new Database(dbPath, { create: true, strict: true });
|
|
52872
53082
|
this.configure();
|
|
52873
53083
|
this.migrate();
|
|
@@ -52943,11 +53153,11 @@ class LogIndex {
|
|
|
52943
53153
|
let scannedFiles = 0;
|
|
52944
53154
|
let scannedLines = 0;
|
|
52945
53155
|
let parseErrors = 0;
|
|
52946
|
-
const eventsDir =
|
|
53156
|
+
const eventsDir = join6(this.baseDir, "events");
|
|
52947
53157
|
const dates = listDateStrings2(fromMs, toMs);
|
|
52948
53158
|
for (const date5 of dates) {
|
|
52949
|
-
const filePath =
|
|
52950
|
-
if (!
|
|
53159
|
+
const filePath = join6(eventsDir, `${date5}.jsonl`);
|
|
53160
|
+
if (!existsSync5(filePath))
|
|
52951
53161
|
continue;
|
|
52952
53162
|
const stats = statSync2(filePath);
|
|
52953
53163
|
const fileRow = this.db.query("SELECT size_bytes, mtime_ms FROM log_index_files WHERE file_path = ?").get(filePath);
|
|
@@ -53038,8 +53248,8 @@ class LogIndex {
|
|
|
53038
53248
|
if (!parsedId)
|
|
53039
53249
|
return null;
|
|
53040
53250
|
const row = this.db.query("SELECT source_date, source_file, line_number, byte_offset FROM log_events WHERE id = ?").get(id);
|
|
53041
|
-
const filePath = row?.source_file ??
|
|
53042
|
-
if (!
|
|
53251
|
+
const filePath = row?.source_file ?? join6(this.baseDir, "events", `${parsedId.date}.jsonl`);
|
|
53252
|
+
if (!existsSync5(filePath))
|
|
53043
53253
|
return null;
|
|
53044
53254
|
const line2 = readLineAtOffset(filePath, row?.byte_offset ?? parsedId.offset);
|
|
53045
53255
|
if (!line2?.trim())
|
|
@@ -53448,8 +53658,8 @@ function getIndexedLogEventDetail(logConfig, id) {
|
|
|
53448
53658
|
const parsedId = decodeOffsetLogEventId(id);
|
|
53449
53659
|
if (!parsedId)
|
|
53450
53660
|
return null;
|
|
53451
|
-
const filePath =
|
|
53452
|
-
if (!
|
|
53661
|
+
const filePath = join6(baseDir, "events", `${parsedId.date}.jsonl`);
|
|
53662
|
+
if (!existsSync5(filePath))
|
|
53453
53663
|
return null;
|
|
53454
53664
|
const line2 = readLineAtOffset(filePath, parsedId.offset);
|
|
53455
53665
|
if (!line2?.trim())
|
|
@@ -53556,12 +53766,8 @@ function buildMessage2(event) {
|
|
|
53556
53766
|
const status = event.upstream_status ?? 0;
|
|
53557
53767
|
return `${event.method} ${event.path} -> ${status}`;
|
|
53558
53768
|
}
|
|
53559
|
-
function
|
|
53560
|
-
|
|
53561
|
-
return true;
|
|
53562
|
-
const identity = resolveLogSessionIdentity(event.request_body);
|
|
53563
|
-
const keyword = q.toLowerCase();
|
|
53564
|
-
const haystack = [
|
|
53769
|
+
function buildKeywordText(event, identity) {
|
|
53770
|
+
return [
|
|
53565
53771
|
event.request_id,
|
|
53566
53772
|
event.path,
|
|
53567
53773
|
event.provider,
|
|
@@ -53575,7 +53781,13 @@ function containsKeyword(event, q) {
|
|
|
53575
53781
|
event.error_message ?? "",
|
|
53576
53782
|
buildMessage2(event)
|
|
53577
53783
|
].join(" ").toLowerCase();
|
|
53578
|
-
|
|
53784
|
+
}
|
|
53785
|
+
function containsKeyword(event, q) {
|
|
53786
|
+
if (!q)
|
|
53787
|
+
return true;
|
|
53788
|
+
const identity = resolveLogSessionIdentity(event.request_body);
|
|
53789
|
+
const keyword = q.toLowerCase();
|
|
53790
|
+
return buildKeywordText(event, identity).includes(keyword);
|
|
53579
53791
|
}
|
|
53580
53792
|
function createRunningStats() {
|
|
53581
53793
|
return {
|
|
@@ -53743,7 +53955,7 @@ function normalizeQuery(input, maxLimit = MAX_QUERY_LIMIT) {
|
|
|
53743
53955
|
}
|
|
53744
53956
|
function eventToSummary(item) {
|
|
53745
53957
|
const { event } = item;
|
|
53746
|
-
const identity = resolveLogSessionIdentity(event.request_body);
|
|
53958
|
+
const identity = item.identity ?? resolveLogSessionIdentity(event.request_body);
|
|
53747
53959
|
return {
|
|
53748
53960
|
id: item.id,
|
|
53749
53961
|
ts: event.ts_start,
|
|
@@ -53768,19 +53980,39 @@ function eventToSummary(item) {
|
|
|
53768
53980
|
tokenUsage: item.tokenUsage
|
|
53769
53981
|
};
|
|
53770
53982
|
}
|
|
53771
|
-
function
|
|
53983
|
+
function extractLogEventFacts(event) {
|
|
53772
53984
|
const ts = Date.parse(event.ts_start);
|
|
53985
|
+
const level = getLevel2(event);
|
|
53986
|
+
const statusClass = getStatusClass3(event);
|
|
53987
|
+
const identity = resolveLogSessionIdentity(event.request_body);
|
|
53988
|
+
return {
|
|
53989
|
+
event,
|
|
53990
|
+
ts: Number.isFinite(ts) ? ts : Number.NaN,
|
|
53991
|
+
level,
|
|
53992
|
+
statusClass,
|
|
53993
|
+
model: event.model_out || event.model_in,
|
|
53994
|
+
identity,
|
|
53995
|
+
hasError: level === "error",
|
|
53996
|
+
keywordText: buildKeywordText(event, identity),
|
|
53997
|
+
tokenUsage: extractTokenUsageSummaryFromLogEvent(event)
|
|
53998
|
+
};
|
|
53999
|
+
}
|
|
54000
|
+
function createLogEventSummaryFromFacts(facts, location) {
|
|
53773
54001
|
return eventToSummary({
|
|
53774
54002
|
id: location.id,
|
|
53775
54003
|
date: location.date,
|
|
53776
54004
|
line: location.line ?? 0,
|
|
53777
|
-
ts:
|
|
53778
|
-
level:
|
|
53779
|
-
statusClass:
|
|
53780
|
-
event,
|
|
53781
|
-
|
|
54005
|
+
ts: facts.ts,
|
|
54006
|
+
level: facts.level,
|
|
54007
|
+
statusClass: facts.statusClass,
|
|
54008
|
+
event: facts.event,
|
|
54009
|
+
identity: facts.identity,
|
|
54010
|
+
tokenUsage: facts.tokenUsage
|
|
53782
54011
|
});
|
|
53783
54012
|
}
|
|
54013
|
+
function createLogEventSummaryFromEvent(event, location) {
|
|
54014
|
+
return createLogEventSummaryFromFacts(extractLogEventFacts(event), location);
|
|
54015
|
+
}
|
|
53784
54016
|
function logEventMatchesQuery(event, query) {
|
|
53785
54017
|
if (!event.ts_start)
|
|
53786
54018
|
return false;
|
|
@@ -53867,23 +54099,23 @@ function readStreamContent(baseDir, streamFile) {
|
|
|
53867
54099
|
if (!streamFile)
|
|
53868
54100
|
return { content: null, warning: null };
|
|
53869
54101
|
try {
|
|
53870
|
-
const resolvedBase =
|
|
53871
|
-
const resolvedFromFile =
|
|
54102
|
+
const resolvedBase = resolve6(baseDir);
|
|
54103
|
+
const resolvedFromFile = resolve6(streamFile);
|
|
53872
54104
|
const looksLikeStreamFile = resolvedFromFile.endsWith(".sse.raw");
|
|
53873
54105
|
if (!looksLikeStreamFile) {
|
|
53874
54106
|
return { content: null, warning: "stream_file \u4E0D\u662F .sse.raw \u6587\u4EF6\uFF0C\u5DF2\u8DF3\u8FC7\u8BFB\u53D6\u3002" };
|
|
53875
54107
|
}
|
|
53876
|
-
if (
|
|
53877
|
-
return { content:
|
|
54108
|
+
if (existsSync6(resolvedFromFile)) {
|
|
54109
|
+
return { content: readFileSync5(resolvedFromFile, "utf-8"), warning: null };
|
|
53878
54110
|
}
|
|
53879
|
-
const fallbackPath =
|
|
54111
|
+
const fallbackPath = resolve6(resolvedBase, streamFile);
|
|
53880
54112
|
if (!fallbackPath.startsWith(`${resolvedBase}/`) && fallbackPath !== resolvedBase) {
|
|
53881
54113
|
return { content: null, warning: "stream_file \u8DEF\u5F84\u975E\u6CD5\uFF0C\u5DF2\u62D2\u7EDD\u8BFB\u53D6\u3002" };
|
|
53882
54114
|
}
|
|
53883
|
-
if (!
|
|
54115
|
+
if (!existsSync6(fallbackPath)) {
|
|
53884
54116
|
return { content: null, warning: "stream_file \u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u5DF2\u88AB\u6E05\u7406\u3002" };
|
|
53885
54117
|
}
|
|
53886
|
-
return { content:
|
|
54118
|
+
return { content: readFileSync5(fallbackPath, "utf-8"), warning: null };
|
|
53887
54119
|
} catch (err) {
|
|
53888
54120
|
return {
|
|
53889
54121
|
content: null,
|
|
@@ -53979,8 +54211,8 @@ async function buildLogEventDetail(id, parsed, location, context2) {
|
|
|
53979
54211
|
};
|
|
53980
54212
|
}
|
|
53981
54213
|
async function scanEvents(baseDir, query) {
|
|
53982
|
-
const eventsDir =
|
|
53983
|
-
if (!
|
|
54214
|
+
const eventsDir = join7(baseDir, "events");
|
|
54215
|
+
if (!existsSync6(eventsDir)) {
|
|
53984
54216
|
return {
|
|
53985
54217
|
items: [],
|
|
53986
54218
|
stats: createEmptyLogQueryStats(),
|
|
@@ -54006,8 +54238,8 @@ async function scanEvents(baseDir, query) {
|
|
|
54006
54238
|
truncated = true;
|
|
54007
54239
|
break;
|
|
54008
54240
|
}
|
|
54009
|
-
const filePath =
|
|
54010
|
-
if (!
|
|
54241
|
+
const filePath = join7(eventsDir, `${date5}.jsonl`);
|
|
54242
|
+
if (!existsSync6(filePath))
|
|
54011
54243
|
continue;
|
|
54012
54244
|
scannedFiles += 1;
|
|
54013
54245
|
const stream = createReadStream3(filePath, { encoding: "utf-8" });
|
|
@@ -54098,6 +54330,9 @@ async function scanEvents(baseDir, query) {
|
|
|
54098
54330
|
function isLogQueryWindow(value) {
|
|
54099
54331
|
return value === "1h" || value === "6h" || value === "24h";
|
|
54100
54332
|
}
|
|
54333
|
+
function getLogQueryWindowMs(window2) {
|
|
54334
|
+
return WINDOW_MS2[window2];
|
|
54335
|
+
}
|
|
54101
54336
|
function resolveLogQueryRange(input) {
|
|
54102
54337
|
const nowMs = input.nowMs ?? Date.now();
|
|
54103
54338
|
const hasFrom = !!input.from;
|
|
@@ -54189,8 +54424,8 @@ async function getLogEventDetailById(context2, id) {
|
|
|
54189
54424
|
}
|
|
54190
54425
|
const { date: date5, line: line2 } = decodeEventId(id);
|
|
54191
54426
|
const baseDir = resolveLogBaseDir(context2.logConfig);
|
|
54192
|
-
const filePath =
|
|
54193
|
-
if (!
|
|
54427
|
+
const filePath = join7(baseDir, "events", `${date5}.jsonl`);
|
|
54428
|
+
if (!existsSync6(filePath))
|
|
54194
54429
|
return null;
|
|
54195
54430
|
const stream = createReadStream3(filePath, { encoding: "utf-8" });
|
|
54196
54431
|
const rl = createInterface2({ input: stream, crlfDelay: Number.POSITIVE_INFINITY });
|
|
@@ -54403,12 +54638,666 @@ function parseBooleanFlag(value) {
|
|
|
54403
54638
|
throw new Error("hasError \u53C2\u6570\u4EC5\u652F\u6301 true/false/1/0");
|
|
54404
54639
|
}
|
|
54405
54640
|
|
|
54641
|
+
// src/log-tail.ts
|
|
54642
|
+
var subscribers = new Set;
|
|
54643
|
+
function publishLogEvent(event) {
|
|
54644
|
+
for (const subscriber of subscribers) {
|
|
54645
|
+
try {
|
|
54646
|
+
subscriber(event);
|
|
54647
|
+
} catch {}
|
|
54648
|
+
}
|
|
54649
|
+
}
|
|
54650
|
+
function subscribeLogEvents(subscriber) {
|
|
54651
|
+
subscribers.add(subscriber);
|
|
54652
|
+
return () => {
|
|
54653
|
+
subscribers.delete(subscriber);
|
|
54654
|
+
};
|
|
54655
|
+
}
|
|
54656
|
+
|
|
54657
|
+
// src/network-access.ts
|
|
54658
|
+
var REMOTE_ADDRESS_ENV_KEY = "LOCAL_ROUTER_REMOTE_ADDRESS";
|
|
54659
|
+
function parseIpv4(address) {
|
|
54660
|
+
const parts = address.split(".");
|
|
54661
|
+
if (parts.length !== 4)
|
|
54662
|
+
return null;
|
|
54663
|
+
const octets = parts.map((part) => {
|
|
54664
|
+
if (!/^\d{1,3}$/.test(part))
|
|
54665
|
+
return Number.NaN;
|
|
54666
|
+
const value = Number.parseInt(part, 10);
|
|
54667
|
+
return value >= 0 && value <= 255 ? value : Number.NaN;
|
|
54668
|
+
});
|
|
54669
|
+
return octets.every(Number.isFinite) ? octets : null;
|
|
54670
|
+
}
|
|
54671
|
+
function normalizeIpAddress(raw2) {
|
|
54672
|
+
let address = raw2.trim().toLowerCase();
|
|
54673
|
+
if (address.startsWith("[")) {
|
|
54674
|
+
const end = address.indexOf("]");
|
|
54675
|
+
if (end !== -1)
|
|
54676
|
+
address = address.slice(1, end);
|
|
54677
|
+
}
|
|
54678
|
+
const mappedIpv4 = address.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/);
|
|
54679
|
+
if (mappedIpv4) {
|
|
54680
|
+
return mappedIpv4[1];
|
|
54681
|
+
}
|
|
54682
|
+
return address;
|
|
54683
|
+
}
|
|
54684
|
+
function isLoopbackAddress(raw2) {
|
|
54685
|
+
if (!raw2)
|
|
54686
|
+
return false;
|
|
54687
|
+
const address = normalizeIpAddress(raw2);
|
|
54688
|
+
const ipv43 = parseIpv4(address);
|
|
54689
|
+
if (ipv43)
|
|
54690
|
+
return ipv43[0] === 127;
|
|
54691
|
+
return address === "::1";
|
|
54692
|
+
}
|
|
54693
|
+
function isLanAddress(raw2) {
|
|
54694
|
+
if (!raw2)
|
|
54695
|
+
return false;
|
|
54696
|
+
const address = normalizeIpAddress(raw2);
|
|
54697
|
+
const ipv43 = parseIpv4(address);
|
|
54698
|
+
if (ipv43) {
|
|
54699
|
+
const [a, b] = ipv43;
|
|
54700
|
+
return a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 169 && b === 254;
|
|
54701
|
+
}
|
|
54702
|
+
return address.startsWith("fc") || address.startsWith("fd") || /^fe[89ab]/.test(address);
|
|
54703
|
+
}
|
|
54704
|
+
function decideNetworkAccess(serverConfig, rawRemoteAddress) {
|
|
54705
|
+
const remoteAddress = rawRemoteAddress ? normalizeIpAddress(rawRemoteAddress) : null;
|
|
54706
|
+
if (!remoteAddress || isLoopbackAddress(remoteAddress)) {
|
|
54707
|
+
return { allowed: true, remoteAddress };
|
|
54708
|
+
}
|
|
54709
|
+
const lanEnabled = serverConfig?.lanAccess?.enabled === true;
|
|
54710
|
+
if (!lanEnabled) {
|
|
54711
|
+
return { allowed: false, remoteAddress, reason: "lan-disabled" };
|
|
54712
|
+
}
|
|
54713
|
+
if (!isLanAddress(remoteAddress)) {
|
|
54714
|
+
return { allowed: false, remoteAddress, reason: "non-lan-address" };
|
|
54715
|
+
}
|
|
54716
|
+
return { allowed: true, remoteAddress };
|
|
54717
|
+
}
|
|
54718
|
+
function getRemoteAddressFromContext(c2) {
|
|
54719
|
+
const env = c2.env;
|
|
54720
|
+
const value = env?.[REMOTE_ADDRESS_ENV_KEY];
|
|
54721
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
54722
|
+
}
|
|
54723
|
+
function createNetworkAccessMiddleware(store) {
|
|
54724
|
+
return async (c2, next) => {
|
|
54725
|
+
const decision = decideNetworkAccess(store.get().server, getRemoteAddressFromContext(c2));
|
|
54726
|
+
if (!decision.allowed) {
|
|
54727
|
+
return c2.json({
|
|
54728
|
+
error: decision.reason === "lan-disabled" ? "\u5C40\u57DF\u7F51\u670D\u52A1\u672A\u5F00\u542F\uFF0C\u5DF2\u62D2\u7EDD\u975E\u672C\u673A\u8BF7\u6C42" : "\u4EC5\u5141\u8BB8\u672C\u673A\u6216\u5C40\u57DF\u7F51\u6765\u6E90\u8BBF\u95EE",
|
|
54729
|
+
remoteAddress: decision.remoteAddress
|
|
54730
|
+
}, 403);
|
|
54731
|
+
}
|
|
54732
|
+
await next();
|
|
54733
|
+
};
|
|
54734
|
+
}
|
|
54735
|
+
|
|
54736
|
+
// src/log-realtime.ts
|
|
54737
|
+
var WS_PATHNAME = "/api/logs/events/ws";
|
|
54738
|
+
var MAX_CONNECTIONS = 64;
|
|
54739
|
+
var MAX_SUBSCRIPTIONS = 64;
|
|
54740
|
+
var MAX_CLIENT_MESSAGE_BYTES = 16 * 1024;
|
|
54741
|
+
var MAX_GLOBAL_EVENT_QUEUE_ITEMS = 1000;
|
|
54742
|
+
var MAX_GLOBAL_EVENT_QUEUE_BYTES = 16 * 1024 * 1024;
|
|
54743
|
+
var MAX_PENDING_BYTES_PER_CONNECTION = 1024 * 1024;
|
|
54744
|
+
var MAX_TOTAL_PENDING_BYTES = 32 * 1024 * 1024;
|
|
54745
|
+
var BACKPRESSURE_BUFFERED_BYTES = 512 * 1024;
|
|
54746
|
+
var HEARTBEAT_INTERVAL_MS = 15000;
|
|
54747
|
+
var FLUSH_BUDGET_MS = 10;
|
|
54748
|
+
var MAX_Q_LENGTH2 = 200;
|
|
54749
|
+
function jsonResponse(status, body) {
|
|
54750
|
+
return new Response(JSON.stringify(body), {
|
|
54751
|
+
status,
|
|
54752
|
+
headers: { "Content-Type": "application/json; charset=utf-8" }
|
|
54753
|
+
});
|
|
54754
|
+
}
|
|
54755
|
+
function createId(prefix) {
|
|
54756
|
+
return `${prefix}_${crypto.randomUUID()}`;
|
|
54757
|
+
}
|
|
54758
|
+
function estimatePublishedEventBytes(event) {
|
|
54759
|
+
const bodyBytes = (event.event.request_bytes ?? 0) + (event.event.response_bytes ?? 0);
|
|
54760
|
+
const streamBytes = event.event.stream_bytes ?? 0;
|
|
54761
|
+
return 1024 + Math.min(64 * 1024, Math.max(0, bodyBytes + streamBytes));
|
|
54762
|
+
}
|
|
54763
|
+
function byteLength(text2) {
|
|
54764
|
+
return Buffer.byteLength(text2);
|
|
54765
|
+
}
|
|
54766
|
+
function isSocketOpen(ws) {
|
|
54767
|
+
return typeof ws.readyState !== "number" || ws.readyState === 1;
|
|
54768
|
+
}
|
|
54769
|
+
function bufferedAmount(ws) {
|
|
54770
|
+
return typeof ws.bufferedAmount === "number" ? ws.bufferedAmount : 0;
|
|
54771
|
+
}
|
|
54772
|
+
function parseClientMessage(raw2) {
|
|
54773
|
+
let text2;
|
|
54774
|
+
if (typeof raw2 === "string") {
|
|
54775
|
+
text2 = raw2;
|
|
54776
|
+
} else {
|
|
54777
|
+
text2 = new TextDecoder().decode(raw2);
|
|
54778
|
+
}
|
|
54779
|
+
if (byteLength(text2) > MAX_CLIENT_MESSAGE_BYTES) {
|
|
54780
|
+
throw new Error("\u5BA2\u6237\u7AEF\u6D88\u606F\u8FC7\u5927");
|
|
54781
|
+
}
|
|
54782
|
+
const parsed = JSON.parse(text2);
|
|
54783
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
54784
|
+
throw new Error("\u5BA2\u6237\u7AEF\u6D88\u606F\u5FC5\u987B\u662F JSON \u5BF9\u8C61");
|
|
54785
|
+
}
|
|
54786
|
+
const message = parsed;
|
|
54787
|
+
if (message.type !== "subscribe" && message.type !== "unsubscribe" && message.type !== "ping") {
|
|
54788
|
+
throw new Error("\u4E0D\u652F\u6301\u7684\u5B9E\u65F6\u65E5\u5FD7\u6D88\u606F\u7C7B\u578B");
|
|
54789
|
+
}
|
|
54790
|
+
return message;
|
|
54791
|
+
}
|
|
54792
|
+
function parseStringValue(value) {
|
|
54793
|
+
if (typeof value !== "string")
|
|
54794
|
+
return;
|
|
54795
|
+
const trimmed = value.trim();
|
|
54796
|
+
return trimmed || undefined;
|
|
54797
|
+
}
|
|
54798
|
+
function parseStringArrayValue(value) {
|
|
54799
|
+
if (Array.isArray(value)) {
|
|
54800
|
+
return value.flatMap((item) => typeof item === "string" ? parseCommaSeparated(item) : []);
|
|
54801
|
+
}
|
|
54802
|
+
if (typeof value === "string")
|
|
54803
|
+
return parseCommaSeparated(value);
|
|
54804
|
+
return [];
|
|
54805
|
+
}
|
|
54806
|
+
function parseBooleanValue(value) {
|
|
54807
|
+
if (typeof value === "boolean")
|
|
54808
|
+
return value;
|
|
54809
|
+
if (typeof value === "string")
|
|
54810
|
+
return parseBooleanFlag(value);
|
|
54811
|
+
return null;
|
|
54812
|
+
}
|
|
54813
|
+
function createQueryHash(value) {
|
|
54814
|
+
const text2 = JSON.stringify(value);
|
|
54815
|
+
let hash2 = 2166136261;
|
|
54816
|
+
for (let i = 0;i < text2.length; i += 1) {
|
|
54817
|
+
hash2 ^= text2.charCodeAt(i);
|
|
54818
|
+
hash2 = Math.imul(hash2, 16777619);
|
|
54819
|
+
}
|
|
54820
|
+
return `q_${(hash2 >>> 0).toString(36)}`;
|
|
54821
|
+
}
|
|
54822
|
+
function parseValidatedArray(value, validate, errorMessage) {
|
|
54823
|
+
const raw2 = parseStringArrayValue(value);
|
|
54824
|
+
const parsed = raw2.filter(validate);
|
|
54825
|
+
if (parsed.length !== raw2.length) {
|
|
54826
|
+
throw new Error(errorMessage);
|
|
54827
|
+
}
|
|
54828
|
+
return parsed;
|
|
54829
|
+
}
|
|
54830
|
+
function compileRealtimeQuery(input) {
|
|
54831
|
+
const query = input ?? {};
|
|
54832
|
+
const nowMs = Date.now();
|
|
54833
|
+
const windowRaw = parseStringValue(query.window) ?? "24h";
|
|
54834
|
+
if (!isLogQueryWindow(windowRaw)) {
|
|
54835
|
+
throw new Error("window \u53C2\u6570\u4EC5\u652F\u6301 1h | 6h | 24h");
|
|
54836
|
+
}
|
|
54837
|
+
const from = parseStringValue(query.from);
|
|
54838
|
+
const to = parseStringValue(query.to);
|
|
54839
|
+
const range = from || to ? {
|
|
54840
|
+
type: "fixed",
|
|
54841
|
+
...resolveLogQueryRange({ window: windowRaw, from, to, nowMs })
|
|
54842
|
+
} : {
|
|
54843
|
+
type: "window",
|
|
54844
|
+
window: windowRaw,
|
|
54845
|
+
windowMs: getLogQueryWindowMs(windowRaw)
|
|
54846
|
+
};
|
|
54847
|
+
if (range.type === "fixed" && range.toMs <= nowMs) {
|
|
54848
|
+
throw new Error("\u56FA\u5B9A\u7ED3\u675F\u65F6\u95F4\u5DF2\u8FC7\u671F\uFF0C\u5B9E\u65F6\u63A8\u9001\u4EC5\u652F\u6301\u4ECD\u5728\u6EDA\u52A8\u7684\u67E5\u8BE2\u7A97\u53E3");
|
|
54849
|
+
}
|
|
54850
|
+
const sortRaw = parseStringValue(query.sort) ?? "time_desc";
|
|
54851
|
+
if (!validateSort(sortRaw)) {
|
|
54852
|
+
throw new Error("sort \u53C2\u6570\u4EC5\u652F\u6301 time_desc | time_asc");
|
|
54853
|
+
}
|
|
54854
|
+
const levels = parseValidatedArray(query.levels, validateLogLevel, "levels \u53C2\u6570\u4EC5\u652F\u6301 info,error");
|
|
54855
|
+
const statusClasses = parseValidatedArray(query.statusClass, validateStatusClass, "statusClass \u53C2\u6570\u4EC5\u652F\u6301 2xx,4xx,5xx,network_error");
|
|
54856
|
+
const q = (parseStringValue(query.q) ?? "").slice(0, MAX_Q_LENGTH2).toLowerCase();
|
|
54857
|
+
const normalized = {
|
|
54858
|
+
range,
|
|
54859
|
+
sort: sortRaw,
|
|
54860
|
+
levels,
|
|
54861
|
+
providers: parseStringArrayValue(query.provider),
|
|
54862
|
+
routeTypes: parseStringArrayValue(query.routeType),
|
|
54863
|
+
models: parseStringArrayValue(query.model),
|
|
54864
|
+
modelIns: parseStringArrayValue(query.modelIn),
|
|
54865
|
+
modelOuts: parseStringArrayValue(query.modelOut),
|
|
54866
|
+
users: parseStringArrayValue(query.user),
|
|
54867
|
+
sessions: parseStringArrayValue(query.session),
|
|
54868
|
+
statusClasses,
|
|
54869
|
+
hasError: parseBooleanValue(query.hasError),
|
|
54870
|
+
q
|
|
54871
|
+
};
|
|
54872
|
+
return {
|
|
54873
|
+
queryHash: createQueryHash(normalized),
|
|
54874
|
+
sort: sortRaw,
|
|
54875
|
+
range,
|
|
54876
|
+
levels: new Set(levels),
|
|
54877
|
+
providers: new Set(normalized.providers),
|
|
54878
|
+
routeTypes: new Set(normalized.routeTypes),
|
|
54879
|
+
models: new Set(normalized.models),
|
|
54880
|
+
modelIns: new Set(normalized.modelIns),
|
|
54881
|
+
modelOuts: new Set(normalized.modelOuts),
|
|
54882
|
+
users: new Set(normalized.users),
|
|
54883
|
+
sessions: new Set(normalized.sessions),
|
|
54884
|
+
statusClasses: new Set(statusClasses),
|
|
54885
|
+
hasError: normalized.hasError,
|
|
54886
|
+
q
|
|
54887
|
+
};
|
|
54888
|
+
}
|
|
54889
|
+
function setHas(set2, value) {
|
|
54890
|
+
return set2.size === 0 || set2.has(value);
|
|
54891
|
+
}
|
|
54892
|
+
function matchesCompiledQuery(query, facts, nowMs) {
|
|
54893
|
+
if (!facts.event.ts_start || !Number.isFinite(facts.ts))
|
|
54894
|
+
return false;
|
|
54895
|
+
const fromMs = query.range.type === "window" ? nowMs - query.range.windowMs : query.range.fromMs;
|
|
54896
|
+
const toMs = query.range.type === "window" ? nowMs : query.range.toMs;
|
|
54897
|
+
if (facts.ts < fromMs || facts.ts > toMs)
|
|
54898
|
+
return false;
|
|
54899
|
+
if (!setHas(query.levels, facts.level))
|
|
54900
|
+
return false;
|
|
54901
|
+
if (!setHas(query.providers, facts.event.provider))
|
|
54902
|
+
return false;
|
|
54903
|
+
if (!setHas(query.routeTypes, facts.event.route_type))
|
|
54904
|
+
return false;
|
|
54905
|
+
if (!setHas(query.models, facts.model))
|
|
54906
|
+
return false;
|
|
54907
|
+
if (!setHas(query.modelIns, facts.event.model_in))
|
|
54908
|
+
return false;
|
|
54909
|
+
if (!setHas(query.modelOuts, facts.event.model_out))
|
|
54910
|
+
return false;
|
|
54911
|
+
if (query.users.size > 0) {
|
|
54912
|
+
const matchedByRaw = facts.identity.userIdRaw ? query.users.has(facts.identity.userIdRaw) : false;
|
|
54913
|
+
const matchedByUserKey = facts.identity.userKey ? query.users.has(facts.identity.userKey) : false;
|
|
54914
|
+
if (!matchedByRaw && !matchedByUserKey)
|
|
54915
|
+
return false;
|
|
54916
|
+
}
|
|
54917
|
+
if (query.sessions.size > 0) {
|
|
54918
|
+
if (!facts.identity.sessionId || !query.sessions.has(facts.identity.sessionId))
|
|
54919
|
+
return false;
|
|
54920
|
+
}
|
|
54921
|
+
if (!setHas(query.statusClasses, facts.statusClass))
|
|
54922
|
+
return false;
|
|
54923
|
+
if (query.hasError !== null && query.hasError !== facts.hasError)
|
|
54924
|
+
return false;
|
|
54925
|
+
return !query.q || facts.keywordText.includes(query.q);
|
|
54926
|
+
}
|
|
54927
|
+
function createLogRealtimeRuntime(options) {
|
|
54928
|
+
const connections = new Map;
|
|
54929
|
+
const subscriptions = new Map;
|
|
54930
|
+
const eventQueue = [];
|
|
54931
|
+
let tailUnsubscribe = null;
|
|
54932
|
+
let flushTimer = null;
|
|
54933
|
+
let heartbeatTimer = null;
|
|
54934
|
+
let eventQueueBytes = 0;
|
|
54935
|
+
let totalPendingBytes = 0;
|
|
54936
|
+
let droppedUpstreamEvents = 0;
|
|
54937
|
+
const sendText = (connection, text2) => {
|
|
54938
|
+
if (!isSocketOpen(connection.ws))
|
|
54939
|
+
return false;
|
|
54940
|
+
const bytes = byteLength(text2);
|
|
54941
|
+
const shouldQueue = connection.queue.length > 0 || bufferedAmount(connection.ws) > BACKPRESSURE_BUFFERED_BYTES;
|
|
54942
|
+
if (!shouldQueue) {
|
|
54943
|
+
try {
|
|
54944
|
+
connection.ws.send(text2);
|
|
54945
|
+
return true;
|
|
54946
|
+
} catch {
|
|
54947
|
+
removeConnection(connection.id, "send-failed");
|
|
54948
|
+
return false;
|
|
54949
|
+
}
|
|
54950
|
+
}
|
|
54951
|
+
if (connection.pendingBytes + bytes > MAX_PENDING_BYTES_PER_CONNECTION || totalPendingBytes + bytes > MAX_TOTAL_PENDING_BYTES) {
|
|
54952
|
+
connection.droppedOutbound += 1;
|
|
54953
|
+
if (connection.droppedOutbound >= 3) {
|
|
54954
|
+
removeConnection(connection.id, "slow-client");
|
|
54955
|
+
}
|
|
54956
|
+
return false;
|
|
54957
|
+
}
|
|
54958
|
+
connection.queue.push({ text: text2, bytes });
|
|
54959
|
+
connection.pendingBytes += bytes;
|
|
54960
|
+
totalPendingBytes += bytes;
|
|
54961
|
+
return true;
|
|
54962
|
+
};
|
|
54963
|
+
const sendMessage = (connection, message) => {
|
|
54964
|
+
return sendText(connection, JSON.stringify(message));
|
|
54965
|
+
};
|
|
54966
|
+
const flushConnectionQueue = (connection) => {
|
|
54967
|
+
if (!isSocketOpen(connection.ws))
|
|
54968
|
+
return;
|
|
54969
|
+
while (connection.queue.length > 0 && bufferedAmount(connection.ws) <= BACKPRESSURE_BUFFERED_BYTES) {
|
|
54970
|
+
const item = connection.queue.shift();
|
|
54971
|
+
if (!item)
|
|
54972
|
+
break;
|
|
54973
|
+
connection.pendingBytes -= item.bytes;
|
|
54974
|
+
totalPendingBytes -= item.bytes;
|
|
54975
|
+
try {
|
|
54976
|
+
connection.ws.send(item.text);
|
|
54977
|
+
} catch {
|
|
54978
|
+
removeConnection(connection.id, "send-failed");
|
|
54979
|
+
return;
|
|
54980
|
+
}
|
|
54981
|
+
}
|
|
54982
|
+
};
|
|
54983
|
+
const sendError = (connection, error48, requestId) => {
|
|
54984
|
+
sendMessage(connection, { type: "error", requestId, error: error48 });
|
|
54985
|
+
};
|
|
54986
|
+
const ensureTailSubscription = () => {
|
|
54987
|
+
if (tailUnsubscribe || subscriptions.size === 0)
|
|
54988
|
+
return;
|
|
54989
|
+
tailUnsubscribe = subscribeLogEvents((event) => {
|
|
54990
|
+
if (subscriptions.size === 0)
|
|
54991
|
+
return;
|
|
54992
|
+
const bytes = estimatePublishedEventBytes(event);
|
|
54993
|
+
while (eventQueue.length >= MAX_GLOBAL_EVENT_QUEUE_ITEMS || eventQueueBytes + bytes > MAX_GLOBAL_EVENT_QUEUE_BYTES) {
|
|
54994
|
+
const dropped = eventQueue.shift();
|
|
54995
|
+
if (!dropped)
|
|
54996
|
+
break;
|
|
54997
|
+
eventQueueBytes -= dropped.bytes;
|
|
54998
|
+
droppedUpstreamEvents += 1;
|
|
54999
|
+
}
|
|
55000
|
+
eventQueue.push({ event, bytes });
|
|
55001
|
+
eventQueueBytes += bytes;
|
|
55002
|
+
scheduleFlush();
|
|
55003
|
+
});
|
|
55004
|
+
};
|
|
55005
|
+
const maybeStopTailSubscription = () => {
|
|
55006
|
+
if (subscriptions.size > 0)
|
|
55007
|
+
return;
|
|
55008
|
+
tailUnsubscribe?.();
|
|
55009
|
+
tailUnsubscribe = null;
|
|
55010
|
+
eventQueue.length = 0;
|
|
55011
|
+
eventQueueBytes = 0;
|
|
55012
|
+
droppedUpstreamEvents = 0;
|
|
55013
|
+
};
|
|
55014
|
+
const sendOverflow = (subscription, dropped, message) => {
|
|
55015
|
+
const connection = connections.get(subscription.connectionId);
|
|
55016
|
+
if (!connection)
|
|
55017
|
+
return;
|
|
55018
|
+
sendMessage(connection, {
|
|
55019
|
+
type: "overflow",
|
|
55020
|
+
subscriptionId: subscription.id,
|
|
55021
|
+
dropped,
|
|
55022
|
+
message
|
|
55023
|
+
});
|
|
55024
|
+
};
|
|
55025
|
+
const removeSubscription = (subscriptionId, reason, notify2 = true) => {
|
|
55026
|
+
const subscription = subscriptions.get(subscriptionId);
|
|
55027
|
+
if (!subscription)
|
|
55028
|
+
return;
|
|
55029
|
+
subscriptions.delete(subscriptionId);
|
|
55030
|
+
const connection = connections.get(subscription.connectionId);
|
|
55031
|
+
if (connection) {
|
|
55032
|
+
connection.subscriptions.delete(subscriptionId);
|
|
55033
|
+
if (notify2) {
|
|
55034
|
+
sendMessage(connection, {
|
|
55035
|
+
type: "unsubscribed",
|
|
55036
|
+
subscriptionId,
|
|
55037
|
+
reason
|
|
55038
|
+
});
|
|
55039
|
+
}
|
|
55040
|
+
}
|
|
55041
|
+
maybeStopTailSubscription();
|
|
55042
|
+
};
|
|
55043
|
+
function removeConnection(connectionId, reason) {
|
|
55044
|
+
const connection = connections.get(connectionId);
|
|
55045
|
+
if (!connection)
|
|
55046
|
+
return;
|
|
55047
|
+
connections.delete(connectionId);
|
|
55048
|
+
for (const subscriptionId of Array.from(connection.subscriptions)) {
|
|
55049
|
+
removeSubscription(subscriptionId, reason, false);
|
|
55050
|
+
}
|
|
55051
|
+
for (const item of connection.queue) {
|
|
55052
|
+
totalPendingBytes -= item.bytes;
|
|
55053
|
+
}
|
|
55054
|
+
connection.queue.length = 0;
|
|
55055
|
+
connection.pendingBytes = 0;
|
|
55056
|
+
try {
|
|
55057
|
+
connection.ws.close(1001, reason);
|
|
55058
|
+
} catch {}
|
|
55059
|
+
if (connections.size === 0 && heartbeatTimer) {
|
|
55060
|
+
clearInterval(heartbeatTimer);
|
|
55061
|
+
heartbeatTimer = null;
|
|
55062
|
+
}
|
|
55063
|
+
maybeStopTailSubscription();
|
|
55064
|
+
}
|
|
55065
|
+
const pruneExpiredSubscriptions = (nowMs) => {
|
|
55066
|
+
for (const subscription of Array.from(subscriptions.values())) {
|
|
55067
|
+
const range = subscription.query.range;
|
|
55068
|
+
if (range.type === "fixed" && range.toMs <= nowMs) {
|
|
55069
|
+
removeSubscription(subscription.id, "expired");
|
|
55070
|
+
}
|
|
55071
|
+
}
|
|
55072
|
+
};
|
|
55073
|
+
const flushEvents = () => {
|
|
55074
|
+
flushTimer = null;
|
|
55075
|
+
if (subscriptions.size === 0) {
|
|
55076
|
+
eventQueue.length = 0;
|
|
55077
|
+
eventQueueBytes = 0;
|
|
55078
|
+
droppedUpstreamEvents = 0;
|
|
55079
|
+
maybeStopTailSubscription();
|
|
55080
|
+
return;
|
|
55081
|
+
}
|
|
55082
|
+
const startedAt = Date.now();
|
|
55083
|
+
const nowMs = Date.now();
|
|
55084
|
+
pruneExpiredSubscriptions(nowMs);
|
|
55085
|
+
if (droppedUpstreamEvents > 0) {
|
|
55086
|
+
const dropped = droppedUpstreamEvents;
|
|
55087
|
+
droppedUpstreamEvents = 0;
|
|
55088
|
+
for (const subscription of subscriptions.values()) {
|
|
55089
|
+
sendOverflow(subscription, dropped, `\u5B9E\u65F6\u65E5\u5FD7\u961F\u5217\u5DF2\u4E22\u5F03 ${dropped} \u6761\u4E8B\u4EF6\uFF0C\u8BF7\u91CD\u65B0\u67E5\u8BE2\u4EE5\u8865\u9F50\u3002`);
|
|
55090
|
+
}
|
|
55091
|
+
}
|
|
55092
|
+
while (eventQueue.length > 0 && Date.now() - startedAt < FLUSH_BUDGET_MS) {
|
|
55093
|
+
const queued = eventQueue.shift();
|
|
55094
|
+
if (!queued)
|
|
55095
|
+
break;
|
|
55096
|
+
eventQueueBytes -= queued.bytes;
|
|
55097
|
+
const facts = extractLogEventFacts(queued.event.event);
|
|
55098
|
+
let summary = null;
|
|
55099
|
+
for (const subscription of subscriptions.values()) {
|
|
55100
|
+
if (!matchesCompiledQuery(subscription.query, facts, Date.now()))
|
|
55101
|
+
continue;
|
|
55102
|
+
const connection = connections.get(subscription.connectionId);
|
|
55103
|
+
if (!connection)
|
|
55104
|
+
continue;
|
|
55105
|
+
summary ??= createLogEventSummaryFromFacts(facts, {
|
|
55106
|
+
id: queued.event.id,
|
|
55107
|
+
date: queued.event.date,
|
|
55108
|
+
line: null
|
|
55109
|
+
});
|
|
55110
|
+
const sent = sendMessage(connection, {
|
|
55111
|
+
type: "log.event",
|
|
55112
|
+
subscriptionId: subscription.id,
|
|
55113
|
+
item: summary
|
|
55114
|
+
});
|
|
55115
|
+
if (!sent) {
|
|
55116
|
+
sendOverflow(subscription, 1, "\u5B9E\u65F6\u65E5\u5FD7\u5BA2\u6237\u7AEF\u53D1\u9001\u961F\u5217\u5DF2\u6EE1\uFF0C\u8BF7\u91CD\u65B0\u67E5\u8BE2\u4EE5\u8865\u9F50\u3002");
|
|
55117
|
+
}
|
|
55118
|
+
}
|
|
55119
|
+
}
|
|
55120
|
+
for (const connection of connections.values()) {
|
|
55121
|
+
flushConnectionQueue(connection);
|
|
55122
|
+
}
|
|
55123
|
+
if (eventQueue.length > 0)
|
|
55124
|
+
scheduleFlush();
|
|
55125
|
+
};
|
|
55126
|
+
function scheduleFlush() {
|
|
55127
|
+
if (flushTimer)
|
|
55128
|
+
return;
|
|
55129
|
+
flushTimer = setTimeout(flushEvents, 0);
|
|
55130
|
+
flushTimer.unref?.();
|
|
55131
|
+
}
|
|
55132
|
+
const ensureHeartbeat = () => {
|
|
55133
|
+
if (heartbeatTimer)
|
|
55134
|
+
return;
|
|
55135
|
+
heartbeatTimer = setInterval(() => {
|
|
55136
|
+
const ts = new Date().toISOString();
|
|
55137
|
+
pruneExpiredSubscriptions(Date.now());
|
|
55138
|
+
for (const connection of connections.values()) {
|
|
55139
|
+
sendMessage(connection, { type: "pong", ts });
|
|
55140
|
+
flushConnectionQueue(connection);
|
|
55141
|
+
}
|
|
55142
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
55143
|
+
heartbeatTimer.unref?.();
|
|
55144
|
+
};
|
|
55145
|
+
const subscribeConnection = (connection, requestId, query) => {
|
|
55146
|
+
if (subscriptions.size >= MAX_SUBSCRIPTIONS && connection.subscriptions.size === 0) {
|
|
55147
|
+
sendError(connection, "\u5B9E\u65F6\u65E5\u5FD7\u8BA2\u9605\u6570\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5", requestId);
|
|
55148
|
+
return;
|
|
55149
|
+
}
|
|
55150
|
+
let compiled;
|
|
55151
|
+
try {
|
|
55152
|
+
compiled = compileRealtimeQuery(query);
|
|
55153
|
+
} catch (err) {
|
|
55154
|
+
sendError(connection, err instanceof Error ? err.message : String(err), requestId);
|
|
55155
|
+
return;
|
|
55156
|
+
}
|
|
55157
|
+
for (const subscriptionId2 of Array.from(connection.subscriptions)) {
|
|
55158
|
+
removeSubscription(subscriptionId2, "replaced");
|
|
55159
|
+
}
|
|
55160
|
+
const subscriptionId = createId("sub");
|
|
55161
|
+
const subscription = {
|
|
55162
|
+
id: subscriptionId,
|
|
55163
|
+
connectionId: connection.id,
|
|
55164
|
+
query: compiled
|
|
55165
|
+
};
|
|
55166
|
+
subscriptions.set(subscriptionId, subscription);
|
|
55167
|
+
connection.subscriptions.add(subscriptionId);
|
|
55168
|
+
ensureTailSubscription();
|
|
55169
|
+
sendMessage(connection, {
|
|
55170
|
+
type: "subscribed",
|
|
55171
|
+
requestId: requestId ?? null,
|
|
55172
|
+
subscriptionId,
|
|
55173
|
+
queryHash: compiled.queryHash,
|
|
55174
|
+
now: new Date().toISOString()
|
|
55175
|
+
});
|
|
55176
|
+
};
|
|
55177
|
+
const openConnection = (ws) => {
|
|
55178
|
+
const data = ws.data;
|
|
55179
|
+
const connection = {
|
|
55180
|
+
id: data.connectionId,
|
|
55181
|
+
remoteAddress: data.remoteAddress,
|
|
55182
|
+
ws,
|
|
55183
|
+
subscriptions: new Set,
|
|
55184
|
+
queue: [],
|
|
55185
|
+
pendingBytes: 0,
|
|
55186
|
+
droppedOutbound: 0
|
|
55187
|
+
};
|
|
55188
|
+
connections.set(connection.id, connection);
|
|
55189
|
+
ensureHeartbeat();
|
|
55190
|
+
sendMessage(connection, {
|
|
55191
|
+
type: "ready",
|
|
55192
|
+
connectionId: connection.id,
|
|
55193
|
+
now: new Date().toISOString()
|
|
55194
|
+
});
|
|
55195
|
+
};
|
|
55196
|
+
const handleMessage = (ws, raw2) => {
|
|
55197
|
+
const connection = connections.get(ws.data.connectionId);
|
|
55198
|
+
if (!connection)
|
|
55199
|
+
return;
|
|
55200
|
+
let message;
|
|
55201
|
+
try {
|
|
55202
|
+
message = parseClientMessage(raw2);
|
|
55203
|
+
} catch (err) {
|
|
55204
|
+
sendError(connection, err instanceof Error ? err.message : String(err));
|
|
55205
|
+
return;
|
|
55206
|
+
}
|
|
55207
|
+
if (message.type === "ping") {
|
|
55208
|
+
sendMessage(connection, { type: "pong", ts: message.ts ?? new Date().toISOString() });
|
|
55209
|
+
return;
|
|
55210
|
+
}
|
|
55211
|
+
if (message.type === "unsubscribe") {
|
|
55212
|
+
if (message.subscriptionId) {
|
|
55213
|
+
removeSubscription(message.subscriptionId, "client-unsubscribe");
|
|
55214
|
+
} else {
|
|
55215
|
+
for (const subscriptionId of Array.from(connection.subscriptions)) {
|
|
55216
|
+
removeSubscription(subscriptionId, "client-unsubscribe");
|
|
55217
|
+
}
|
|
55218
|
+
}
|
|
55219
|
+
return;
|
|
55220
|
+
}
|
|
55221
|
+
subscribeConnection(connection, message.requestId, message.query);
|
|
55222
|
+
};
|
|
55223
|
+
return {
|
|
55224
|
+
pathname: WS_PATHNAME,
|
|
55225
|
+
upgrade: (request, server, remoteAddress) => {
|
|
55226
|
+
const url2 = new URL(request.url);
|
|
55227
|
+
if (url2.pathname !== WS_PATHNAME)
|
|
55228
|
+
return { handled: false };
|
|
55229
|
+
const upgradeHeader = request.headers.get("upgrade")?.toLowerCase();
|
|
55230
|
+
if (upgradeHeader !== "websocket") {
|
|
55231
|
+
return {
|
|
55232
|
+
handled: true,
|
|
55233
|
+
response: jsonResponse(400, { error: "\u9700\u8981 WebSocket Upgrade" })
|
|
55234
|
+
};
|
|
55235
|
+
}
|
|
55236
|
+
const decision = decideNetworkAccess(options.store.get().server, remoteAddress);
|
|
55237
|
+
if (!decision.allowed) {
|
|
55238
|
+
return {
|
|
55239
|
+
handled: true,
|
|
55240
|
+
response: jsonResponse(403, {
|
|
55241
|
+
error: decision.reason === "lan-disabled" ? "\u5C40\u57DF\u7F51\u670D\u52A1\u672A\u5F00\u542F\uFF0C\u5DF2\u62D2\u7EDD\u975E\u672C\u673A\u8BF7\u6C42" : "\u4EC5\u5141\u8BB8\u672C\u673A\u6216\u5C40\u57DF\u7F51\u6765\u6E90\u8BBF\u95EE",
|
|
55242
|
+
remoteAddress: decision.remoteAddress
|
|
55243
|
+
})
|
|
55244
|
+
};
|
|
55245
|
+
}
|
|
55246
|
+
if (connections.size >= MAX_CONNECTIONS) {
|
|
55247
|
+
return {
|
|
55248
|
+
handled: true,
|
|
55249
|
+
response: jsonResponse(503, { error: "\u5B9E\u65F6\u65E5\u5FD7\u8FDE\u63A5\u6570\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" })
|
|
55250
|
+
};
|
|
55251
|
+
}
|
|
55252
|
+
const upgraded = server.upgrade(request, {
|
|
55253
|
+
data: {
|
|
55254
|
+
kind: "log-realtime",
|
|
55255
|
+
connectionId: createId("conn"),
|
|
55256
|
+
remoteAddress: decision.remoteAddress
|
|
55257
|
+
}
|
|
55258
|
+
});
|
|
55259
|
+
return upgraded ? { handled: true, upgraded: true } : { handled: true, response: jsonResponse(400, { error: "WebSocket Upgrade \u5931\u8D25" }) };
|
|
55260
|
+
},
|
|
55261
|
+
websocket: {
|
|
55262
|
+
open: openConnection,
|
|
55263
|
+
message: handleMessage,
|
|
55264
|
+
drain: (ws) => {
|
|
55265
|
+
const connection = connections.get(ws.data.connectionId);
|
|
55266
|
+
if (connection)
|
|
55267
|
+
flushConnectionQueue(connection);
|
|
55268
|
+
},
|
|
55269
|
+
close: (ws) => {
|
|
55270
|
+
removeConnection(ws.data.connectionId, "closed");
|
|
55271
|
+
}
|
|
55272
|
+
},
|
|
55273
|
+
dispose: () => {
|
|
55274
|
+
if (flushTimer) {
|
|
55275
|
+
clearTimeout(flushTimer);
|
|
55276
|
+
flushTimer = null;
|
|
55277
|
+
}
|
|
55278
|
+
if (heartbeatTimer) {
|
|
55279
|
+
clearInterval(heartbeatTimer);
|
|
55280
|
+
heartbeatTimer = null;
|
|
55281
|
+
}
|
|
55282
|
+
tailUnsubscribe?.();
|
|
55283
|
+
tailUnsubscribe = null;
|
|
55284
|
+
for (const connectionId of Array.from(connections.keys())) {
|
|
55285
|
+
removeConnection(connectionId, "server-dispose");
|
|
55286
|
+
}
|
|
55287
|
+
eventQueue.length = 0;
|
|
55288
|
+
eventQueueBytes = 0;
|
|
55289
|
+
totalPendingBytes = 0;
|
|
55290
|
+
droppedUpstreamEvents = 0;
|
|
55291
|
+
}
|
|
55292
|
+
};
|
|
55293
|
+
}
|
|
55294
|
+
|
|
54406
55295
|
// src/log-sessions.ts
|
|
54407
|
-
import { createReadStream as createReadStream4, existsSync as
|
|
54408
|
-
import { join as
|
|
55296
|
+
import { createReadStream as createReadStream4, existsSync as existsSync7 } from "fs";
|
|
55297
|
+
import { join as join8 } from "path";
|
|
54409
55298
|
import { createInterface as createInterface3 } from "readline";
|
|
54410
55299
|
var MAX_LINES_SCANNED3 = 250000;
|
|
54411
|
-
var
|
|
55300
|
+
var MAX_Q_LENGTH3 = 200;
|
|
54412
55301
|
function toDayStart4(ms) {
|
|
54413
55302
|
const date5 = new Date(ms);
|
|
54414
55303
|
return Date.UTC(date5.getUTCFullYear(), date5.getUTCMonth(), date5.getUTCDate());
|
|
@@ -54427,7 +55316,7 @@ function normalizeInput(input) {
|
|
|
54427
55316
|
toMs: input.toMs,
|
|
54428
55317
|
users: (input.users ?? []).map((item) => item.trim()).filter(Boolean),
|
|
54429
55318
|
sessions: (input.sessions ?? []).map((item) => item.trim()).filter(Boolean),
|
|
54430
|
-
q: qRaw.length >
|
|
55319
|
+
q: qRaw.length > MAX_Q_LENGTH3 ? qRaw.slice(0, MAX_Q_LENGTH3) : qRaw
|
|
54431
55320
|
};
|
|
54432
55321
|
}
|
|
54433
55322
|
function incrementCount(map2, key2) {
|
|
@@ -54526,8 +55415,8 @@ async function queryLogSessions(context2, input) {
|
|
|
54526
55415
|
return createEmptyResult(normalized.fromMs, normalized.toMs);
|
|
54527
55416
|
}
|
|
54528
55417
|
const baseDir = resolveLogBaseDir(context2.logConfig);
|
|
54529
|
-
const eventsDir =
|
|
54530
|
-
if (!
|
|
55418
|
+
const eventsDir = join8(baseDir, "events");
|
|
55419
|
+
if (!existsSync7(eventsDir)) {
|
|
54531
55420
|
return createEmptyResult(normalized.fromMs, normalized.toMs);
|
|
54532
55421
|
}
|
|
54533
55422
|
const usersMap = new Map;
|
|
@@ -54545,8 +55434,8 @@ async function queryLogSessions(context2, input) {
|
|
|
54545
55434
|
truncated = true;
|
|
54546
55435
|
break;
|
|
54547
55436
|
}
|
|
54548
|
-
const filePath =
|
|
54549
|
-
if (!
|
|
55437
|
+
const filePath = join8(eventsDir, `${date5}.jsonl`);
|
|
55438
|
+
if (!existsSync7(filePath))
|
|
54550
55439
|
continue;
|
|
54551
55440
|
scannedFiles += 1;
|
|
54552
55441
|
const stream = createReadStream4(filePath, { encoding: "utf-8" });
|
|
@@ -54654,8 +55543,8 @@ async function queryLogSessions(context2, input) {
|
|
|
54654
55543
|
}
|
|
54655
55544
|
|
|
54656
55545
|
// src/log-storage.ts
|
|
54657
|
-
import { existsSync as
|
|
54658
|
-
import { join as
|
|
55546
|
+
import { existsSync as existsSync8, promises as fsPromises } from "fs";
|
|
55547
|
+
import { join as join9 } from "path";
|
|
54659
55548
|
var cachedStorage = null;
|
|
54660
55549
|
var calculationPromise = null;
|
|
54661
55550
|
var lastCalculationTime = 0;
|
|
@@ -54663,7 +55552,7 @@ var CACHE_TTL_MS2 = 60 * 60 * 1000;
|
|
|
54663
55552
|
var CALCULATION_INTERVAL_MS = 60 * 60 * 1000;
|
|
54664
55553
|
var MIN_CALCULATION_INTERVAL_MS = 5 * 60 * 1000;
|
|
54665
55554
|
async function calculateDirSize(dirPath) {
|
|
54666
|
-
if (!
|
|
55555
|
+
if (!existsSync8(dirPath)) {
|
|
54667
55556
|
return { bytes: 0, fileCount: 0 };
|
|
54668
55557
|
}
|
|
54669
55558
|
let bytes = 0;
|
|
@@ -54672,7 +55561,7 @@ async function calculateDirSize(dirPath) {
|
|
|
54672
55561
|
try {
|
|
54673
55562
|
const entries = await fsPromises.readdir(currentPath, { withFileTypes: true });
|
|
54674
55563
|
for (const entry of entries) {
|
|
54675
|
-
const fullPath =
|
|
55564
|
+
const fullPath = join9(currentPath, entry.name);
|
|
54676
55565
|
if (entry.isDirectory()) {
|
|
54677
55566
|
await walk(fullPath);
|
|
54678
55567
|
} else if (entry.isFile()) {
|
|
@@ -54703,8 +55592,8 @@ async function doCalculateStorage(logConfig) {
|
|
|
54703
55592
|
}
|
|
54704
55593
|
const baseDir = resolveLogBaseDir(logConfig);
|
|
54705
55594
|
const [eventsResult, streamsResult] = await Promise.all([
|
|
54706
|
-
calculateDirSize(
|
|
54707
|
-
calculateDirSize(
|
|
55595
|
+
calculateDirSize(join9(baseDir, "events")),
|
|
55596
|
+
calculateDirSize(join9(baseDir, "streams"))
|
|
54708
55597
|
]);
|
|
54709
55598
|
const indexResult = await calculateIndexSize(baseDir);
|
|
54710
55599
|
return {
|
|
@@ -54718,7 +55607,7 @@ async function doCalculateStorage(logConfig) {
|
|
|
54718
55607
|
};
|
|
54719
55608
|
}
|
|
54720
55609
|
async function calculateIndexSize(baseDir) {
|
|
54721
|
-
if (!
|
|
55610
|
+
if (!existsSync8(baseDir)) {
|
|
54722
55611
|
return { bytes: 0, fileCount: 0 };
|
|
54723
55612
|
}
|
|
54724
55613
|
let bytes = 0;
|
|
@@ -54728,7 +55617,7 @@ async function calculateIndexSize(baseDir) {
|
|
|
54728
55617
|
for (const entry of entries) {
|
|
54729
55618
|
if (!entry.isFile() || !entry.name.startsWith("logs-index.sqlite"))
|
|
54730
55619
|
continue;
|
|
54731
|
-
const stats = await fsPromises.stat(
|
|
55620
|
+
const stats = await fsPromises.stat(join9(baseDir, entry.name));
|
|
54732
55621
|
bytes += stats.size;
|
|
54733
55622
|
fileCount += 1;
|
|
54734
55623
|
}
|
|
@@ -54779,33 +55668,17 @@ function startLogStorageBackgroundTask(logConfig) {
|
|
|
54779
55668
|
};
|
|
54780
55669
|
}
|
|
54781
55670
|
|
|
54782
|
-
// src/log-tail.ts
|
|
54783
|
-
var subscribers = new Set;
|
|
54784
|
-
function publishLogEvent(event) {
|
|
54785
|
-
for (const subscriber of subscribers) {
|
|
54786
|
-
try {
|
|
54787
|
-
subscriber(event);
|
|
54788
|
-
} catch {}
|
|
54789
|
-
}
|
|
54790
|
-
}
|
|
54791
|
-
function subscribeLogEvents(subscriber) {
|
|
54792
|
-
subscribers.add(subscriber);
|
|
54793
|
-
return () => {
|
|
54794
|
-
subscribers.delete(subscriber);
|
|
54795
|
-
};
|
|
54796
|
-
}
|
|
54797
|
-
|
|
54798
55671
|
// src/logger.ts
|
|
54799
55672
|
import {
|
|
54800
55673
|
appendFileSync,
|
|
54801
55674
|
closeSync as closeSync2,
|
|
54802
|
-
existsSync as
|
|
54803
|
-
mkdirSync as
|
|
55675
|
+
existsSync as existsSync9,
|
|
55676
|
+
mkdirSync as mkdirSync4,
|
|
54804
55677
|
openSync as openSync2,
|
|
54805
55678
|
statSync as statSync3,
|
|
54806
55679
|
writeSync
|
|
54807
55680
|
} from "fs";
|
|
54808
|
-
import { join as
|
|
55681
|
+
import { join as join10 } from "path";
|
|
54809
55682
|
class Logger {
|
|
54810
55683
|
baseDir;
|
|
54811
55684
|
eventsDir;
|
|
@@ -54820,8 +55693,8 @@ class Logger {
|
|
|
54820
55693
|
this._bodyPolicy = config2.bodyPolicy ?? "off";
|
|
54821
55694
|
this._streamsEnabled = config2.streams?.enabled !== false;
|
|
54822
55695
|
this.maxStreamBytes = config2.streams?.maxBytesPerRequest ?? 10 * 1024 * 1024;
|
|
54823
|
-
this.eventsDir =
|
|
54824
|
-
this.streamsDir =
|
|
55696
|
+
this.eventsDir = join10(baseDir, "events");
|
|
55697
|
+
this.streamsDir = join10(baseDir, "streams");
|
|
54825
55698
|
if (this._enabled)
|
|
54826
55699
|
this.ensureDirs();
|
|
54827
55700
|
}
|
|
@@ -54833,14 +55706,14 @@ class Logger {
|
|
|
54833
55706
|
}
|
|
54834
55707
|
ensureDirs() {
|
|
54835
55708
|
for (const dir of [this.baseDir, this.eventsDir, this.streamsDir]) {
|
|
54836
|
-
if (!
|
|
54837
|
-
|
|
55709
|
+
if (!existsSync9(dir))
|
|
55710
|
+
mkdirSync4(dir, { recursive: true });
|
|
54838
55711
|
}
|
|
54839
55712
|
}
|
|
54840
55713
|
ensureStreamDateDir(dateStr) {
|
|
54841
|
-
const dir =
|
|
54842
|
-
if (!
|
|
54843
|
-
|
|
55714
|
+
const dir = join10(this.streamsDir, dateStr);
|
|
55715
|
+
if (!existsSync9(dir))
|
|
55716
|
+
mkdirSync4(dir, { recursive: true });
|
|
54844
55717
|
return dir;
|
|
54845
55718
|
}
|
|
54846
55719
|
writeEvent(event) {
|
|
@@ -54850,8 +55723,8 @@ class Logger {
|
|
|
54850
55723
|
const enrichedEvent = enrichLogEventTokenUsage(event, { baseDir: this.baseDir });
|
|
54851
55724
|
this.ensureDirs();
|
|
54852
55725
|
const dateStr = enrichedEvent.ts_start.slice(0, 10);
|
|
54853
|
-
const filePath =
|
|
54854
|
-
const offset =
|
|
55726
|
+
const filePath = join10(this.eventsDir, `${dateStr}.jsonl`);
|
|
55727
|
+
const offset = existsSync9(filePath) ? statSync3(filePath).size : 0;
|
|
54855
55728
|
const line2 = `${JSON.stringify(enrichedEvent)}
|
|
54856
55729
|
`;
|
|
54857
55730
|
appendFileSync(filePath, line2);
|
|
@@ -54880,7 +55753,7 @@ class Logger {
|
|
|
54880
55753
|
let fd;
|
|
54881
55754
|
try {
|
|
54882
55755
|
const dir = this.ensureStreamDateDir(dateStr);
|
|
54883
|
-
filePath =
|
|
55756
|
+
filePath = join10(dir, `${requestId}.sse.raw`);
|
|
54884
55757
|
fd = openSync2(filePath, "a");
|
|
54885
55758
|
} catch (err) {
|
|
54886
55759
|
console.error("[logger] \u6D41\u5F0F\u65E5\u5FD7\u6253\u5F00\u5931\u8D25:", err);
|
|
@@ -54970,85 +55843,6 @@ function normalizeUrl(rawUrl) {
|
|
|
54970
55843
|
return rawUrl;
|
|
54971
55844
|
}
|
|
54972
55845
|
|
|
54973
|
-
// src/network-access.ts
|
|
54974
|
-
var REMOTE_ADDRESS_ENV_KEY = "LOCAL_ROUTER_REMOTE_ADDRESS";
|
|
54975
|
-
function parseIpv4(address) {
|
|
54976
|
-
const parts = address.split(".");
|
|
54977
|
-
if (parts.length !== 4)
|
|
54978
|
-
return null;
|
|
54979
|
-
const octets = parts.map((part) => {
|
|
54980
|
-
if (!/^\d{1,3}$/.test(part))
|
|
54981
|
-
return Number.NaN;
|
|
54982
|
-
const value = Number.parseInt(part, 10);
|
|
54983
|
-
return value >= 0 && value <= 255 ? value : Number.NaN;
|
|
54984
|
-
});
|
|
54985
|
-
return octets.every(Number.isFinite) ? octets : null;
|
|
54986
|
-
}
|
|
54987
|
-
function normalizeIpAddress(raw2) {
|
|
54988
|
-
let address = raw2.trim().toLowerCase();
|
|
54989
|
-
if (address.startsWith("[")) {
|
|
54990
|
-
const end = address.indexOf("]");
|
|
54991
|
-
if (end !== -1)
|
|
54992
|
-
address = address.slice(1, end);
|
|
54993
|
-
}
|
|
54994
|
-
const mappedIpv4 = address.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/);
|
|
54995
|
-
if (mappedIpv4) {
|
|
54996
|
-
return mappedIpv4[1];
|
|
54997
|
-
}
|
|
54998
|
-
return address;
|
|
54999
|
-
}
|
|
55000
|
-
function isLoopbackAddress(raw2) {
|
|
55001
|
-
if (!raw2)
|
|
55002
|
-
return false;
|
|
55003
|
-
const address = normalizeIpAddress(raw2);
|
|
55004
|
-
const ipv43 = parseIpv4(address);
|
|
55005
|
-
if (ipv43)
|
|
55006
|
-
return ipv43[0] === 127;
|
|
55007
|
-
return address === "::1";
|
|
55008
|
-
}
|
|
55009
|
-
function isLanAddress(raw2) {
|
|
55010
|
-
if (!raw2)
|
|
55011
|
-
return false;
|
|
55012
|
-
const address = normalizeIpAddress(raw2);
|
|
55013
|
-
const ipv43 = parseIpv4(address);
|
|
55014
|
-
if (ipv43) {
|
|
55015
|
-
const [a, b] = ipv43;
|
|
55016
|
-
return a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 169 && b === 254;
|
|
55017
|
-
}
|
|
55018
|
-
return address.startsWith("fc") || address.startsWith("fd") || /^fe[89ab]/.test(address);
|
|
55019
|
-
}
|
|
55020
|
-
function decideNetworkAccess(serverConfig, rawRemoteAddress) {
|
|
55021
|
-
const remoteAddress = rawRemoteAddress ? normalizeIpAddress(rawRemoteAddress) : null;
|
|
55022
|
-
if (!remoteAddress || isLoopbackAddress(remoteAddress)) {
|
|
55023
|
-
return { allowed: true, remoteAddress };
|
|
55024
|
-
}
|
|
55025
|
-
const lanEnabled = serverConfig?.lanAccess?.enabled === true;
|
|
55026
|
-
if (!lanEnabled) {
|
|
55027
|
-
return { allowed: false, remoteAddress, reason: "lan-disabled" };
|
|
55028
|
-
}
|
|
55029
|
-
if (!isLanAddress(remoteAddress)) {
|
|
55030
|
-
return { allowed: false, remoteAddress, reason: "non-lan-address" };
|
|
55031
|
-
}
|
|
55032
|
-
return { allowed: true, remoteAddress };
|
|
55033
|
-
}
|
|
55034
|
-
function getRemoteAddressFromContext(c2) {
|
|
55035
|
-
const env = c2.env;
|
|
55036
|
-
const value = env?.[REMOTE_ADDRESS_ENV_KEY];
|
|
55037
|
-
return typeof value === "string" && value.trim() ? value : null;
|
|
55038
|
-
}
|
|
55039
|
-
function createNetworkAccessMiddleware(store) {
|
|
55040
|
-
return async (c2, next) => {
|
|
55041
|
-
const decision = decideNetworkAccess(store.get().server, getRemoteAddressFromContext(c2));
|
|
55042
|
-
if (!decision.allowed) {
|
|
55043
|
-
return c2.json({
|
|
55044
|
-
error: decision.reason === "lan-disabled" ? "\u5C40\u57DF\u7F51\u670D\u52A1\u672A\u5F00\u542F\uFF0C\u5DF2\u62D2\u7EDD\u975E\u672C\u673A\u8BF7\u6C42" : "\u4EC5\u5141\u8BB8\u672C\u673A\u6216\u5C40\u57DF\u7F51\u6765\u6E90\u8BBF\u95EE",
|
|
55045
|
-
remoteAddress: decision.remoteAddress
|
|
55046
|
-
}, 403);
|
|
55047
|
-
}
|
|
55048
|
-
await next();
|
|
55049
|
-
};
|
|
55050
|
-
}
|
|
55051
|
-
|
|
55052
55846
|
// src/openapi.ts
|
|
55053
55847
|
var openAPISpec = {
|
|
55054
55848
|
openapi: "3.0.0",
|
|
@@ -56155,7 +56949,7 @@ var openAPISpec = {
|
|
|
56155
56949
|
// src/plugin-loader.ts
|
|
56156
56950
|
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
56157
56951
|
import { tmpdir } from "os";
|
|
56158
|
-
import { join as
|
|
56952
|
+
import { join as join11, resolve as resolve7 } from "path";
|
|
56159
56953
|
function isLocalPath(pkg) {
|
|
56160
56954
|
return pkg.startsWith("./") || pkg.startsWith("../") || pkg.startsWith("/") || /^[A-Za-z]:[\\/]/.test(pkg);
|
|
56161
56955
|
}
|
|
@@ -56178,7 +56972,7 @@ var remoteTmpDir = null;
|
|
|
56178
56972
|
var remoteTmpFiles = [];
|
|
56179
56973
|
async function ensureRemoteTmpDir() {
|
|
56180
56974
|
if (!remoteTmpDir) {
|
|
56181
|
-
remoteTmpDir = await mkdtemp(
|
|
56975
|
+
remoteTmpDir = await mkdtemp(join11(tmpdir(), "local-router-plugins-"));
|
|
56182
56976
|
}
|
|
56183
56977
|
return remoteTmpDir;
|
|
56184
56978
|
}
|
|
@@ -56191,7 +56985,7 @@ async function fetchRemotePlugin(url2) {
|
|
|
56191
56985
|
const ext = inferExtension(url2, response.headers.get("content-type"));
|
|
56192
56986
|
const dir = await ensureRemoteTmpDir();
|
|
56193
56987
|
const fileName = `plugin_${Date.now()}_${Math.random().toString(36).slice(2, 8)}${ext}`;
|
|
56194
|
-
const filePath =
|
|
56988
|
+
const filePath = join11(dir, fileName);
|
|
56195
56989
|
await writeFile(filePath, content, "utf-8");
|
|
56196
56990
|
remoteTmpFiles.push(filePath);
|
|
56197
56991
|
return filePath;
|
|
@@ -56211,7 +57005,7 @@ async function importPlugin(pkg, configDir) {
|
|
|
56211
57005
|
const localPath = await fetchRemotePlugin(pkg);
|
|
56212
57006
|
modulePath = `${localPath}?t=${Date.now()}`;
|
|
56213
57007
|
} else if (isLocalPath(pkg)) {
|
|
56214
|
-
const absolutePath =
|
|
57008
|
+
const absolutePath = resolve7(configDir, pkg);
|
|
56215
57009
|
modulePath = `${absolutePath}?t=${Date.now()}`;
|
|
56216
57010
|
} else {
|
|
56217
57011
|
modulePath = pkg;
|
|
@@ -56985,7 +57779,7 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
56985
57779
|
const CRYPTO_SESSION_TTL_MS = 2 * 60 * 1000;
|
|
56986
57780
|
const CRYPTO_SESSION_MAX = 512;
|
|
56987
57781
|
const schemaPath = getBundledSchemaPath();
|
|
56988
|
-
const schemaJson = JSON.parse(
|
|
57782
|
+
const schemaJson = JSON.parse(readFileSync6(schemaPath, "utf-8"));
|
|
56989
57783
|
const pruneExpiredCryptoSessions = (now2 = Date.now()) => {
|
|
56990
57784
|
for (const [id, record2] of Array.from(cryptoSessions.entries())) {
|
|
56991
57785
|
if (now2 - record2.createdAt > CRYPTO_SESSION_TTL_MS) {
|
|
@@ -57125,6 +57919,45 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
57125
57919
|
return c2.json({ error: `\u8BFB\u53D6\u914D\u7F6E schema \u5931\u8D25: ${err instanceof Error ? err.message : err}` }, 500);
|
|
57126
57920
|
}
|
|
57127
57921
|
});
|
|
57922
|
+
api2.get("/autostart", async (c2) => {
|
|
57923
|
+
try {
|
|
57924
|
+
const manager = createAutostartManager();
|
|
57925
|
+
const systemInstalled = await manager.isInstalled();
|
|
57926
|
+
const config2 = store.get();
|
|
57927
|
+
return c2.json({
|
|
57928
|
+
enabled: config2.server?.autostart ?? false,
|
|
57929
|
+
systemInstalled,
|
|
57930
|
+
platform: manager.platform,
|
|
57931
|
+
servicePath: manager.getServicePath()
|
|
57932
|
+
});
|
|
57933
|
+
} catch (err) {
|
|
57934
|
+
return c2.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
57935
|
+
}
|
|
57936
|
+
});
|
|
57937
|
+
api2.put("/autostart", async (c2) => {
|
|
57938
|
+
try {
|
|
57939
|
+
const { enabled } = await c2.req.json();
|
|
57940
|
+
if (typeof enabled !== "boolean") {
|
|
57941
|
+
return c2.json({ error: "enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C" }, 400);
|
|
57942
|
+
}
|
|
57943
|
+
const manager = createAutostartManager();
|
|
57944
|
+
if (manager.platform === "unsupported") {
|
|
57945
|
+
return c2.json({ error: "\u5F53\u524D\u5E73\u53F0\u4E0D\u652F\u6301\u81EA\u542F\u52A8" }, 400);
|
|
57946
|
+
}
|
|
57947
|
+
if (enabled) {
|
|
57948
|
+
const { execPath, args } = getAutostartExecArgs();
|
|
57949
|
+
await manager.install({ execPath, args, label: "com.lakphy.local-router" });
|
|
57950
|
+
} else {
|
|
57951
|
+
await manager.uninstall();
|
|
57952
|
+
}
|
|
57953
|
+
const config2 = store.get();
|
|
57954
|
+
const updated = { ...config2, server: { ...config2.server, autostart: enabled } };
|
|
57955
|
+
store.save(updated);
|
|
57956
|
+
return c2.json({ ok: true, enabled });
|
|
57957
|
+
} catch (err) {
|
|
57958
|
+
return c2.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
57959
|
+
}
|
|
57960
|
+
});
|
|
57128
57961
|
api2.post("/chat/proxy", async (c2) => {
|
|
57129
57962
|
let body;
|
|
57130
57963
|
try {
|
|
@@ -57590,7 +58423,7 @@ async function createApp(store, options) {
|
|
|
57590
58423
|
}
|
|
57591
58424
|
const stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
|
|
57592
58425
|
options?.registerCleanup?.(stopLogStorageTask);
|
|
57593
|
-
const configDir =
|
|
58426
|
+
const configDir = dirname3(resolve8(store.getPath()));
|
|
57594
58427
|
const pluginManager = new PluginManager(configDir);
|
|
57595
58428
|
const reloadResult = await pluginManager.reloadAll(config2.providers);
|
|
57596
58429
|
if (!reloadResult.ok) {
|
|
@@ -57637,28 +58470,56 @@ async function createApp(store, options) {
|
|
|
57637
58470
|
}
|
|
57638
58471
|
return app;
|
|
57639
58472
|
}
|
|
57640
|
-
async function
|
|
57641
|
-
const configPath = parseConfigPath();
|
|
58473
|
+
async function createAppRuntimeFromConfigPath(configPath, listen) {
|
|
57642
58474
|
const store = new ConfigStore(configPath);
|
|
57643
|
-
|
|
57644
|
-
|
|
57645
|
-
|
|
57646
|
-
|
|
58475
|
+
const cleanups = [];
|
|
58476
|
+
const app = await createApp(store, {
|
|
58477
|
+
listen,
|
|
58478
|
+
registerCleanup: (cleanup) => {
|
|
58479
|
+
cleanups.push(cleanup);
|
|
57647
58480
|
}
|
|
57648
58481
|
});
|
|
58482
|
+
const logRealtime = createLogRealtimeRuntime({ store });
|
|
58483
|
+
return {
|
|
58484
|
+
app,
|
|
58485
|
+
logRealtime,
|
|
58486
|
+
dispose: () => {
|
|
58487
|
+
logRealtime.dispose();
|
|
58488
|
+
for (const cleanup of cleanups.reverse()) {
|
|
58489
|
+
try {
|
|
58490
|
+
cleanup();
|
|
58491
|
+
} catch {}
|
|
58492
|
+
}
|
|
58493
|
+
}
|
|
58494
|
+
};
|
|
58495
|
+
}
|
|
58496
|
+
async function createDefaultAppRuntimeFromProcessArgs() {
|
|
58497
|
+
const configPath = parseConfigPath();
|
|
58498
|
+
return createAppRuntimeFromConfigPath(configPath, {
|
|
58499
|
+
host: process.env.HOST ?? "0.0.0.0",
|
|
58500
|
+
port: Number.parseInt(process.env.PORT ?? "4099", 10)
|
|
58501
|
+
});
|
|
57649
58502
|
}
|
|
57650
58503
|
|
|
57651
58504
|
// src/entry.ts
|
|
57652
|
-
var
|
|
58505
|
+
var runtime = await createDefaultAppRuntimeFromProcessArgs();
|
|
57653
58506
|
var entry_default = {
|
|
57654
58507
|
hostname: process.env.HOST ?? "0.0.0.0",
|
|
57655
58508
|
port: Number.parseInt(process.env.PORT ?? "4099", 10),
|
|
57656
58509
|
fetch(request, server) {
|
|
57657
58510
|
const remoteAddress = server.requestIP(request)?.address ?? null;
|
|
57658
|
-
|
|
58511
|
+
const realtimeUpgrade = runtime.logRealtime.upgrade(request, server, remoteAddress);
|
|
58512
|
+
if (realtimeUpgrade.handled) {
|
|
58513
|
+
if (realtimeUpgrade.upgraded) {
|
|
58514
|
+
return;
|
|
58515
|
+
}
|
|
58516
|
+
return realtimeUpgrade.response ?? new Response("WebSocket Upgrade failed", { status: 400 });
|
|
58517
|
+
}
|
|
58518
|
+
return runtime.app.fetch(request, {
|
|
57659
58519
|
[REMOTE_ADDRESS_ENV_KEY]: remoteAddress
|
|
57660
58520
|
});
|
|
57661
|
-
}
|
|
58521
|
+
},
|
|
58522
|
+
websocket: runtime.logRealtime.websocket
|
|
57662
58523
|
};
|
|
57663
58524
|
export {
|
|
57664
58525
|
entry_default as default
|