@lakphy/local-router 0.4.1 → 0.5.0
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/README.md +4 -1
- package/config.schema.json +33 -1
- package/dist/cli.js +10073 -8818
- package/dist/entry.js +1609 -456
- package/dist/index.js +13699 -0
- package/dist/web/assets/index-BprGtkte.js +191 -0
- package/dist/web/assets/index-OkpSAlwA.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/plugin.ts +10 -7
- package/dist/web/assets/index-BpifDtUQ.css +0 -2
- package/dist/web/assets/index-Dt7z9ioY.js +0 -191
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 = resolve4.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 resolve4(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 resolve4(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: resolve4,
|
|
5723
5723
|
resolveComponent,
|
|
5724
5724
|
equal,
|
|
5725
5725
|
serialize,
|
|
@@ -5856,7 +5856,7 @@ var require_core = __commonJS((exports) => {
|
|
|
5856
5856
|
opts = this.opts = { ...opts, ...requiredOptions(opts) };
|
|
5857
5857
|
const { es5, lines } = this.opts.code;
|
|
5858
5858
|
this.scope = new codegen_2.ValueScope({ scope: {}, prefixes: EXT_SCOPE_NAMES, es5, lines });
|
|
5859
|
-
this.logger =
|
|
5859
|
+
this.logger = getLogger(opts.logger);
|
|
5860
5860
|
const formatOpt = opts.validateFormats;
|
|
5861
5861
|
opts.validateFormats = false;
|
|
5862
5862
|
this.RULES = (0, rules_1.getRules)();
|
|
@@ -6253,7 +6253,7 @@ var require_core = __commonJS((exports) => {
|
|
|
6253
6253
|
return metaOpts;
|
|
6254
6254
|
}
|
|
6255
6255
|
var noLogs = { log() {}, warn() {}, error() {} };
|
|
6256
|
-
function
|
|
6256
|
+
function getLogger(logger) {
|
|
6257
6257
|
if (logger === false)
|
|
6258
6258
|
return noLogs;
|
|
6259
6259
|
if (logger === undefined)
|
|
@@ -40602,6 +40602,267 @@ function createOpenAICompatible(options) {
|
|
|
40602
40602
|
provider.imageModel = createImageModel;
|
|
40603
40603
|
return provider;
|
|
40604
40604
|
}
|
|
40605
|
+
|
|
40606
|
+
// node_modules/.bun/hono@4.12.5/node_modules/hono/dist/utils/html.js
|
|
40607
|
+
var HtmlEscapedCallbackPhase = {
|
|
40608
|
+
Stringify: 1,
|
|
40609
|
+
BeforeStream: 2,
|
|
40610
|
+
Stream: 3
|
|
40611
|
+
};
|
|
40612
|
+
var raw = (value, callbacks) => {
|
|
40613
|
+
const escapedString = new String(value);
|
|
40614
|
+
escapedString.isEscaped = true;
|
|
40615
|
+
escapedString.callbacks = callbacks;
|
|
40616
|
+
return escapedString;
|
|
40617
|
+
};
|
|
40618
|
+
var escapeRe = /[&<>'"]/;
|
|
40619
|
+
var stringBufferToString = async (buffer, callbacks) => {
|
|
40620
|
+
let str = "";
|
|
40621
|
+
callbacks ||= [];
|
|
40622
|
+
const resolvedBuffer = await Promise.all(buffer);
|
|
40623
|
+
for (let i = resolvedBuffer.length - 1;; i--) {
|
|
40624
|
+
str += resolvedBuffer[i];
|
|
40625
|
+
i--;
|
|
40626
|
+
if (i < 0) {
|
|
40627
|
+
break;
|
|
40628
|
+
}
|
|
40629
|
+
let r = resolvedBuffer[i];
|
|
40630
|
+
if (typeof r === "object") {
|
|
40631
|
+
callbacks.push(...r.callbacks || []);
|
|
40632
|
+
}
|
|
40633
|
+
const isEscaped = r.isEscaped;
|
|
40634
|
+
r = await (typeof r === "object" ? r.toString() : r);
|
|
40635
|
+
if (typeof r === "object") {
|
|
40636
|
+
callbacks.push(...r.callbacks || []);
|
|
40637
|
+
}
|
|
40638
|
+
if (r.isEscaped ?? isEscaped) {
|
|
40639
|
+
str += r;
|
|
40640
|
+
} else {
|
|
40641
|
+
const buf = [str];
|
|
40642
|
+
escapeToBuffer(r, buf);
|
|
40643
|
+
str = buf[0];
|
|
40644
|
+
}
|
|
40645
|
+
}
|
|
40646
|
+
return raw(str, callbacks);
|
|
40647
|
+
};
|
|
40648
|
+
var escapeToBuffer = (str, buffer) => {
|
|
40649
|
+
const match = str.search(escapeRe);
|
|
40650
|
+
if (match === -1) {
|
|
40651
|
+
buffer[0] += str;
|
|
40652
|
+
return;
|
|
40653
|
+
}
|
|
40654
|
+
let escape2;
|
|
40655
|
+
let index;
|
|
40656
|
+
let lastIndex = 0;
|
|
40657
|
+
for (index = match;index < str.length; index++) {
|
|
40658
|
+
switch (str.charCodeAt(index)) {
|
|
40659
|
+
case 34:
|
|
40660
|
+
escape2 = """;
|
|
40661
|
+
break;
|
|
40662
|
+
case 39:
|
|
40663
|
+
escape2 = "'";
|
|
40664
|
+
break;
|
|
40665
|
+
case 38:
|
|
40666
|
+
escape2 = "&";
|
|
40667
|
+
break;
|
|
40668
|
+
case 60:
|
|
40669
|
+
escape2 = "<";
|
|
40670
|
+
break;
|
|
40671
|
+
case 62:
|
|
40672
|
+
escape2 = ">";
|
|
40673
|
+
break;
|
|
40674
|
+
default:
|
|
40675
|
+
continue;
|
|
40676
|
+
}
|
|
40677
|
+
buffer[0] += str.substring(lastIndex, index) + escape2;
|
|
40678
|
+
lastIndex = index + 1;
|
|
40679
|
+
}
|
|
40680
|
+
buffer[0] += str.substring(lastIndex, index);
|
|
40681
|
+
};
|
|
40682
|
+
var resolveCallbackSync = (str) => {
|
|
40683
|
+
const callbacks = str.callbacks;
|
|
40684
|
+
if (!callbacks?.length) {
|
|
40685
|
+
return str;
|
|
40686
|
+
}
|
|
40687
|
+
const buffer = [str];
|
|
40688
|
+
const context = {};
|
|
40689
|
+
callbacks.forEach((c) => c({ phase: HtmlEscapedCallbackPhase.Stringify, buffer, context }));
|
|
40690
|
+
return buffer[0];
|
|
40691
|
+
};
|
|
40692
|
+
var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
|
|
40693
|
+
if (typeof str === "object" && !(str instanceof String)) {
|
|
40694
|
+
if (!(str instanceof Promise)) {
|
|
40695
|
+
str = str.toString();
|
|
40696
|
+
}
|
|
40697
|
+
if (str instanceof Promise) {
|
|
40698
|
+
str = await str;
|
|
40699
|
+
}
|
|
40700
|
+
}
|
|
40701
|
+
const callbacks = str.callbacks;
|
|
40702
|
+
if (!callbacks?.length) {
|
|
40703
|
+
return Promise.resolve(str);
|
|
40704
|
+
}
|
|
40705
|
+
if (buffer) {
|
|
40706
|
+
buffer[0] += str;
|
|
40707
|
+
} else {
|
|
40708
|
+
buffer = [str];
|
|
40709
|
+
}
|
|
40710
|
+
const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then((res) => Promise.all(res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context, buffer))).then(() => buffer[0]));
|
|
40711
|
+
if (preserveCallbacks) {
|
|
40712
|
+
return raw(await resStr, callbacks);
|
|
40713
|
+
} else {
|
|
40714
|
+
return resStr;
|
|
40715
|
+
}
|
|
40716
|
+
};
|
|
40717
|
+
|
|
40718
|
+
// node_modules/.bun/hono@4.12.5/node_modules/hono/dist/helper/html/index.js
|
|
40719
|
+
var html = (strings, ...values) => {
|
|
40720
|
+
const buffer = [""];
|
|
40721
|
+
for (let i = 0, len = strings.length - 1;i < len; i++) {
|
|
40722
|
+
buffer[0] += strings[i];
|
|
40723
|
+
const children = Array.isArray(values[i]) ? values[i].flat(Infinity) : [values[i]];
|
|
40724
|
+
for (let i2 = 0, len2 = children.length;i2 < len2; i2++) {
|
|
40725
|
+
const child = children[i2];
|
|
40726
|
+
if (typeof child === "string") {
|
|
40727
|
+
escapeToBuffer(child, buffer);
|
|
40728
|
+
} else if (typeof child === "number") {
|
|
40729
|
+
buffer[0] += child;
|
|
40730
|
+
} else if (typeof child === "boolean" || child === null || child === undefined) {
|
|
40731
|
+
continue;
|
|
40732
|
+
} else if (typeof child === "object" && child.isEscaped) {
|
|
40733
|
+
if (child.callbacks) {
|
|
40734
|
+
buffer.unshift("", child);
|
|
40735
|
+
} else {
|
|
40736
|
+
const tmp = child.toString();
|
|
40737
|
+
if (tmp instanceof Promise) {
|
|
40738
|
+
buffer.unshift("", tmp);
|
|
40739
|
+
} else {
|
|
40740
|
+
buffer[0] += tmp;
|
|
40741
|
+
}
|
|
40742
|
+
}
|
|
40743
|
+
} else if (child instanceof Promise) {
|
|
40744
|
+
buffer.unshift("", child);
|
|
40745
|
+
} else {
|
|
40746
|
+
escapeToBuffer(child.toString(), buffer);
|
|
40747
|
+
}
|
|
40748
|
+
}
|
|
40749
|
+
}
|
|
40750
|
+
buffer[0] += strings.at(-1);
|
|
40751
|
+
return buffer.length === 1 ? "callbacks" in buffer ? raw(resolveCallbackSync(raw(buffer[0], buffer.callbacks))) : raw(buffer[0]) : stringBufferToString(buffer, buffer.callbacks);
|
|
40752
|
+
};
|
|
40753
|
+
|
|
40754
|
+
// node_modules/.bun/@hono+swagger-ui@0.5.3+bac6debbfb5ded19/node_modules/@hono/swagger-ui/dist/index.js
|
|
40755
|
+
var RENDER_TYPE = {
|
|
40756
|
+
STRING_ARRAY: "string_array",
|
|
40757
|
+
STRING: "string",
|
|
40758
|
+
JSON_STRING: "json_string",
|
|
40759
|
+
RAW: "raw"
|
|
40760
|
+
};
|
|
40761
|
+
var RENDER_TYPE_MAP = {
|
|
40762
|
+
configUrl: RENDER_TYPE.STRING,
|
|
40763
|
+
deepLinking: RENDER_TYPE.RAW,
|
|
40764
|
+
presets: RENDER_TYPE.STRING_ARRAY,
|
|
40765
|
+
plugins: RENDER_TYPE.STRING_ARRAY,
|
|
40766
|
+
spec: RENDER_TYPE.JSON_STRING,
|
|
40767
|
+
url: RENDER_TYPE.STRING,
|
|
40768
|
+
urls: RENDER_TYPE.JSON_STRING,
|
|
40769
|
+
layout: RENDER_TYPE.STRING,
|
|
40770
|
+
docExpansion: RENDER_TYPE.STRING,
|
|
40771
|
+
maxDisplayedTags: RENDER_TYPE.RAW,
|
|
40772
|
+
operationsSorter: RENDER_TYPE.RAW,
|
|
40773
|
+
requestInterceptor: RENDER_TYPE.RAW,
|
|
40774
|
+
responseInterceptor: RENDER_TYPE.RAW,
|
|
40775
|
+
persistAuthorization: RENDER_TYPE.RAW,
|
|
40776
|
+
defaultModelsExpandDepth: RENDER_TYPE.RAW,
|
|
40777
|
+
defaultModelExpandDepth: RENDER_TYPE.RAW,
|
|
40778
|
+
defaultModelRendering: RENDER_TYPE.STRING,
|
|
40779
|
+
displayRequestDuration: RENDER_TYPE.RAW,
|
|
40780
|
+
filter: RENDER_TYPE.RAW,
|
|
40781
|
+
showExtensions: RENDER_TYPE.RAW,
|
|
40782
|
+
showCommonExtensions: RENDER_TYPE.RAW,
|
|
40783
|
+
queryConfigEnabled: RENDER_TYPE.RAW,
|
|
40784
|
+
displayOperationId: RENDER_TYPE.RAW,
|
|
40785
|
+
tagsSorter: RENDER_TYPE.RAW,
|
|
40786
|
+
onComplete: RENDER_TYPE.RAW,
|
|
40787
|
+
syntaxHighlight: RENDER_TYPE.JSON_STRING,
|
|
40788
|
+
tryItOutEnabled: RENDER_TYPE.RAW,
|
|
40789
|
+
requestSnippetsEnabled: RENDER_TYPE.RAW,
|
|
40790
|
+
requestSnippets: RENDER_TYPE.JSON_STRING,
|
|
40791
|
+
oauth2RedirectUrl: RENDER_TYPE.STRING,
|
|
40792
|
+
showMutabledRequest: RENDER_TYPE.RAW,
|
|
40793
|
+
request: RENDER_TYPE.JSON_STRING,
|
|
40794
|
+
supportedSubmitMethods: RENDER_TYPE.JSON_STRING,
|
|
40795
|
+
validatorUrl: RENDER_TYPE.STRING,
|
|
40796
|
+
withCredentials: RENDER_TYPE.RAW,
|
|
40797
|
+
modelPropertyMacro: RENDER_TYPE.RAW,
|
|
40798
|
+
parameterMacro: RENDER_TYPE.RAW
|
|
40799
|
+
};
|
|
40800
|
+
var renderSwaggerUIOptions = (options) => {
|
|
40801
|
+
return Object.entries(options).map(([k, v]) => {
|
|
40802
|
+
const key = k;
|
|
40803
|
+
if (!RENDER_TYPE_MAP[key] || v === undefined)
|
|
40804
|
+
return "";
|
|
40805
|
+
switch (RENDER_TYPE_MAP[key]) {
|
|
40806
|
+
case RENDER_TYPE.STRING:
|
|
40807
|
+
return `${key}: '${v}'`;
|
|
40808
|
+
case RENDER_TYPE.STRING_ARRAY:
|
|
40809
|
+
if (!Array.isArray(v))
|
|
40810
|
+
return "";
|
|
40811
|
+
return `${key}: [${v.map((ve) => `${ve}`).join(",")}]`;
|
|
40812
|
+
case RENDER_TYPE.JSON_STRING:
|
|
40813
|
+
return `${key}: ${JSON.stringify(v)}`;
|
|
40814
|
+
case RENDER_TYPE.RAW:
|
|
40815
|
+
return `${key}: ${v}`;
|
|
40816
|
+
default:
|
|
40817
|
+
return "";
|
|
40818
|
+
}
|
|
40819
|
+
}).filter((item) => item !== "").join(",");
|
|
40820
|
+
};
|
|
40821
|
+
var remoteAssets = ({ version: version2 }) => {
|
|
40822
|
+
const url2 = `https://cdn.jsdelivr.net/npm/swagger-ui-dist${version2 !== undefined ? `@${version2}` : ""}`;
|
|
40823
|
+
return {
|
|
40824
|
+
css: [`${url2}/swagger-ui.css`],
|
|
40825
|
+
js: [`${url2}/swagger-ui-bundle.js`]
|
|
40826
|
+
};
|
|
40827
|
+
};
|
|
40828
|
+
var SwaggerUI = (options) => {
|
|
40829
|
+
const asset = remoteAssets({ version: options?.version });
|
|
40830
|
+
delete options.version;
|
|
40831
|
+
if (options.manuallySwaggerUIHtml)
|
|
40832
|
+
return options.manuallySwaggerUIHtml(asset);
|
|
40833
|
+
const optionsStrings = renderSwaggerUIOptions(options);
|
|
40834
|
+
return `
|
|
40835
|
+
<div>
|
|
40836
|
+
<div id="swagger-ui"></div>
|
|
40837
|
+
${asset.css.map((url2) => html`<link rel="stylesheet" href="${url2}" />`)}
|
|
40838
|
+
${asset.js.map((url2) => html`<script src="${url2}" crossorigin="anonymous"><\/script>`)}
|
|
40839
|
+
<script>
|
|
40840
|
+
window.onload = () => {
|
|
40841
|
+
window.ui = SwaggerUIBundle({
|
|
40842
|
+
dom_id: '#swagger-ui',${optionsStrings},
|
|
40843
|
+
})
|
|
40844
|
+
}
|
|
40845
|
+
</script>
|
|
40846
|
+
</div>
|
|
40847
|
+
`;
|
|
40848
|
+
};
|
|
40849
|
+
var middleware = (options) => async (c) => {
|
|
40850
|
+
const title = options?.title ?? "SwaggerUI";
|
|
40851
|
+
return c.html(`
|
|
40852
|
+
<!doctype html>
|
|
40853
|
+
<html lang="en">
|
|
40854
|
+
<head>
|
|
40855
|
+
<meta charset="utf-8" />
|
|
40856
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
40857
|
+
<meta name="description" content="SwaggerUI" />
|
|
40858
|
+
<title>${title}</title>
|
|
40859
|
+
</head>
|
|
40860
|
+
<body>
|
|
40861
|
+
${SwaggerUI(options)}
|
|
40862
|
+
</body>
|
|
40863
|
+
</html>
|
|
40864
|
+
`);
|
|
40865
|
+
};
|
|
40605
40866
|
// node_modules/.bun/@ai-sdk+gateway@3.0.66+3c5d820c62823f0b/node_modules/@ai-sdk/gateway/dist/index.mjs
|
|
40606
40867
|
var import_oidc = __toESM(require_dist(), 1);
|
|
40607
40868
|
var import_oidc2 = __toESM(require_dist(), 1);
|
|
@@ -48100,267 +48361,6 @@ var _a20;
|
|
|
48100
48361
|
_a20 = symbol20;
|
|
48101
48362
|
var defaultDownload2 = createDownload();
|
|
48102
48363
|
|
|
48103
|
-
// node_modules/.bun/hono@4.12.5/node_modules/hono/dist/utils/html.js
|
|
48104
|
-
var HtmlEscapedCallbackPhase = {
|
|
48105
|
-
Stringify: 1,
|
|
48106
|
-
BeforeStream: 2,
|
|
48107
|
-
Stream: 3
|
|
48108
|
-
};
|
|
48109
|
-
var raw = (value, callbacks) => {
|
|
48110
|
-
const escapedString = new String(value);
|
|
48111
|
-
escapedString.isEscaped = true;
|
|
48112
|
-
escapedString.callbacks = callbacks;
|
|
48113
|
-
return escapedString;
|
|
48114
|
-
};
|
|
48115
|
-
var escapeRe = /[&<>'"]/;
|
|
48116
|
-
var stringBufferToString = async (buffer, callbacks) => {
|
|
48117
|
-
let str = "";
|
|
48118
|
-
callbacks ||= [];
|
|
48119
|
-
const resolvedBuffer = await Promise.all(buffer);
|
|
48120
|
-
for (let i = resolvedBuffer.length - 1;; i--) {
|
|
48121
|
-
str += resolvedBuffer[i];
|
|
48122
|
-
i--;
|
|
48123
|
-
if (i < 0) {
|
|
48124
|
-
break;
|
|
48125
|
-
}
|
|
48126
|
-
let r = resolvedBuffer[i];
|
|
48127
|
-
if (typeof r === "object") {
|
|
48128
|
-
callbacks.push(...r.callbacks || []);
|
|
48129
|
-
}
|
|
48130
|
-
const isEscaped = r.isEscaped;
|
|
48131
|
-
r = await (typeof r === "object" ? r.toString() : r);
|
|
48132
|
-
if (typeof r === "object") {
|
|
48133
|
-
callbacks.push(...r.callbacks || []);
|
|
48134
|
-
}
|
|
48135
|
-
if (r.isEscaped ?? isEscaped) {
|
|
48136
|
-
str += r;
|
|
48137
|
-
} else {
|
|
48138
|
-
const buf = [str];
|
|
48139
|
-
escapeToBuffer(r, buf);
|
|
48140
|
-
str = buf[0];
|
|
48141
|
-
}
|
|
48142
|
-
}
|
|
48143
|
-
return raw(str, callbacks);
|
|
48144
|
-
};
|
|
48145
|
-
var escapeToBuffer = (str, buffer) => {
|
|
48146
|
-
const match = str.search(escapeRe);
|
|
48147
|
-
if (match === -1) {
|
|
48148
|
-
buffer[0] += str;
|
|
48149
|
-
return;
|
|
48150
|
-
}
|
|
48151
|
-
let escape2;
|
|
48152
|
-
let index;
|
|
48153
|
-
let lastIndex = 0;
|
|
48154
|
-
for (index = match;index < str.length; index++) {
|
|
48155
|
-
switch (str.charCodeAt(index)) {
|
|
48156
|
-
case 34:
|
|
48157
|
-
escape2 = """;
|
|
48158
|
-
break;
|
|
48159
|
-
case 39:
|
|
48160
|
-
escape2 = "'";
|
|
48161
|
-
break;
|
|
48162
|
-
case 38:
|
|
48163
|
-
escape2 = "&";
|
|
48164
|
-
break;
|
|
48165
|
-
case 60:
|
|
48166
|
-
escape2 = "<";
|
|
48167
|
-
break;
|
|
48168
|
-
case 62:
|
|
48169
|
-
escape2 = ">";
|
|
48170
|
-
break;
|
|
48171
|
-
default:
|
|
48172
|
-
continue;
|
|
48173
|
-
}
|
|
48174
|
-
buffer[0] += str.substring(lastIndex, index) + escape2;
|
|
48175
|
-
lastIndex = index + 1;
|
|
48176
|
-
}
|
|
48177
|
-
buffer[0] += str.substring(lastIndex, index);
|
|
48178
|
-
};
|
|
48179
|
-
var resolveCallbackSync = (str) => {
|
|
48180
|
-
const callbacks = str.callbacks;
|
|
48181
|
-
if (!callbacks?.length) {
|
|
48182
|
-
return str;
|
|
48183
|
-
}
|
|
48184
|
-
const buffer = [str];
|
|
48185
|
-
const context2 = {};
|
|
48186
|
-
callbacks.forEach((c) => c({ phase: HtmlEscapedCallbackPhase.Stringify, buffer, context: context2 }));
|
|
48187
|
-
return buffer[0];
|
|
48188
|
-
};
|
|
48189
|
-
var resolveCallback = async (str, phase, preserveCallbacks, context2, buffer) => {
|
|
48190
|
-
if (typeof str === "object" && !(str instanceof String)) {
|
|
48191
|
-
if (!(str instanceof Promise)) {
|
|
48192
|
-
str = str.toString();
|
|
48193
|
-
}
|
|
48194
|
-
if (str instanceof Promise) {
|
|
48195
|
-
str = await str;
|
|
48196
|
-
}
|
|
48197
|
-
}
|
|
48198
|
-
const callbacks = str.callbacks;
|
|
48199
|
-
if (!callbacks?.length) {
|
|
48200
|
-
return Promise.resolve(str);
|
|
48201
|
-
}
|
|
48202
|
-
if (buffer) {
|
|
48203
|
-
buffer[0] += str;
|
|
48204
|
-
} else {
|
|
48205
|
-
buffer = [str];
|
|
48206
|
-
}
|
|
48207
|
-
const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context: context2 }))).then((res) => Promise.all(res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context2, buffer))).then(() => buffer[0]));
|
|
48208
|
-
if (preserveCallbacks) {
|
|
48209
|
-
return raw(await resStr, callbacks);
|
|
48210
|
-
} else {
|
|
48211
|
-
return resStr;
|
|
48212
|
-
}
|
|
48213
|
-
};
|
|
48214
|
-
|
|
48215
|
-
// node_modules/.bun/hono@4.12.5/node_modules/hono/dist/helper/html/index.js
|
|
48216
|
-
var html = (strings, ...values) => {
|
|
48217
|
-
const buffer = [""];
|
|
48218
|
-
for (let i = 0, len = strings.length - 1;i < len; i++) {
|
|
48219
|
-
buffer[0] += strings[i];
|
|
48220
|
-
const children = Array.isArray(values[i]) ? values[i].flat(Infinity) : [values[i]];
|
|
48221
|
-
for (let i2 = 0, len2 = children.length;i2 < len2; i2++) {
|
|
48222
|
-
const child = children[i2];
|
|
48223
|
-
if (typeof child === "string") {
|
|
48224
|
-
escapeToBuffer(child, buffer);
|
|
48225
|
-
} else if (typeof child === "number") {
|
|
48226
|
-
buffer[0] += child;
|
|
48227
|
-
} else if (typeof child === "boolean" || child === null || child === undefined) {
|
|
48228
|
-
continue;
|
|
48229
|
-
} else if (typeof child === "object" && child.isEscaped) {
|
|
48230
|
-
if (child.callbacks) {
|
|
48231
|
-
buffer.unshift("", child);
|
|
48232
|
-
} else {
|
|
48233
|
-
const tmp = child.toString();
|
|
48234
|
-
if (tmp instanceof Promise) {
|
|
48235
|
-
buffer.unshift("", tmp);
|
|
48236
|
-
} else {
|
|
48237
|
-
buffer[0] += tmp;
|
|
48238
|
-
}
|
|
48239
|
-
}
|
|
48240
|
-
} else if (child instanceof Promise) {
|
|
48241
|
-
buffer.unshift("", child);
|
|
48242
|
-
} else {
|
|
48243
|
-
escapeToBuffer(child.toString(), buffer);
|
|
48244
|
-
}
|
|
48245
|
-
}
|
|
48246
|
-
}
|
|
48247
|
-
buffer[0] += strings.at(-1);
|
|
48248
|
-
return buffer.length === 1 ? "callbacks" in buffer ? raw(resolveCallbackSync(raw(buffer[0], buffer.callbacks))) : raw(buffer[0]) : stringBufferToString(buffer, buffer.callbacks);
|
|
48249
|
-
};
|
|
48250
|
-
|
|
48251
|
-
// node_modules/.bun/@hono+swagger-ui@0.5.3+bac6debbfb5ded19/node_modules/@hono/swagger-ui/dist/index.js
|
|
48252
|
-
var RENDER_TYPE = {
|
|
48253
|
-
STRING_ARRAY: "string_array",
|
|
48254
|
-
STRING: "string",
|
|
48255
|
-
JSON_STRING: "json_string",
|
|
48256
|
-
RAW: "raw"
|
|
48257
|
-
};
|
|
48258
|
-
var RENDER_TYPE_MAP = {
|
|
48259
|
-
configUrl: RENDER_TYPE.STRING,
|
|
48260
|
-
deepLinking: RENDER_TYPE.RAW,
|
|
48261
|
-
presets: RENDER_TYPE.STRING_ARRAY,
|
|
48262
|
-
plugins: RENDER_TYPE.STRING_ARRAY,
|
|
48263
|
-
spec: RENDER_TYPE.JSON_STRING,
|
|
48264
|
-
url: RENDER_TYPE.STRING,
|
|
48265
|
-
urls: RENDER_TYPE.JSON_STRING,
|
|
48266
|
-
layout: RENDER_TYPE.STRING,
|
|
48267
|
-
docExpansion: RENDER_TYPE.STRING,
|
|
48268
|
-
maxDisplayedTags: RENDER_TYPE.RAW,
|
|
48269
|
-
operationsSorter: RENDER_TYPE.RAW,
|
|
48270
|
-
requestInterceptor: RENDER_TYPE.RAW,
|
|
48271
|
-
responseInterceptor: RENDER_TYPE.RAW,
|
|
48272
|
-
persistAuthorization: RENDER_TYPE.RAW,
|
|
48273
|
-
defaultModelsExpandDepth: RENDER_TYPE.RAW,
|
|
48274
|
-
defaultModelExpandDepth: RENDER_TYPE.RAW,
|
|
48275
|
-
defaultModelRendering: RENDER_TYPE.STRING,
|
|
48276
|
-
displayRequestDuration: RENDER_TYPE.RAW,
|
|
48277
|
-
filter: RENDER_TYPE.RAW,
|
|
48278
|
-
showExtensions: RENDER_TYPE.RAW,
|
|
48279
|
-
showCommonExtensions: RENDER_TYPE.RAW,
|
|
48280
|
-
queryConfigEnabled: RENDER_TYPE.RAW,
|
|
48281
|
-
displayOperationId: RENDER_TYPE.RAW,
|
|
48282
|
-
tagsSorter: RENDER_TYPE.RAW,
|
|
48283
|
-
onComplete: RENDER_TYPE.RAW,
|
|
48284
|
-
syntaxHighlight: RENDER_TYPE.JSON_STRING,
|
|
48285
|
-
tryItOutEnabled: RENDER_TYPE.RAW,
|
|
48286
|
-
requestSnippetsEnabled: RENDER_TYPE.RAW,
|
|
48287
|
-
requestSnippets: RENDER_TYPE.JSON_STRING,
|
|
48288
|
-
oauth2RedirectUrl: RENDER_TYPE.STRING,
|
|
48289
|
-
showMutabledRequest: RENDER_TYPE.RAW,
|
|
48290
|
-
request: RENDER_TYPE.JSON_STRING,
|
|
48291
|
-
supportedSubmitMethods: RENDER_TYPE.JSON_STRING,
|
|
48292
|
-
validatorUrl: RENDER_TYPE.STRING,
|
|
48293
|
-
withCredentials: RENDER_TYPE.RAW,
|
|
48294
|
-
modelPropertyMacro: RENDER_TYPE.RAW,
|
|
48295
|
-
parameterMacro: RENDER_TYPE.RAW
|
|
48296
|
-
};
|
|
48297
|
-
var renderSwaggerUIOptions = (options) => {
|
|
48298
|
-
return Object.entries(options).map(([k, v]) => {
|
|
48299
|
-
const key = k;
|
|
48300
|
-
if (!RENDER_TYPE_MAP[key] || v === undefined)
|
|
48301
|
-
return "";
|
|
48302
|
-
switch (RENDER_TYPE_MAP[key]) {
|
|
48303
|
-
case RENDER_TYPE.STRING:
|
|
48304
|
-
return `${key}: '${v}'`;
|
|
48305
|
-
case RENDER_TYPE.STRING_ARRAY:
|
|
48306
|
-
if (!Array.isArray(v))
|
|
48307
|
-
return "";
|
|
48308
|
-
return `${key}: [${v.map((ve) => `${ve}`).join(",")}]`;
|
|
48309
|
-
case RENDER_TYPE.JSON_STRING:
|
|
48310
|
-
return `${key}: ${JSON.stringify(v)}`;
|
|
48311
|
-
case RENDER_TYPE.RAW:
|
|
48312
|
-
return `${key}: ${v}`;
|
|
48313
|
-
default:
|
|
48314
|
-
return "";
|
|
48315
|
-
}
|
|
48316
|
-
}).filter((item) => item !== "").join(",");
|
|
48317
|
-
};
|
|
48318
|
-
var remoteAssets = ({ version: version2 }) => {
|
|
48319
|
-
const url2 = `https://cdn.jsdelivr.net/npm/swagger-ui-dist${version2 !== undefined ? `@${version2}` : ""}`;
|
|
48320
|
-
return {
|
|
48321
|
-
css: [`${url2}/swagger-ui.css`],
|
|
48322
|
-
js: [`${url2}/swagger-ui-bundle.js`]
|
|
48323
|
-
};
|
|
48324
|
-
};
|
|
48325
|
-
var SwaggerUI = (options) => {
|
|
48326
|
-
const asset = remoteAssets({ version: options?.version });
|
|
48327
|
-
delete options.version;
|
|
48328
|
-
if (options.manuallySwaggerUIHtml)
|
|
48329
|
-
return options.manuallySwaggerUIHtml(asset);
|
|
48330
|
-
const optionsStrings = renderSwaggerUIOptions(options);
|
|
48331
|
-
return `
|
|
48332
|
-
<div>
|
|
48333
|
-
<div id="swagger-ui"></div>
|
|
48334
|
-
${asset.css.map((url2) => html`<link rel="stylesheet" href="${url2}" />`)}
|
|
48335
|
-
${asset.js.map((url2) => html`<script src="${url2}" crossorigin="anonymous"><\/script>`)}
|
|
48336
|
-
<script>
|
|
48337
|
-
window.onload = () => {
|
|
48338
|
-
window.ui = SwaggerUIBundle({
|
|
48339
|
-
dom_id: '#swagger-ui',${optionsStrings},
|
|
48340
|
-
})
|
|
48341
|
-
}
|
|
48342
|
-
</script>
|
|
48343
|
-
</div>
|
|
48344
|
-
`;
|
|
48345
|
-
};
|
|
48346
|
-
var middleware = (options) => async (c) => {
|
|
48347
|
-
const title = options?.title ?? "SwaggerUI";
|
|
48348
|
-
return c.html(`
|
|
48349
|
-
<!doctype html>
|
|
48350
|
-
<html lang="en">
|
|
48351
|
-
<head>
|
|
48352
|
-
<meta charset="utf-8" />
|
|
48353
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
48354
|
-
<meta name="description" content="SwaggerUI" />
|
|
48355
|
-
<title>${title}</title>
|
|
48356
|
-
</head>
|
|
48357
|
-
<body>
|
|
48358
|
-
${SwaggerUI(options)}
|
|
48359
|
-
</body>
|
|
48360
|
-
</html>
|
|
48361
|
-
`);
|
|
48362
|
-
};
|
|
48363
|
-
|
|
48364
48364
|
// node_modules/.bun/hono@4.12.5/node_modules/hono/dist/compose.js
|
|
48365
48365
|
var compose = (middleware2, onError, onNotFound) => {
|
|
48366
48366
|
return (context2, next) => {
|
|
@@ -51277,6 +51277,14 @@ var DEFAULT_CONFIG = `{
|
|
|
51277
51277
|
// },
|
|
51278
51278
|
},
|
|
51279
51279
|
|
|
51280
|
+
// \u901A\u7528\u670D\u52A1\u8BBE\u7F6E
|
|
51281
|
+
server: {
|
|
51282
|
+
// \u9ED8\u8BA4\u5173\u95ED\u5C40\u57DF\u7F51\u8BBF\u95EE\u3002\u5F00\u542F\u540E\uFF0C\u5C40\u57DF\u7F51\u5185\u5176\u4ED6\u8BBE\u5907\u53EF\u8BBF\u95EE\u672C\u670D\u52A1\u3002
|
|
51283
|
+
lanAccess: {
|
|
51284
|
+
enabled: false,
|
|
51285
|
+
},
|
|
51286
|
+
},
|
|
51287
|
+
|
|
51280
51288
|
// \u65E5\u5FD7\u914D\u7F6E\uFF08\u53EF\u9009\uFF0C\u4E0D\u914D\u7F6E\u5219\u4E0D\u542F\u7528\u65E5\u5FD7\u8BB0\u5F55\uFF09
|
|
51281
51289
|
// log: {
|
|
51282
51290
|
// enabled: true,
|
|
@@ -51347,8 +51355,8 @@ function resolveLogBaseDir(logConfig) {
|
|
|
51347
51355
|
}
|
|
51348
51356
|
|
|
51349
51357
|
// src/config-store.ts
|
|
51350
|
-
import { resolve as resolve3 } from "path";
|
|
51351
51358
|
import { writeFileSync as writeFileSync2 } from "fs";
|
|
51359
|
+
import { resolve as resolve3 } from "path";
|
|
51352
51360
|
class ConfigStore {
|
|
51353
51361
|
config;
|
|
51354
51362
|
absolutePath;
|
|
@@ -51384,6 +51392,51 @@ class ConfigStore {
|
|
|
51384
51392
|
}
|
|
51385
51393
|
}
|
|
51386
51394
|
|
|
51395
|
+
// src/config-validate.ts
|
|
51396
|
+
var import__2020 = __toESM(require_2020(), 1);
|
|
51397
|
+
var import_ajv_formats = __toESM(require_dist2(), 1);
|
|
51398
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
51399
|
+
|
|
51400
|
+
// src/runtime-assets.ts
|
|
51401
|
+
import { fileURLToPath } from "url";
|
|
51402
|
+
function resolveBundledAssetPath(relativePath) {
|
|
51403
|
+
return fileURLToPath(new URL(relativePath, import.meta.url));
|
|
51404
|
+
}
|
|
51405
|
+
function getBundledSchemaPath() {
|
|
51406
|
+
return resolveBundledAssetPath("../config.schema.json");
|
|
51407
|
+
}
|
|
51408
|
+
function getBundledWebRoot() {
|
|
51409
|
+
return resolveBundledAssetPath("../dist/web/");
|
|
51410
|
+
}
|
|
51411
|
+
|
|
51412
|
+
// src/config-validate.ts
|
|
51413
|
+
function validateBusinessRules(config2) {
|
|
51414
|
+
for (const [routeType, modelMap] of Object.entries(config2.routes)) {
|
|
51415
|
+
if (!modelMap["*"]) {
|
|
51416
|
+
throw new Error(`\u8DEF\u7531 "${routeType}" \u7F3A\u5C11 "*" \u515C\u5E95\u89C4\u5219`);
|
|
51417
|
+
}
|
|
51418
|
+
for (const target of Object.values(modelMap)) {
|
|
51419
|
+
if (!config2.providers[target.provider]) {
|
|
51420
|
+
throw new Error(`\u8DEF\u7531 "${routeType}" \u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684 provider "${target.provider}"`);
|
|
51421
|
+
}
|
|
51422
|
+
}
|
|
51423
|
+
}
|
|
51424
|
+
}
|
|
51425
|
+
function validateConfigOrThrow(config2) {
|
|
51426
|
+
validateBusinessRules(config2);
|
|
51427
|
+
const ajv = new import__2020.default({ allErrors: true, strict: false });
|
|
51428
|
+
import_ajv_formats.default(ajv);
|
|
51429
|
+
const schemaJson = JSON.parse(readFileSync2(getBundledSchemaPath(), "utf-8"));
|
|
51430
|
+
const validateBySchema = ajv.compile(schemaJson);
|
|
51431
|
+
const valid = validateBySchema(config2);
|
|
51432
|
+
if (!valid) {
|
|
51433
|
+
const firstError = validateBySchema.errors?.[0];
|
|
51434
|
+
const path = firstError?.instancePath || "(root)";
|
|
51435
|
+
const message = firstError?.message ?? "unknown schema validation error";
|
|
51436
|
+
throw new Error(`Schema \u6821\u9A8C\u5931\u8D25: ${path} ${message}`);
|
|
51437
|
+
}
|
|
51438
|
+
}
|
|
51439
|
+
|
|
51387
51440
|
// src/crypto.ts
|
|
51388
51441
|
var ECDH_PARAMS = { name: "ECDH", namedCurve: "P-256" };
|
|
51389
51442
|
function base64Encode(buf) {
|
|
@@ -51736,10 +51789,23 @@ async function getLogMetrics(options) {
|
|
|
51736
51789
|
}
|
|
51737
51790
|
|
|
51738
51791
|
// src/log-query.ts
|
|
51739
|
-
import { createReadStream as
|
|
51740
|
-
import { join as
|
|
51792
|
+
import { createReadStream as createReadStream3, existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
51793
|
+
import { join as join5, resolve as resolve4 } from "path";
|
|
51741
51794
|
import { createInterface as createInterface2 } from "readline";
|
|
51742
51795
|
|
|
51796
|
+
// src/log-index.ts
|
|
51797
|
+
import { Database } from "bun:sqlite";
|
|
51798
|
+
import {
|
|
51799
|
+
closeSync,
|
|
51800
|
+
createReadStream as createReadStream2,
|
|
51801
|
+
existsSync as existsSync3,
|
|
51802
|
+
mkdirSync as mkdirSync2,
|
|
51803
|
+
openSync,
|
|
51804
|
+
readSync,
|
|
51805
|
+
statSync
|
|
51806
|
+
} from "fs";
|
|
51807
|
+
import { join as join4 } from "path";
|
|
51808
|
+
|
|
51743
51809
|
// src/log-session-identity.ts
|
|
51744
51810
|
var USER_SESSION_DELIMITER = "_account__session_";
|
|
51745
51811
|
function toRecord(value) {
|
|
@@ -51816,6 +51882,824 @@ function resolveLogSessionIdentity(requestBody) {
|
|
|
51816
51882
|
};
|
|
51817
51883
|
}
|
|
51818
51884
|
|
|
51885
|
+
// src/log-index.ts
|
|
51886
|
+
var SCHEMA_VERSION = 1;
|
|
51887
|
+
var MAX_INDEX_QUEUE = 20000;
|
|
51888
|
+
var INDEX_BATCH_SIZE = 250;
|
|
51889
|
+
var INDEX_FLUSH_DELAY_MS = 50;
|
|
51890
|
+
var LIKE_SEARCH_THRESHOLD = 2;
|
|
51891
|
+
var FTS_TOKEN_PATTERN = /[\p{L}\p{N}_-]+/gu;
|
|
51892
|
+
var singleton = null;
|
|
51893
|
+
function encodeBase64Url(value) {
|
|
51894
|
+
return Buffer.from(value, "utf-8").toString("base64url");
|
|
51895
|
+
}
|
|
51896
|
+
function decodeBase64Url(value) {
|
|
51897
|
+
return Buffer.from(value, "base64url").toString("utf-8");
|
|
51898
|
+
}
|
|
51899
|
+
function encodeOffsetLogEventId(date5, offset) {
|
|
51900
|
+
return encodeBase64Url(JSON.stringify({ v: 2, d: date5, o: offset }));
|
|
51901
|
+
}
|
|
51902
|
+
function decodeOffsetLogEventId(id) {
|
|
51903
|
+
try {
|
|
51904
|
+
const parsed = JSON.parse(decodeBase64Url(id));
|
|
51905
|
+
if (parsed.v !== 2 || typeof parsed.d !== "string" || !Number.isInteger(parsed.o)) {
|
|
51906
|
+
return null;
|
|
51907
|
+
}
|
|
51908
|
+
const offset = Number(parsed.o);
|
|
51909
|
+
if (offset < 0)
|
|
51910
|
+
return null;
|
|
51911
|
+
return { v: 2, date: parsed.d, offset };
|
|
51912
|
+
} catch {
|
|
51913
|
+
return null;
|
|
51914
|
+
}
|
|
51915
|
+
}
|
|
51916
|
+
function encodeCursor(data) {
|
|
51917
|
+
return encodeBase64Url(JSON.stringify(data));
|
|
51918
|
+
}
|
|
51919
|
+
function decodeCursor(raw2) {
|
|
51920
|
+
const parsed = JSON.parse(decodeBase64Url(raw2));
|
|
51921
|
+
if (parsed.v !== 2 || parsed.sort !== "time_desc" && parsed.sort !== "time_asc" || typeof parsed.id !== "string" || typeof parsed.queryHash !== "string" || !Number.isFinite(parsed.tsMs)) {
|
|
51922
|
+
throw new Error("cursor \u975E\u6CD5");
|
|
51923
|
+
}
|
|
51924
|
+
return parsed;
|
|
51925
|
+
}
|
|
51926
|
+
function toDayStart2(ms) {
|
|
51927
|
+
const date5 = new Date(ms);
|
|
51928
|
+
return Date.UTC(date5.getUTCFullYear(), date5.getUTCMonth(), date5.getUTCDate());
|
|
51929
|
+
}
|
|
51930
|
+
function listDateStrings2(fromMs, toMs) {
|
|
51931
|
+
const result = [];
|
|
51932
|
+
for (let day = toDayStart2(fromMs);day <= toDayStart2(toMs); day += 24 * 60 * 60 * 1000) {
|
|
51933
|
+
result.push(new Date(day).toISOString().slice(0, 10));
|
|
51934
|
+
}
|
|
51935
|
+
return result;
|
|
51936
|
+
}
|
|
51937
|
+
function getStatusClass2(event) {
|
|
51938
|
+
if (event.error_type)
|
|
51939
|
+
return "network_error";
|
|
51940
|
+
const status = event.upstream_status ?? 0;
|
|
51941
|
+
if (status >= 200 && status < 300)
|
|
51942
|
+
return "2xx";
|
|
51943
|
+
if (status >= 400 && status < 500)
|
|
51944
|
+
return "4xx";
|
|
51945
|
+
if (status >= 500)
|
|
51946
|
+
return "5xx";
|
|
51947
|
+
return "network_error";
|
|
51948
|
+
}
|
|
51949
|
+
function isErrorEvent2(event) {
|
|
51950
|
+
if (event.error_type)
|
|
51951
|
+
return true;
|
|
51952
|
+
const status = event.upstream_status ?? 0;
|
|
51953
|
+
return status < 200 || status >= 400;
|
|
51954
|
+
}
|
|
51955
|
+
function getLevel(event) {
|
|
51956
|
+
return isErrorEvent2(event) ? "error" : "info";
|
|
51957
|
+
}
|
|
51958
|
+
function buildMessage(event) {
|
|
51959
|
+
if (event.error_message)
|
|
51960
|
+
return event.error_message;
|
|
51961
|
+
if (event.error_type)
|
|
51962
|
+
return event.error_type;
|
|
51963
|
+
const status = event.upstream_status ?? 0;
|
|
51964
|
+
return `${event.method} ${event.path} -> ${status}`;
|
|
51965
|
+
}
|
|
51966
|
+
function toPercent2(numerator, denominator) {
|
|
51967
|
+
if (denominator <= 0)
|
|
51968
|
+
return 0;
|
|
51969
|
+
return Number((numerator / denominator * 100).toFixed(2));
|
|
51970
|
+
}
|
|
51971
|
+
function hashQuery(query) {
|
|
51972
|
+
const stable = {
|
|
51973
|
+
fromMs: query.fromMs,
|
|
51974
|
+
toMs: query.toMs,
|
|
51975
|
+
levels: [...query.levels].sort(),
|
|
51976
|
+
providers: [...query.providers].sort(),
|
|
51977
|
+
routeTypes: [...query.routeTypes].sort(),
|
|
51978
|
+
models: [...query.models].sort(),
|
|
51979
|
+
modelIns: [...query.modelIns].sort(),
|
|
51980
|
+
modelOuts: [...query.modelOuts].sort(),
|
|
51981
|
+
users: [...query.users].sort(),
|
|
51982
|
+
sessions: [...query.sessions].sort(),
|
|
51983
|
+
statusClasses: [...query.statusClasses].sort(),
|
|
51984
|
+
hasError: query.hasError,
|
|
51985
|
+
q: query.q
|
|
51986
|
+
};
|
|
51987
|
+
return Bun.hash(JSON.stringify(stable)).toString(36);
|
|
51988
|
+
}
|
|
51989
|
+
function buildSearchText(event) {
|
|
51990
|
+
const identity = resolveLogSessionIdentity(event.request_body);
|
|
51991
|
+
return [
|
|
51992
|
+
event.request_id,
|
|
51993
|
+
event.path,
|
|
51994
|
+
event.provider,
|
|
51995
|
+
event.model_in,
|
|
51996
|
+
event.model_out,
|
|
51997
|
+
event.route_type,
|
|
51998
|
+
identity.userIdRaw ?? "",
|
|
51999
|
+
identity.userKey ?? "",
|
|
52000
|
+
identity.sessionId ?? "",
|
|
52001
|
+
event.error_type ?? "",
|
|
52002
|
+
event.error_message ?? "",
|
|
52003
|
+
buildMessage(event)
|
|
52004
|
+
].join(" ").toLowerCase();
|
|
52005
|
+
}
|
|
52006
|
+
function buildFtsQuery(q) {
|
|
52007
|
+
const tokens = q.match(FTS_TOKEN_PATTERN)?.map((token2) => token2.trim()).filter(Boolean) ?? [];
|
|
52008
|
+
if (tokens.length === 0)
|
|
52009
|
+
return null;
|
|
52010
|
+
return tokens.map((token2) => `"${token2.replaceAll('"', '""')}"`).join(" AND ");
|
|
52011
|
+
}
|
|
52012
|
+
function shouldUseFts(q) {
|
|
52013
|
+
return q.trim().length >= LIKE_SEARCH_THRESHOLD && buildFtsQuery(q) !== null;
|
|
52014
|
+
}
|
|
52015
|
+
function escapeLikePattern(value) {
|
|
52016
|
+
return value.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
|
|
52017
|
+
}
|
|
52018
|
+
function eventToRow(input) {
|
|
52019
|
+
const { event } = input;
|
|
52020
|
+
if (!event.ts_start)
|
|
52021
|
+
return null;
|
|
52022
|
+
const tsMs = Date.parse(event.ts_start);
|
|
52023
|
+
if (!Number.isFinite(tsMs))
|
|
52024
|
+
return null;
|
|
52025
|
+
const identity = resolveLogSessionIdentity(event.request_body);
|
|
52026
|
+
const level = getLevel(event);
|
|
52027
|
+
const statusClass = getStatusClass2(event);
|
|
52028
|
+
const latencyMs = Math.max(0, event.latency_ms ?? 0);
|
|
52029
|
+
const model = event.model_out || event.model_in;
|
|
52030
|
+
return {
|
|
52031
|
+
id: input.id,
|
|
52032
|
+
ts_ms: tsMs,
|
|
52033
|
+
ts_start: event.ts_start,
|
|
52034
|
+
level,
|
|
52035
|
+
provider: event.provider,
|
|
52036
|
+
route_type: event.route_type,
|
|
52037
|
+
model,
|
|
52038
|
+
model_in: event.model_in,
|
|
52039
|
+
model_out: event.model_out,
|
|
52040
|
+
path: event.path,
|
|
52041
|
+
request_id: event.request_id,
|
|
52042
|
+
latency_ms: latencyMs,
|
|
52043
|
+
upstream_status: event.upstream_status ?? 0,
|
|
52044
|
+
status_class: statusClass,
|
|
52045
|
+
has_error: level === "error" ? 1 : 0,
|
|
52046
|
+
message: buildMessage(event),
|
|
52047
|
+
error_type: event.error_type,
|
|
52048
|
+
has_metadata: identity.hasMetadata ? 1 : 0,
|
|
52049
|
+
user_id_raw: identity.userIdRaw,
|
|
52050
|
+
user_key: identity.userKey,
|
|
52051
|
+
session_id: identity.sessionId,
|
|
52052
|
+
source_date: input.date,
|
|
52053
|
+
source_file: input.filePath,
|
|
52054
|
+
line_number: input.lineNumber,
|
|
52055
|
+
byte_offset: input.offset,
|
|
52056
|
+
byte_length: input.byteLength,
|
|
52057
|
+
search_text: buildSearchText(event)
|
|
52058
|
+
};
|
|
52059
|
+
}
|
|
52060
|
+
function rowToSummary(row) {
|
|
52061
|
+
return {
|
|
52062
|
+
id: row.id,
|
|
52063
|
+
ts: row.ts_start,
|
|
52064
|
+
level: row.level,
|
|
52065
|
+
provider: row.provider,
|
|
52066
|
+
routeType: row.route_type,
|
|
52067
|
+
model: row.model,
|
|
52068
|
+
modelIn: row.model_in,
|
|
52069
|
+
modelOut: row.model_out,
|
|
52070
|
+
path: row.path,
|
|
52071
|
+
requestId: row.request_id,
|
|
52072
|
+
latencyMs: row.latency_ms,
|
|
52073
|
+
upstreamStatus: row.upstream_status,
|
|
52074
|
+
statusClass: row.status_class,
|
|
52075
|
+
hasError: row.has_error === 1,
|
|
52076
|
+
message: row.message,
|
|
52077
|
+
errorType: row.error_type,
|
|
52078
|
+
hasMetadata: row.has_metadata === 1,
|
|
52079
|
+
userIdRaw: row.user_id_raw,
|
|
52080
|
+
userKey: row.user_key,
|
|
52081
|
+
sessionId: row.session_id
|
|
52082
|
+
};
|
|
52083
|
+
}
|
|
52084
|
+
async function* readJsonlLinesWithOffsets(filePath) {
|
|
52085
|
+
const stream = createReadStream2(filePath);
|
|
52086
|
+
let buffer2 = Buffer.alloc(0);
|
|
52087
|
+
let bufferOffset = 0;
|
|
52088
|
+
let lineNumber = 0;
|
|
52089
|
+
for await (const chunk of stream) {
|
|
52090
|
+
const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
52091
|
+
buffer2 = buffer2.length === 0 ? chunkBuffer : Buffer.concat([buffer2, chunkBuffer]);
|
|
52092
|
+
let newlineIndex = buffer2.indexOf(10);
|
|
52093
|
+
while (newlineIndex !== -1) {
|
|
52094
|
+
const lineBuffer = buffer2.subarray(0, newlineIndex);
|
|
52095
|
+
const byteLength = newlineIndex + 1;
|
|
52096
|
+
const lineOffset = bufferOffset;
|
|
52097
|
+
lineNumber += 1;
|
|
52098
|
+
yield {
|
|
52099
|
+
line: lineBuffer.toString("utf-8").replace(/\r$/, ""),
|
|
52100
|
+
offset: lineOffset,
|
|
52101
|
+
lineNumber,
|
|
52102
|
+
byteLength
|
|
52103
|
+
};
|
|
52104
|
+
buffer2 = buffer2.subarray(byteLength);
|
|
52105
|
+
bufferOffset += byteLength;
|
|
52106
|
+
newlineIndex = buffer2.indexOf(10);
|
|
52107
|
+
}
|
|
52108
|
+
}
|
|
52109
|
+
if (buffer2.length > 0) {
|
|
52110
|
+
lineNumber += 1;
|
|
52111
|
+
yield {
|
|
52112
|
+
line: buffer2.toString("utf-8").replace(/\r$/, ""),
|
|
52113
|
+
offset: bufferOffset,
|
|
52114
|
+
lineNumber,
|
|
52115
|
+
byteLength: buffer2.length
|
|
52116
|
+
};
|
|
52117
|
+
}
|
|
52118
|
+
}
|
|
52119
|
+
function readLineAtOffset(filePath, offset) {
|
|
52120
|
+
const fd = openSync(filePath, "r");
|
|
52121
|
+
try {
|
|
52122
|
+
const chunks = [];
|
|
52123
|
+
const buffer2 = Buffer.allocUnsafe(64 * 1024);
|
|
52124
|
+
let position = offset;
|
|
52125
|
+
while (true) {
|
|
52126
|
+
const bytesRead = readSync(fd, buffer2, 0, buffer2.length, position);
|
|
52127
|
+
if (bytesRead <= 0)
|
|
52128
|
+
break;
|
|
52129
|
+
const readable = buffer2.subarray(0, bytesRead);
|
|
52130
|
+
const newline = readable.indexOf(10);
|
|
52131
|
+
if (newline >= 0 && newline < bytesRead) {
|
|
52132
|
+
chunks.push(Buffer.from(readable.subarray(0, newline)));
|
|
52133
|
+
break;
|
|
52134
|
+
}
|
|
52135
|
+
chunks.push(Buffer.from(readable));
|
|
52136
|
+
position += bytesRead;
|
|
52137
|
+
}
|
|
52138
|
+
if (chunks.length === 0)
|
|
52139
|
+
return null;
|
|
52140
|
+
return Buffer.concat(chunks).toString("utf-8").replace(/\r$/, "");
|
|
52141
|
+
} finally {
|
|
52142
|
+
closeSync(fd);
|
|
52143
|
+
}
|
|
52144
|
+
}
|
|
52145
|
+
function createEmptyStats() {
|
|
52146
|
+
return {
|
|
52147
|
+
total: 0,
|
|
52148
|
+
errorCount: 0,
|
|
52149
|
+
errorRate: 0,
|
|
52150
|
+
avgLatencyMs: 0,
|
|
52151
|
+
p95LatencyMs: 0
|
|
52152
|
+
};
|
|
52153
|
+
}
|
|
52154
|
+
function createEmptyQueryResult(query, meta3 = {}) {
|
|
52155
|
+
return {
|
|
52156
|
+
items: [],
|
|
52157
|
+
nextCursor: null,
|
|
52158
|
+
hasMore: false,
|
|
52159
|
+
stats: createEmptyStats(),
|
|
52160
|
+
meta: {
|
|
52161
|
+
scannedFiles: 0,
|
|
52162
|
+
scannedLines: 0,
|
|
52163
|
+
parseErrors: 0,
|
|
52164
|
+
truncated: false,
|
|
52165
|
+
indexUsed: true,
|
|
52166
|
+
indexFresh: true,
|
|
52167
|
+
usesFts: shouldUseFts(query.q),
|
|
52168
|
+
queryMs: 0,
|
|
52169
|
+
rowsReturned: 0,
|
|
52170
|
+
statsMode: "exact",
|
|
52171
|
+
...meta3
|
|
52172
|
+
}
|
|
52173
|
+
};
|
|
52174
|
+
}
|
|
52175
|
+
function appendInClause(clauses, params, column2, values) {
|
|
52176
|
+
if (values.length === 0)
|
|
52177
|
+
return;
|
|
52178
|
+
clauses.push(`${column2} IN (${values.map(() => "?").join(", ")})`);
|
|
52179
|
+
params.push(...values);
|
|
52180
|
+
}
|
|
52181
|
+
function buildWhereClause(query, options = {}) {
|
|
52182
|
+
const clauses = ["e.ts_ms >= ?", "e.ts_ms <= ?"];
|
|
52183
|
+
const params = [query.fromMs, query.toMs];
|
|
52184
|
+
const usesFts = !options.forceLikeSearch && shouldUseFts(query.q);
|
|
52185
|
+
appendInClause(clauses, params, "e.level", query.levels);
|
|
52186
|
+
appendInClause(clauses, params, "e.provider", query.providers);
|
|
52187
|
+
appendInClause(clauses, params, "e.route_type", query.routeTypes);
|
|
52188
|
+
appendInClause(clauses, params, "e.model", query.models);
|
|
52189
|
+
appendInClause(clauses, params, "e.model_in", query.modelIns);
|
|
52190
|
+
appendInClause(clauses, params, "e.model_out", query.modelOuts);
|
|
52191
|
+
appendInClause(clauses, params, "e.status_class", query.statusClasses);
|
|
52192
|
+
if (query.users.length > 0) {
|
|
52193
|
+
clauses.push(`(e.user_id_raw IN (${query.users.map(() => "?").join(", ")}) OR e.user_key IN (${query.users.map(() => "?").join(", ")}))`);
|
|
52194
|
+
params.push(...query.users, ...query.users);
|
|
52195
|
+
}
|
|
52196
|
+
appendInClause(clauses, params, "e.session_id", query.sessions);
|
|
52197
|
+
if (query.hasError !== null) {
|
|
52198
|
+
clauses.push("e.has_error = ?");
|
|
52199
|
+
params.push(query.hasError ? 1 : 0);
|
|
52200
|
+
}
|
|
52201
|
+
if (query.q) {
|
|
52202
|
+
if (usesFts) {
|
|
52203
|
+
const ftsQuery = buildFtsQuery(query.q);
|
|
52204
|
+
clauses.push("e.id IN (SELECT event_id FROM log_events_fts WHERE log_events_fts MATCH ?)");
|
|
52205
|
+
params.push(ftsQuery);
|
|
52206
|
+
} else {
|
|
52207
|
+
clauses.push("e.search_text LIKE ? ESCAPE '\\'");
|
|
52208
|
+
params.push(`%${escapeLikePattern(query.q.toLowerCase())}%`);
|
|
52209
|
+
}
|
|
52210
|
+
}
|
|
52211
|
+
return {
|
|
52212
|
+
whereSql: clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "",
|
|
52213
|
+
params,
|
|
52214
|
+
usesFts
|
|
52215
|
+
};
|
|
52216
|
+
}
|
|
52217
|
+
|
|
52218
|
+
class LogIndex {
|
|
52219
|
+
baseDir;
|
|
52220
|
+
config;
|
|
52221
|
+
db;
|
|
52222
|
+
queue = [];
|
|
52223
|
+
flushTimer = null;
|
|
52224
|
+
disposed = false;
|
|
52225
|
+
rebuildingFiles = new Set;
|
|
52226
|
+
dirtyFiles = new Set;
|
|
52227
|
+
insertEventStmt;
|
|
52228
|
+
insertFtsStmt;
|
|
52229
|
+
deleteFtsStmt;
|
|
52230
|
+
upsertFileStmt;
|
|
52231
|
+
constructor(baseDir, config2) {
|
|
52232
|
+
this.baseDir = baseDir;
|
|
52233
|
+
this.config = config2;
|
|
52234
|
+
mkdirSync2(baseDir, { recursive: true });
|
|
52235
|
+
const dbPath = join4(baseDir, "logs-index.sqlite");
|
|
52236
|
+
this.db = new Database(dbPath, { create: true, strict: true });
|
|
52237
|
+
this.configure();
|
|
52238
|
+
this.migrate();
|
|
52239
|
+
this.insertEventStmt = this.db.prepare(`
|
|
52240
|
+
INSERT OR REPLACE INTO log_events (
|
|
52241
|
+
id, ts_ms, ts_start, level, provider, route_type, model, model_in, model_out,
|
|
52242
|
+
path, request_id, latency_ms, upstream_status, status_class, has_error,
|
|
52243
|
+
message, error_type, has_metadata, user_id_raw, user_key, session_id,
|
|
52244
|
+
source_date, source_file, line_number, byte_offset, byte_length, search_text
|
|
52245
|
+
) VALUES (
|
|
52246
|
+
$id, $ts_ms, $ts_start, $level, $provider, $route_type, $model, $model_in, $model_out,
|
|
52247
|
+
$path, $request_id, $latency_ms, $upstream_status, $status_class, $has_error,
|
|
52248
|
+
$message, $error_type, $has_metadata, $user_id_raw, $user_key, $session_id,
|
|
52249
|
+
$source_date, $source_file, $line_number, $byte_offset, $byte_length, $search_text
|
|
52250
|
+
)
|
|
52251
|
+
`);
|
|
52252
|
+
this.deleteFtsStmt = this.db.prepare("DELETE FROM log_events_fts WHERE event_id = ?");
|
|
52253
|
+
this.insertFtsStmt = this.db.prepare("INSERT INTO log_events_fts(event_id, search_text) VALUES (?, ?)");
|
|
52254
|
+
this.upsertFileStmt = this.db.prepare(`
|
|
52255
|
+
INSERT INTO log_index_files(file_path, source_date, size_bytes, mtime_ms, indexed_at)
|
|
52256
|
+
VALUES (?, ?, ?, ?, ?)
|
|
52257
|
+
ON CONFLICT(file_path) DO UPDATE SET
|
|
52258
|
+
source_date = excluded.source_date,
|
|
52259
|
+
size_bytes = excluded.size_bytes,
|
|
52260
|
+
mtime_ms = excluded.mtime_ms,
|
|
52261
|
+
indexed_at = excluded.indexed_at
|
|
52262
|
+
`);
|
|
52263
|
+
}
|
|
52264
|
+
dispose() {
|
|
52265
|
+
if (this.flushTimer) {
|
|
52266
|
+
clearTimeout(this.flushTimer);
|
|
52267
|
+
this.flushTimer = null;
|
|
52268
|
+
}
|
|
52269
|
+
this.queue = [];
|
|
52270
|
+
this.disposed = true;
|
|
52271
|
+
this.db.close();
|
|
52272
|
+
}
|
|
52273
|
+
enqueue(item) {
|
|
52274
|
+
if (this.disposed)
|
|
52275
|
+
return;
|
|
52276
|
+
if (this.queue.length >= MAX_INDEX_QUEUE) {
|
|
52277
|
+
const dropped = this.queue.shift();
|
|
52278
|
+
if (dropped) {
|
|
52279
|
+
this.dirtyFiles.add(dropped.filePath);
|
|
52280
|
+
}
|
|
52281
|
+
}
|
|
52282
|
+
this.queue.push(item);
|
|
52283
|
+
if (!this.flushTimer) {
|
|
52284
|
+
this.flushTimer = setTimeout(() => {
|
|
52285
|
+
this.flushTimer = null;
|
|
52286
|
+
this.flushQueue();
|
|
52287
|
+
}, INDEX_FLUSH_DELAY_MS);
|
|
52288
|
+
this.flushTimer.unref?.();
|
|
52289
|
+
}
|
|
52290
|
+
}
|
|
52291
|
+
async ensureRangeIndexed(fromMs, toMs) {
|
|
52292
|
+
let scannedFiles = 0;
|
|
52293
|
+
let scannedLines = 0;
|
|
52294
|
+
let parseErrors = 0;
|
|
52295
|
+
const eventsDir = join4(this.baseDir, "events");
|
|
52296
|
+
const dates = listDateStrings2(fromMs, toMs);
|
|
52297
|
+
for (const date5 of dates) {
|
|
52298
|
+
const filePath = join4(eventsDir, `${date5}.jsonl`);
|
|
52299
|
+
if (!existsSync3(filePath))
|
|
52300
|
+
continue;
|
|
52301
|
+
const stats = statSync(filePath);
|
|
52302
|
+
const fileRow = this.db.query("SELECT size_bytes, mtime_ms FROM log_index_files WHERE file_path = ?").get(filePath);
|
|
52303
|
+
const sizeBytes = stats.size;
|
|
52304
|
+
const mtimeMs = Math.trunc(stats.mtimeMs);
|
|
52305
|
+
if (!this.dirtyFiles.has(filePath) && fileRow && fileRow.size_bytes === sizeBytes && fileRow.mtime_ms === mtimeMs) {
|
|
52306
|
+
continue;
|
|
52307
|
+
}
|
|
52308
|
+
const result = await this.rebuildFile(filePath, date5, sizeBytes, mtimeMs);
|
|
52309
|
+
scannedFiles += 1;
|
|
52310
|
+
scannedLines += result.scannedLines;
|
|
52311
|
+
parseErrors += result.parseErrors;
|
|
52312
|
+
}
|
|
52313
|
+
return { scannedFiles, scannedLines, parseErrors };
|
|
52314
|
+
}
|
|
52315
|
+
queryEvents(query, options = {}) {
|
|
52316
|
+
const startedAt = performance.now();
|
|
52317
|
+
const queryHash = hashQuery(query);
|
|
52318
|
+
const decodedCursor = query.cursor ? decodeCursor(query.cursor) : null;
|
|
52319
|
+
if (decodedCursor) {
|
|
52320
|
+
if (decodedCursor.sort !== query.sort || decodedCursor.queryHash !== queryHash) {
|
|
52321
|
+
throw new Error("cursor \u4E0E\u5F53\u524D\u67E5\u8BE2\u6761\u4EF6\u4E0D\u5339\u914D");
|
|
52322
|
+
}
|
|
52323
|
+
}
|
|
52324
|
+
const { whereSql, params, usesFts } = buildWhereClause(query, options);
|
|
52325
|
+
const cursorClause = decodedCursor ? query.sort === "time_desc" ? "AND (e.ts_ms < ? OR (e.ts_ms = ? AND e.id < ?))" : "AND (e.ts_ms > ? OR (e.ts_ms = ? AND e.id > ?))" : "";
|
|
52326
|
+
const cursorParams = decodedCursor ? [decodedCursor.tsMs, decodedCursor.tsMs, decodedCursor.id] : [];
|
|
52327
|
+
const orderSql = query.sort === "time_desc" ? "ORDER BY e.ts_ms DESC, e.id DESC" : "ORDER BY e.ts_ms ASC, e.id ASC";
|
|
52328
|
+
const limit = Math.max(1, query.limit);
|
|
52329
|
+
let rows;
|
|
52330
|
+
try {
|
|
52331
|
+
rows = this.db.query(`
|
|
52332
|
+
SELECT
|
|
52333
|
+
e.id, e.ts_start, e.level, e.provider, e.route_type, e.model, e.model_in,
|
|
52334
|
+
e.model_out, e.path, e.request_id, e.latency_ms, e.upstream_status,
|
|
52335
|
+
e.status_class, e.has_error, e.message, e.error_type, e.has_metadata,
|
|
52336
|
+
e.user_id_raw, e.user_key, e.session_id, e.ts_ms
|
|
52337
|
+
FROM log_events e
|
|
52338
|
+
${whereSql}
|
|
52339
|
+
${cursorClause}
|
|
52340
|
+
${orderSql}
|
|
52341
|
+
LIMIT ?
|
|
52342
|
+
`).all(...params, ...cursorParams, limit + 1);
|
|
52343
|
+
} catch (err) {
|
|
52344
|
+
if (!usesFts)
|
|
52345
|
+
throw err;
|
|
52346
|
+
const fallback = this.queryEvents(query, { forceLikeSearch: true });
|
|
52347
|
+
return {
|
|
52348
|
+
...fallback,
|
|
52349
|
+
meta: {
|
|
52350
|
+
...fallback.meta,
|
|
52351
|
+
usesFts: false,
|
|
52352
|
+
fallbackReason: `FTS \u67E5\u8BE2\u5931\u8D25\uFF0C\u5DF2\u9000\u56DE\u7D22\u5F15 LIKE/\u5185\u5B58\u8FC7\u6EE4: ${err instanceof Error ? err.message : String(err)}`
|
|
52353
|
+
}
|
|
52354
|
+
};
|
|
52355
|
+
}
|
|
52356
|
+
const pageRows = rows.slice(0, limit);
|
|
52357
|
+
const hasMore = rows.length > limit;
|
|
52358
|
+
const lastRow = pageRows[pageRows.length - 1];
|
|
52359
|
+
const stats = this.queryStats(whereSql, params);
|
|
52360
|
+
return {
|
|
52361
|
+
items: pageRows.map(rowToSummary),
|
|
52362
|
+
nextCursor: hasMore && lastRow ? encodeCursor({
|
|
52363
|
+
v: 2,
|
|
52364
|
+
sort: query.sort,
|
|
52365
|
+
tsMs: lastRow.ts_ms,
|
|
52366
|
+
id: lastRow.id,
|
|
52367
|
+
queryHash
|
|
52368
|
+
}) : null,
|
|
52369
|
+
hasMore,
|
|
52370
|
+
stats,
|
|
52371
|
+
meta: {
|
|
52372
|
+
scannedFiles: 0,
|
|
52373
|
+
scannedLines: 0,
|
|
52374
|
+
parseErrors: 0,
|
|
52375
|
+
truncated: false,
|
|
52376
|
+
indexUsed: true,
|
|
52377
|
+
indexFresh: true,
|
|
52378
|
+
usesFts,
|
|
52379
|
+
queryMs: Math.round((performance.now() - startedAt) * 100) / 100,
|
|
52380
|
+
rowsReturned: pageRows.length,
|
|
52381
|
+
statsMode: "exact"
|
|
52382
|
+
}
|
|
52383
|
+
};
|
|
52384
|
+
}
|
|
52385
|
+
getEventRecordByOffsetId(id) {
|
|
52386
|
+
const parsedId = decodeOffsetLogEventId(id);
|
|
52387
|
+
if (!parsedId)
|
|
52388
|
+
return null;
|
|
52389
|
+
const row = this.db.query("SELECT source_date, source_file, line_number, byte_offset FROM log_events WHERE id = ?").get(id);
|
|
52390
|
+
const filePath = row?.source_file ?? join4(this.baseDir, "events", `${parsedId.date}.jsonl`);
|
|
52391
|
+
if (!existsSync3(filePath))
|
|
52392
|
+
return null;
|
|
52393
|
+
const line2 = readLineAtOffset(filePath, row?.byte_offset ?? parsedId.offset);
|
|
52394
|
+
if (!line2?.trim())
|
|
52395
|
+
return null;
|
|
52396
|
+
const event = JSON.parse(line2);
|
|
52397
|
+
return {
|
|
52398
|
+
event,
|
|
52399
|
+
location: {
|
|
52400
|
+
id,
|
|
52401
|
+
date: row?.source_date ?? parsedId.date,
|
|
52402
|
+
file: filePath,
|
|
52403
|
+
line: row?.line_number ?? null,
|
|
52404
|
+
offset: row?.byte_offset ?? parsedId.offset
|
|
52405
|
+
}
|
|
52406
|
+
};
|
|
52407
|
+
}
|
|
52408
|
+
configure() {
|
|
52409
|
+
this.db.exec(`
|
|
52410
|
+
PRAGMA journal_mode = WAL;
|
|
52411
|
+
PRAGMA synchronous = NORMAL;
|
|
52412
|
+
PRAGMA temp_store = MEMORY;
|
|
52413
|
+
PRAGMA busy_timeout = 3000;
|
|
52414
|
+
PRAGMA foreign_keys = ON;
|
|
52415
|
+
`);
|
|
52416
|
+
}
|
|
52417
|
+
migrate() {
|
|
52418
|
+
this.db.exec(`
|
|
52419
|
+
CREATE TABLE IF NOT EXISTS log_index_meta (
|
|
52420
|
+
key TEXT PRIMARY KEY,
|
|
52421
|
+
value TEXT NOT NULL
|
|
52422
|
+
);
|
|
52423
|
+
|
|
52424
|
+
CREATE TABLE IF NOT EXISTS log_index_files (
|
|
52425
|
+
file_path TEXT PRIMARY KEY,
|
|
52426
|
+
source_date TEXT NOT NULL,
|
|
52427
|
+
size_bytes INTEGER NOT NULL,
|
|
52428
|
+
mtime_ms INTEGER NOT NULL,
|
|
52429
|
+
indexed_at INTEGER NOT NULL
|
|
52430
|
+
);
|
|
52431
|
+
|
|
52432
|
+
CREATE TABLE IF NOT EXISTS log_events (
|
|
52433
|
+
id TEXT PRIMARY KEY,
|
|
52434
|
+
ts_ms INTEGER NOT NULL,
|
|
52435
|
+
ts_start TEXT NOT NULL,
|
|
52436
|
+
level TEXT NOT NULL,
|
|
52437
|
+
provider TEXT NOT NULL,
|
|
52438
|
+
route_type TEXT NOT NULL,
|
|
52439
|
+
model TEXT NOT NULL,
|
|
52440
|
+
model_in TEXT NOT NULL,
|
|
52441
|
+
model_out TEXT NOT NULL,
|
|
52442
|
+
path TEXT NOT NULL,
|
|
52443
|
+
request_id TEXT NOT NULL,
|
|
52444
|
+
latency_ms INTEGER NOT NULL,
|
|
52445
|
+
upstream_status INTEGER NOT NULL,
|
|
52446
|
+
status_class TEXT NOT NULL,
|
|
52447
|
+
has_error INTEGER NOT NULL,
|
|
52448
|
+
message TEXT NOT NULL,
|
|
52449
|
+
error_type TEXT,
|
|
52450
|
+
has_metadata INTEGER NOT NULL,
|
|
52451
|
+
user_id_raw TEXT,
|
|
52452
|
+
user_key TEXT,
|
|
52453
|
+
session_id TEXT,
|
|
52454
|
+
source_date TEXT NOT NULL,
|
|
52455
|
+
source_file TEXT NOT NULL,
|
|
52456
|
+
line_number INTEGER,
|
|
52457
|
+
byte_offset INTEGER NOT NULL,
|
|
52458
|
+
byte_length INTEGER NOT NULL,
|
|
52459
|
+
search_text TEXT NOT NULL
|
|
52460
|
+
);
|
|
52461
|
+
|
|
52462
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS log_events_fts
|
|
52463
|
+
USING fts5(event_id UNINDEXED, search_text);
|
|
52464
|
+
|
|
52465
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_time_desc ON log_events(ts_ms DESC, id DESC);
|
|
52466
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_time_asc ON log_events(ts_ms ASC, id ASC);
|
|
52467
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_level_time ON log_events(level, ts_ms DESC);
|
|
52468
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_provider_time ON log_events(provider, ts_ms DESC);
|
|
52469
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_route_time ON log_events(route_type, ts_ms DESC);
|
|
52470
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_model_time ON log_events(model, ts_ms DESC);
|
|
52471
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_status_time ON log_events(status_class, ts_ms DESC);
|
|
52472
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_error_time ON log_events(has_error, ts_ms DESC);
|
|
52473
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_user_time ON log_events(user_key, ts_ms DESC);
|
|
52474
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_session_time ON log_events(session_id, ts_ms DESC);
|
|
52475
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_file ON log_events(source_file);
|
|
52476
|
+
`);
|
|
52477
|
+
this.db.prepare(`
|
|
52478
|
+
INSERT INTO log_index_meta(key, value)
|
|
52479
|
+
VALUES ('schema_version', ?)
|
|
52480
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
52481
|
+
`).run(String(SCHEMA_VERSION));
|
|
52482
|
+
}
|
|
52483
|
+
flushQueue() {
|
|
52484
|
+
if (this.queue.length === 0 || this.disposed)
|
|
52485
|
+
return;
|
|
52486
|
+
const batch = this.queue.splice(0, INDEX_BATCH_SIZE);
|
|
52487
|
+
const transaction = this.db.transaction((items) => {
|
|
52488
|
+
for (const item of items) {
|
|
52489
|
+
this.insertQueueItem(item);
|
|
52490
|
+
}
|
|
52491
|
+
});
|
|
52492
|
+
try {
|
|
52493
|
+
transaction(batch);
|
|
52494
|
+
} catch (err) {
|
|
52495
|
+
console.error("[log-index] \u589E\u91CF\u7D22\u5F15\u5199\u5165\u5931\u8D25:", err);
|
|
52496
|
+
}
|
|
52497
|
+
if (this.queue.length > 0 && !this.flushTimer) {
|
|
52498
|
+
this.flushTimer = setTimeout(() => {
|
|
52499
|
+
this.flushTimer = null;
|
|
52500
|
+
this.flushQueue();
|
|
52501
|
+
}, INDEX_FLUSH_DELAY_MS);
|
|
52502
|
+
this.flushTimer.unref?.();
|
|
52503
|
+
}
|
|
52504
|
+
}
|
|
52505
|
+
insertQueueItem(item) {
|
|
52506
|
+
if (this.rebuildingFiles.has(item.filePath))
|
|
52507
|
+
return;
|
|
52508
|
+
const id = encodeOffsetLogEventId(item.date, item.offset);
|
|
52509
|
+
const row = eventToRow({
|
|
52510
|
+
id,
|
|
52511
|
+
date: item.date,
|
|
52512
|
+
filePath: item.filePath,
|
|
52513
|
+
lineNumber: null,
|
|
52514
|
+
offset: item.offset,
|
|
52515
|
+
byteLength: item.byteLength,
|
|
52516
|
+
event: item.event
|
|
52517
|
+
});
|
|
52518
|
+
if (!row)
|
|
52519
|
+
return;
|
|
52520
|
+
this.insertEventStmt.run(row);
|
|
52521
|
+
this.deleteFtsStmt.run(id);
|
|
52522
|
+
this.insertFtsStmt.run(id, row.search_text);
|
|
52523
|
+
if (!this.dirtyFiles.has(item.filePath)) {
|
|
52524
|
+
try {
|
|
52525
|
+
const stats = statSync(item.filePath);
|
|
52526
|
+
const indexedThrough = item.offset + item.byteLength;
|
|
52527
|
+
this.upsertFileStmt.run(item.filePath, item.date, Math.min(indexedThrough, stats.size), Math.trunc(stats.mtimeMs), Date.now());
|
|
52528
|
+
} catch {}
|
|
52529
|
+
}
|
|
52530
|
+
}
|
|
52531
|
+
async rebuildFile(filePath, date5, sizeBytes, mtimeMs) {
|
|
52532
|
+
if (this.rebuildingFiles.has(filePath)) {
|
|
52533
|
+
return { scannedLines: 0, parseErrors: 0 };
|
|
52534
|
+
}
|
|
52535
|
+
this.rebuildingFiles.add(filePath);
|
|
52536
|
+
let scannedLines = 0;
|
|
52537
|
+
let parseErrors = 0;
|
|
52538
|
+
const rows = [];
|
|
52539
|
+
try {
|
|
52540
|
+
for await (const item of readJsonlLinesWithOffsets(filePath)) {
|
|
52541
|
+
scannedLines += 1;
|
|
52542
|
+
if (!item.line.trim())
|
|
52543
|
+
continue;
|
|
52544
|
+
let event;
|
|
52545
|
+
try {
|
|
52546
|
+
event = JSON.parse(item.line);
|
|
52547
|
+
} catch {
|
|
52548
|
+
parseErrors += 1;
|
|
52549
|
+
continue;
|
|
52550
|
+
}
|
|
52551
|
+
const row = eventToRow({
|
|
52552
|
+
id: encodeOffsetLogEventId(date5, item.offset),
|
|
52553
|
+
date: date5,
|
|
52554
|
+
filePath,
|
|
52555
|
+
lineNumber: item.lineNumber,
|
|
52556
|
+
offset: item.offset,
|
|
52557
|
+
byteLength: item.byteLength,
|
|
52558
|
+
event
|
|
52559
|
+
});
|
|
52560
|
+
if (row)
|
|
52561
|
+
rows.push(row);
|
|
52562
|
+
}
|
|
52563
|
+
const transaction = this.db.transaction((eventRows) => {
|
|
52564
|
+
this.db.prepare("DELETE FROM log_events_fts WHERE event_id IN (SELECT id FROM log_events WHERE source_file = ?)").run(filePath);
|
|
52565
|
+
this.db.prepare("DELETE FROM log_events WHERE source_file = ?").run(filePath);
|
|
52566
|
+
for (const row of eventRows) {
|
|
52567
|
+
this.insertEventStmt.run(row);
|
|
52568
|
+
this.insertFtsStmt.run(row.id, row.search_text);
|
|
52569
|
+
}
|
|
52570
|
+
this.upsertFileStmt.run(filePath, date5, sizeBytes, mtimeMs, Date.now());
|
|
52571
|
+
});
|
|
52572
|
+
transaction(rows);
|
|
52573
|
+
this.dirtyFiles.delete(filePath);
|
|
52574
|
+
return { scannedLines, parseErrors };
|
|
52575
|
+
} finally {
|
|
52576
|
+
this.rebuildingFiles.delete(filePath);
|
|
52577
|
+
}
|
|
52578
|
+
}
|
|
52579
|
+
queryStats(whereSql, params) {
|
|
52580
|
+
const aggregate = this.db.query(`
|
|
52581
|
+
SELECT
|
|
52582
|
+
COUNT(*) AS total,
|
|
52583
|
+
COALESCE(SUM(has_error), 0) AS errorCount,
|
|
52584
|
+
COALESCE(AVG(latency_ms), 0) AS avgLatencyMs
|
|
52585
|
+
FROM log_events e
|
|
52586
|
+
${whereSql}
|
|
52587
|
+
`).get(...params);
|
|
52588
|
+
const total = Number(aggregate.total) || 0;
|
|
52589
|
+
if (total <= 0)
|
|
52590
|
+
return createEmptyStats();
|
|
52591
|
+
const p95Offset = Math.max(0, Math.ceil(total * 0.95) - 1);
|
|
52592
|
+
const p95Row = this.db.query(`
|
|
52593
|
+
SELECT latency_ms
|
|
52594
|
+
FROM log_events e
|
|
52595
|
+
${whereSql}
|
|
52596
|
+
ORDER BY latency_ms ASC
|
|
52597
|
+
LIMIT 1 OFFSET ?
|
|
52598
|
+
`).get(...params, p95Offset);
|
|
52599
|
+
const errorCount = Number(aggregate.errorCount) || 0;
|
|
52600
|
+
return {
|
|
52601
|
+
total,
|
|
52602
|
+
errorCount,
|
|
52603
|
+
errorRate: toPercent2(errorCount, total),
|
|
52604
|
+
avgLatencyMs: Math.round(Number(aggregate.avgLatencyMs) || 0),
|
|
52605
|
+
p95LatencyMs: Math.round(p95Row?.latency_ms ?? 0)
|
|
52606
|
+
};
|
|
52607
|
+
}
|
|
52608
|
+
}
|
|
52609
|
+
function initLogIndex(baseDir, config2) {
|
|
52610
|
+
disposeLogIndex();
|
|
52611
|
+
if (config2?.enabled === false)
|
|
52612
|
+
return;
|
|
52613
|
+
try {
|
|
52614
|
+
singleton = new LogIndex(baseDir, config2);
|
|
52615
|
+
} catch (err) {
|
|
52616
|
+
singleton = null;
|
|
52617
|
+
console.error("[log-index] SQLite \u7D22\u5F15\u521D\u59CB\u5316\u5931\u8D25\uFF0C\u5C06\u9000\u56DE JSONL \u626B\u63CF:", err);
|
|
52618
|
+
}
|
|
52619
|
+
}
|
|
52620
|
+
function disposeLogIndex() {
|
|
52621
|
+
if (!singleton)
|
|
52622
|
+
return;
|
|
52623
|
+
try {
|
|
52624
|
+
singleton.dispose();
|
|
52625
|
+
} catch (err) {
|
|
52626
|
+
console.error("[log-index] SQLite \u7D22\u5F15\u5173\u95ED\u5931\u8D25:", err);
|
|
52627
|
+
} finally {
|
|
52628
|
+
singleton = null;
|
|
52629
|
+
}
|
|
52630
|
+
}
|
|
52631
|
+
function getLogIndex(baseDir) {
|
|
52632
|
+
if (!singleton)
|
|
52633
|
+
return null;
|
|
52634
|
+
if (baseDir && singleton.baseDir !== baseDir)
|
|
52635
|
+
return null;
|
|
52636
|
+
return singleton;
|
|
52637
|
+
}
|
|
52638
|
+
function enqueueLogEventForIndex(input) {
|
|
52639
|
+
const index = getLogIndex(input.baseDir);
|
|
52640
|
+
index?.enqueue(input);
|
|
52641
|
+
}
|
|
52642
|
+
async function queryIndexedLogEvents(logConfig, query) {
|
|
52643
|
+
if (!logConfig || logConfig.enabled === false)
|
|
52644
|
+
return createEmptyQueryResult(query);
|
|
52645
|
+
const baseDir = resolveLogBaseDir(logConfig);
|
|
52646
|
+
const index = getLogIndex(baseDir);
|
|
52647
|
+
if (!index)
|
|
52648
|
+
return null;
|
|
52649
|
+
try {
|
|
52650
|
+
const freshness = await index.ensureRangeIndexed(query.fromMs, query.toMs);
|
|
52651
|
+
const result = index.queryEvents(query);
|
|
52652
|
+
return {
|
|
52653
|
+
...result,
|
|
52654
|
+
meta: {
|
|
52655
|
+
...result.meta,
|
|
52656
|
+
scannedFiles: freshness.scannedFiles,
|
|
52657
|
+
scannedLines: freshness.scannedLines,
|
|
52658
|
+
parseErrors: freshness.parseErrors
|
|
52659
|
+
}
|
|
52660
|
+
};
|
|
52661
|
+
} catch (err) {
|
|
52662
|
+
if (err instanceof Error && err.message.includes("cursor")) {
|
|
52663
|
+
throw err;
|
|
52664
|
+
}
|
|
52665
|
+
return {
|
|
52666
|
+
...createEmptyQueryResult(query, {
|
|
52667
|
+
indexUsed: false,
|
|
52668
|
+
indexFresh: false,
|
|
52669
|
+
fallbackReason: err instanceof Error ? err.message : String(err),
|
|
52670
|
+
statsMode: "none"
|
|
52671
|
+
})
|
|
52672
|
+
};
|
|
52673
|
+
}
|
|
52674
|
+
}
|
|
52675
|
+
function getIndexedLogEventDetail(logConfig, id) {
|
|
52676
|
+
if (!logConfig || logConfig.enabled === false)
|
|
52677
|
+
return null;
|
|
52678
|
+
const baseDir = resolveLogBaseDir(logConfig);
|
|
52679
|
+
const indexed = getLogIndex(baseDir)?.getEventRecordByOffsetId(id);
|
|
52680
|
+
if (indexed)
|
|
52681
|
+
return indexed;
|
|
52682
|
+
const parsedId = decodeOffsetLogEventId(id);
|
|
52683
|
+
if (!parsedId)
|
|
52684
|
+
return null;
|
|
52685
|
+
const filePath = join4(baseDir, "events", `${parsedId.date}.jsonl`);
|
|
52686
|
+
if (!existsSync3(filePath))
|
|
52687
|
+
return null;
|
|
52688
|
+
const line2 = readLineAtOffset(filePath, parsedId.offset);
|
|
52689
|
+
if (!line2?.trim())
|
|
52690
|
+
return null;
|
|
52691
|
+
return {
|
|
52692
|
+
event: JSON.parse(line2),
|
|
52693
|
+
location: {
|
|
52694
|
+
id,
|
|
52695
|
+
date: parsedId.date,
|
|
52696
|
+
file: filePath,
|
|
52697
|
+
line: null,
|
|
52698
|
+
offset: parsedId.offset
|
|
52699
|
+
}
|
|
52700
|
+
};
|
|
52701
|
+
}
|
|
52702
|
+
|
|
51819
52703
|
// src/log-query.ts
|
|
51820
52704
|
var WINDOW_MS2 = {
|
|
51821
52705
|
"1h": 60 * 60 * 1000,
|
|
@@ -51827,49 +52711,57 @@ var MAX_QUERY_LIMIT = 200;
|
|
|
51827
52711
|
var DEFAULT_QUERY_LIMIT = 50;
|
|
51828
52712
|
var MAX_EXPORT_ROWS = 5000;
|
|
51829
52713
|
var MAX_Q_LENGTH = 200;
|
|
51830
|
-
function
|
|
52714
|
+
function encodeBase64Url2(value) {
|
|
51831
52715
|
return Buffer.from(value, "utf-8").toString("base64url");
|
|
51832
52716
|
}
|
|
51833
|
-
function
|
|
52717
|
+
function decodeBase64Url2(value) {
|
|
51834
52718
|
return Buffer.from(value, "base64url").toString("utf-8");
|
|
51835
52719
|
}
|
|
51836
|
-
function
|
|
51837
|
-
return
|
|
52720
|
+
function encodeCursor2(data) {
|
|
52721
|
+
return encodeBase64Url2(JSON.stringify(data));
|
|
51838
52722
|
}
|
|
51839
|
-
function
|
|
51840
|
-
const parsed = JSON.parse(
|
|
52723
|
+
function decodeCursor2(raw2) {
|
|
52724
|
+
const parsed = JSON.parse(decodeBase64Url2(raw2));
|
|
51841
52725
|
if (!Number.isInteger(parsed.offset) || parsed.offset < 0) {
|
|
51842
52726
|
throw new Error("cursor \u975E\u6CD5");
|
|
51843
52727
|
}
|
|
51844
52728
|
return parsed;
|
|
51845
52729
|
}
|
|
52730
|
+
function isLegacyOffsetCursor(raw2) {
|
|
52731
|
+
try {
|
|
52732
|
+
const parsed = JSON.parse(decodeBase64Url2(raw2));
|
|
52733
|
+
return Number.isInteger(parsed.offset) && Number(parsed.offset) >= 0;
|
|
52734
|
+
} catch {
|
|
52735
|
+
return false;
|
|
52736
|
+
}
|
|
52737
|
+
}
|
|
51846
52738
|
function encodeEventId(date5, line2) {
|
|
51847
|
-
return
|
|
52739
|
+
return encodeBase64Url2(JSON.stringify({ d: date5, l: line2 }));
|
|
51848
52740
|
}
|
|
51849
52741
|
function decodeEventId(id) {
|
|
51850
|
-
const parsed = JSON.parse(
|
|
52742
|
+
const parsed = JSON.parse(decodeBase64Url2(id));
|
|
51851
52743
|
if (typeof parsed.d !== "string" || !Number.isInteger(parsed.l) || Number(parsed.l) <= 0) {
|
|
51852
52744
|
throw new Error("id \u975E\u6CD5");
|
|
51853
52745
|
}
|
|
51854
52746
|
return { date: parsed.d, line: Number(parsed.l) };
|
|
51855
52747
|
}
|
|
51856
|
-
function
|
|
52748
|
+
function toPercent3(numerator, denominator) {
|
|
51857
52749
|
if (denominator <= 0)
|
|
51858
52750
|
return 0;
|
|
51859
52751
|
return Number((numerator / denominator * 100).toFixed(2));
|
|
51860
52752
|
}
|
|
51861
|
-
function
|
|
52753
|
+
function toDayStart3(ms) {
|
|
51862
52754
|
const date5 = new Date(ms);
|
|
51863
52755
|
return Date.UTC(date5.getUTCFullYear(), date5.getUTCMonth(), date5.getUTCDate());
|
|
51864
52756
|
}
|
|
51865
|
-
function
|
|
52757
|
+
function listDateStrings3(fromMs, toMs) {
|
|
51866
52758
|
const result = [];
|
|
51867
|
-
for (let day =
|
|
52759
|
+
for (let day = toDayStart3(fromMs);day <= toDayStart3(toMs); day += 24 * 60 * 60 * 1000) {
|
|
51868
52760
|
result.push(new Date(day).toISOString().slice(0, 10));
|
|
51869
52761
|
}
|
|
51870
52762
|
return result;
|
|
51871
52763
|
}
|
|
51872
|
-
function
|
|
52764
|
+
function getStatusClass3(event) {
|
|
51873
52765
|
if (event.error_type)
|
|
51874
52766
|
return "network_error";
|
|
51875
52767
|
const status = event.upstream_status ?? 0;
|
|
@@ -51881,16 +52773,16 @@ function getStatusClass2(event) {
|
|
|
51881
52773
|
return "5xx";
|
|
51882
52774
|
return "network_error";
|
|
51883
52775
|
}
|
|
51884
|
-
function
|
|
52776
|
+
function isErrorEvent3(event) {
|
|
51885
52777
|
if (event.error_type)
|
|
51886
52778
|
return true;
|
|
51887
52779
|
const status = event.upstream_status ?? 0;
|
|
51888
52780
|
return status < 200 || status >= 400;
|
|
51889
52781
|
}
|
|
51890
|
-
function
|
|
51891
|
-
return
|
|
52782
|
+
function getLevel2(event) {
|
|
52783
|
+
return isErrorEvent3(event) ? "error" : "info";
|
|
51892
52784
|
}
|
|
51893
|
-
function
|
|
52785
|
+
function buildMessage2(event) {
|
|
51894
52786
|
if (event.error_message)
|
|
51895
52787
|
return event.error_message;
|
|
51896
52788
|
if (event.error_type)
|
|
@@ -51915,7 +52807,7 @@ function containsKeyword(event, q) {
|
|
|
51915
52807
|
identity.sessionId ?? "",
|
|
51916
52808
|
event.error_type ?? "",
|
|
51917
52809
|
event.error_message ?? "",
|
|
51918
|
-
|
|
52810
|
+
buildMessage2(event)
|
|
51919
52811
|
].join(" ").toLowerCase();
|
|
51920
52812
|
return haystack.includes(keyword);
|
|
51921
52813
|
}
|
|
@@ -51964,7 +52856,7 @@ function finalizeStats(stats) {
|
|
|
51964
52856
|
return {
|
|
51965
52857
|
total: stats.total,
|
|
51966
52858
|
errorCount: stats.errorCount,
|
|
51967
|
-
errorRate:
|
|
52859
|
+
errorRate: toPercent3(stats.errorCount, stats.total),
|
|
51968
52860
|
avgLatencyMs: Math.round(stats.latencySum / stats.total),
|
|
51969
52861
|
p95LatencyMs: percentileFromCounts(stats.latencyCounts, stats.total, 0.95)
|
|
51970
52862
|
};
|
|
@@ -51984,17 +52876,17 @@ function insertBoundedEvent(items, item, sort, maxKeep) {
|
|
|
51984
52876
|
items.pop();
|
|
51985
52877
|
}
|
|
51986
52878
|
}
|
|
51987
|
-
function clampLimit(limit) {
|
|
52879
|
+
function clampLimit(limit, maxLimit = MAX_QUERY_LIMIT) {
|
|
51988
52880
|
if (!Number.isFinite(limit))
|
|
51989
52881
|
return DEFAULT_QUERY_LIMIT;
|
|
51990
52882
|
const integer2 = Math.floor(limit);
|
|
51991
52883
|
if (integer2 <= 0)
|
|
51992
52884
|
return DEFAULT_QUERY_LIMIT;
|
|
51993
|
-
return Math.min(
|
|
52885
|
+
return Math.min(maxLimit, integer2);
|
|
51994
52886
|
}
|
|
51995
|
-
function normalizeQuery(input) {
|
|
52887
|
+
function normalizeQuery(input, maxLimit = MAX_QUERY_LIMIT) {
|
|
51996
52888
|
const sort = input.sort ?? "time_desc";
|
|
51997
|
-
const limit = clampLimit(input.limit);
|
|
52889
|
+
const limit = clampLimit(input.limit, maxLimit);
|
|
51998
52890
|
const qRaw = (input.q ?? "").trim();
|
|
51999
52891
|
const q = qRaw.length > MAX_Q_LENGTH ? qRaw.slice(0, MAX_Q_LENGTH) : qRaw;
|
|
52000
52892
|
return {
|
|
@@ -52034,7 +52926,7 @@ function eventToSummary(item) {
|
|
|
52034
52926
|
upstreamStatus: event.upstream_status ?? 0,
|
|
52035
52927
|
statusClass: item.statusClass,
|
|
52036
52928
|
hasError: item.level === "error",
|
|
52037
|
-
message:
|
|
52929
|
+
message: buildMessage2(event),
|
|
52038
52930
|
errorType: event.error_type,
|
|
52039
52931
|
hasMetadata: identity.hasMetadata,
|
|
52040
52932
|
userIdRaw: identity.userIdRaw,
|
|
@@ -52042,6 +52934,57 @@ function eventToSummary(item) {
|
|
|
52042
52934
|
sessionId: identity.sessionId
|
|
52043
52935
|
};
|
|
52044
52936
|
}
|
|
52937
|
+
function createLogEventSummaryFromEvent(event, location) {
|
|
52938
|
+
const ts = Date.parse(event.ts_start);
|
|
52939
|
+
return eventToSummary({
|
|
52940
|
+
id: location.id,
|
|
52941
|
+
date: location.date,
|
|
52942
|
+
line: location.line ?? 0,
|
|
52943
|
+
ts: Number.isFinite(ts) ? ts : 0,
|
|
52944
|
+
level: getLevel2(event),
|
|
52945
|
+
statusClass: getStatusClass3(event),
|
|
52946
|
+
event
|
|
52947
|
+
});
|
|
52948
|
+
}
|
|
52949
|
+
function logEventMatchesQuery(event, query) {
|
|
52950
|
+
if (!event.ts_start)
|
|
52951
|
+
return false;
|
|
52952
|
+
const ts = Date.parse(event.ts_start);
|
|
52953
|
+
if (!Number.isFinite(ts) || ts < query.fromMs || ts > query.toMs)
|
|
52954
|
+
return false;
|
|
52955
|
+
const level = getLevel2(event);
|
|
52956
|
+
const statusClass = getStatusClass3(event);
|
|
52957
|
+
if (query.levels.length > 0 && !query.levels.includes(level))
|
|
52958
|
+
return false;
|
|
52959
|
+
if (query.providers.length > 0 && !query.providers.includes(event.provider))
|
|
52960
|
+
return false;
|
|
52961
|
+
if (query.routeTypes.length > 0 && !query.routeTypes.includes(event.route_type))
|
|
52962
|
+
return false;
|
|
52963
|
+
const eventModel = event.model_out || event.model_in;
|
|
52964
|
+
if (query.models.length > 0 && !query.models.includes(eventModel))
|
|
52965
|
+
return false;
|
|
52966
|
+
if (query.modelIns.length > 0 && !query.modelIns.includes(event.model_in))
|
|
52967
|
+
return false;
|
|
52968
|
+
if (query.modelOuts.length > 0 && !query.modelOuts.includes(event.model_out))
|
|
52969
|
+
return false;
|
|
52970
|
+
const identity = resolveLogSessionIdentity(event.request_body);
|
|
52971
|
+
if (query.users.length > 0) {
|
|
52972
|
+
const matchedByRaw = identity.userIdRaw ? query.users.includes(identity.userIdRaw) : false;
|
|
52973
|
+
const matchedByUserKey = identity.userKey ? query.users.includes(identity.userKey) : false;
|
|
52974
|
+
if (!matchedByRaw && !matchedByUserKey)
|
|
52975
|
+
return false;
|
|
52976
|
+
}
|
|
52977
|
+
if (query.sessions.length > 0) {
|
|
52978
|
+
if (!identity.sessionId || !query.sessions.includes(identity.sessionId))
|
|
52979
|
+
return false;
|
|
52980
|
+
}
|
|
52981
|
+
if (query.statusClasses.length > 0 && !query.statusClasses.includes(statusClass))
|
|
52982
|
+
return false;
|
|
52983
|
+
const hasError = level === "error";
|
|
52984
|
+
if (query.hasError !== null && query.hasError !== hasError)
|
|
52985
|
+
return false;
|
|
52986
|
+
return containsKeyword(event, query.q);
|
|
52987
|
+
}
|
|
52045
52988
|
function detectBodyPolicy(event) {
|
|
52046
52989
|
const hasRequestBody = event.request_body !== undefined;
|
|
52047
52990
|
const hasResponseBody = event.response_body !== undefined;
|
|
@@ -52095,17 +53038,17 @@ function readStreamContent(baseDir, streamFile) {
|
|
|
52095
53038
|
if (!looksLikeStreamFile) {
|
|
52096
53039
|
return { content: null, warning: "stream_file \u4E0D\u662F .sse.raw \u6587\u4EF6\uFF0C\u5DF2\u8DF3\u8FC7\u8BFB\u53D6\u3002" };
|
|
52097
53040
|
}
|
|
52098
|
-
if (
|
|
52099
|
-
return { content:
|
|
53041
|
+
if (existsSync4(resolvedFromFile)) {
|
|
53042
|
+
return { content: readFileSync3(resolvedFromFile, "utf-8"), warning: null };
|
|
52100
53043
|
}
|
|
52101
53044
|
const fallbackPath = resolve4(resolvedBase, streamFile);
|
|
52102
53045
|
if (!fallbackPath.startsWith(`${resolvedBase}/`) && fallbackPath !== resolvedBase) {
|
|
52103
53046
|
return { content: null, warning: "stream_file \u8DEF\u5F84\u975E\u6CD5\uFF0C\u5DF2\u62D2\u7EDD\u8BFB\u53D6\u3002" };
|
|
52104
53047
|
}
|
|
52105
|
-
if (!
|
|
53048
|
+
if (!existsSync4(fallbackPath)) {
|
|
52106
53049
|
return { content: null, warning: "stream_file \u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u5DF2\u88AB\u6E05\u7406\u3002" };
|
|
52107
53050
|
}
|
|
52108
|
-
return { content:
|
|
53051
|
+
return { content: readFileSync3(fallbackPath, "utf-8"), warning: null };
|
|
52109
53052
|
} catch (err) {
|
|
52110
53053
|
return {
|
|
52111
53054
|
content: null,
|
|
@@ -52115,8 +53058,8 @@ function readStreamContent(baseDir, streamFile) {
|
|
|
52115
53058
|
}
|
|
52116
53059
|
async function buildLogEventDetail(id, parsed, location, context2) {
|
|
52117
53060
|
const event = parsed;
|
|
52118
|
-
const level =
|
|
52119
|
-
const statusClass =
|
|
53061
|
+
const level = getLevel2(event);
|
|
53062
|
+
const statusClass = getStatusClass3(event);
|
|
52120
53063
|
const bodyPolicy = detectBodyPolicy(event);
|
|
52121
53064
|
const requestBodyAvailable = event.request_body !== undefined;
|
|
52122
53065
|
const responseBodyAvailable = event.response_body !== undefined;
|
|
@@ -52188,8 +53131,8 @@ async function buildLogEventDetail(id, parsed, location, context2) {
|
|
|
52188
53131
|
};
|
|
52189
53132
|
}
|
|
52190
53133
|
async function scanEvents(baseDir, query) {
|
|
52191
|
-
const eventsDir =
|
|
52192
|
-
if (!
|
|
53134
|
+
const eventsDir = join5(baseDir, "events");
|
|
53135
|
+
if (!existsSync4(eventsDir)) {
|
|
52193
53136
|
return {
|
|
52194
53137
|
items: [],
|
|
52195
53138
|
stats: {
|
|
@@ -52207,8 +53150,8 @@ async function scanEvents(baseDir, query) {
|
|
|
52207
53150
|
}
|
|
52208
53151
|
};
|
|
52209
53152
|
}
|
|
52210
|
-
const dates =
|
|
52211
|
-
const offset = query.cursor ?
|
|
53153
|
+
const dates = listDateStrings3(query.fromMs, query.toMs);
|
|
53154
|
+
const offset = query.cursor ? decodeCursor2(query.cursor).offset : 0;
|
|
52212
53155
|
const maxKeep = offset + query.limit;
|
|
52213
53156
|
const items = [];
|
|
52214
53157
|
const runningStats = createRunningStats();
|
|
@@ -52221,11 +53164,11 @@ async function scanEvents(baseDir, query) {
|
|
|
52221
53164
|
truncated = true;
|
|
52222
53165
|
break;
|
|
52223
53166
|
}
|
|
52224
|
-
const filePath =
|
|
52225
|
-
if (!
|
|
53167
|
+
const filePath = join5(eventsDir, `${date5}.jsonl`);
|
|
53168
|
+
if (!existsSync4(filePath))
|
|
52226
53169
|
continue;
|
|
52227
53170
|
scannedFiles += 1;
|
|
52228
|
-
const stream =
|
|
53171
|
+
const stream = createReadStream3(filePath, { encoding: "utf-8" });
|
|
52229
53172
|
const rl = createInterface2({ input: stream, crlfDelay: Number.POSITIVE_INFINITY });
|
|
52230
53173
|
let lineNumber = 0;
|
|
52231
53174
|
for await (const line2 of rl) {
|
|
@@ -52251,8 +53194,8 @@ async function scanEvents(baseDir, query) {
|
|
|
52251
53194
|
const ts = Date.parse(event.ts_start);
|
|
52252
53195
|
if (!Number.isFinite(ts) || ts < query.fromMs || ts > query.toMs)
|
|
52253
53196
|
continue;
|
|
52254
|
-
const level =
|
|
52255
|
-
const statusClass =
|
|
53197
|
+
const level = getLevel2(event);
|
|
53198
|
+
const statusClass = getStatusClass3(event);
|
|
52256
53199
|
if (query.levels.length > 0 && !query.levels.includes(level))
|
|
52257
53200
|
continue;
|
|
52258
53201
|
if (query.providers.length > 0 && !query.providers.includes(event.provider))
|
|
@@ -52342,6 +53285,9 @@ function validateSort(value) {
|
|
|
52342
53285
|
return value === "time_desc" || value === "time_asc";
|
|
52343
53286
|
}
|
|
52344
53287
|
async function queryLogEvents(context2, input) {
|
|
53288
|
+
return queryLogEventsInternal(context2, input, MAX_QUERY_LIMIT);
|
|
53289
|
+
}
|
|
53290
|
+
async function queryLogEventsInternal(context2, input, maxLimit) {
|
|
52345
53291
|
const logEnabled = !!context2.logConfig && context2.logConfig.enabled !== false;
|
|
52346
53292
|
if (!logEnabled) {
|
|
52347
53293
|
return {
|
|
@@ -52364,30 +53310,51 @@ async function queryLogEvents(context2, input) {
|
|
|
52364
53310
|
};
|
|
52365
53311
|
}
|
|
52366
53312
|
const baseDir = resolveLogBaseDir(context2.logConfig);
|
|
52367
|
-
const query = normalizeQuery(input);
|
|
52368
|
-
const
|
|
53313
|
+
const query = normalizeQuery(input, maxLimit);
|
|
53314
|
+
const indexed = await queryIndexedLogEvents(context2.logConfig, query);
|
|
53315
|
+
if (indexed?.meta.indexUsed) {
|
|
53316
|
+
return indexed;
|
|
53317
|
+
}
|
|
53318
|
+
if (query.cursor && !isLegacyOffsetCursor(query.cursor)) {
|
|
53319
|
+
throw new Error(`\u7D22\u5F15\u67E5\u8BE2\u5931\u8D25\uFF0C\u65E0\u6CD5\u4F7F\u7528\u7D22\u5F15 cursor \u56DE\u9000\u5230 JSONL\uFF0C\u8BF7\u91CD\u65B0\u67E5\u8BE2\u7B2C\u4E00\u9875${indexed?.meta.fallbackReason ? `: ${indexed.meta.fallbackReason}` : ""}`);
|
|
53320
|
+
}
|
|
53321
|
+
const offset = query.cursor ? decodeCursor2(query.cursor).offset : 0;
|
|
52369
53322
|
const scanned = await scanEvents(baseDir, query);
|
|
52370
53323
|
const pageItems = scanned.items.slice(offset, offset + query.limit);
|
|
52371
53324
|
const hasMore = scanned.stats.total > offset + query.limit;
|
|
52372
53325
|
const nextOffset = offset + pageItems.length;
|
|
52373
53326
|
return {
|
|
52374
53327
|
items: pageItems.map(eventToSummary),
|
|
52375
|
-
nextCursor: hasMore ?
|
|
53328
|
+
nextCursor: hasMore ? encodeCursor2({ offset: nextOffset }) : null,
|
|
52376
53329
|
hasMore,
|
|
52377
53330
|
stats: scanned.stats,
|
|
52378
|
-
meta:
|
|
53331
|
+
meta: {
|
|
53332
|
+
...scanned.meta,
|
|
53333
|
+
indexUsed: false,
|
|
53334
|
+
indexFresh: false,
|
|
53335
|
+
fallbackReason: indexed?.meta.fallbackReason,
|
|
53336
|
+
statsMode: "exact"
|
|
53337
|
+
}
|
|
52379
53338
|
};
|
|
52380
53339
|
}
|
|
52381
53340
|
async function getLogEventDetailById(context2, id) {
|
|
52382
53341
|
const logEnabled = !!context2.logConfig && context2.logConfig.enabled !== false;
|
|
52383
53342
|
if (!logEnabled)
|
|
52384
53343
|
return null;
|
|
53344
|
+
const indexed = getIndexedLogEventDetail(context2.logConfig, id);
|
|
53345
|
+
if (indexed) {
|
|
53346
|
+
return buildLogEventDetail(id, indexed.event, {
|
|
53347
|
+
date: indexed.location.date,
|
|
53348
|
+
line: indexed.location.line ?? 0,
|
|
53349
|
+
file: indexed.location.file
|
|
53350
|
+
}, context2);
|
|
53351
|
+
}
|
|
52385
53352
|
const { date: date5, line: line2 } = decodeEventId(id);
|
|
52386
53353
|
const baseDir = resolveLogBaseDir(context2.logConfig);
|
|
52387
|
-
const filePath =
|
|
52388
|
-
if (!
|
|
53354
|
+
const filePath = join5(baseDir, "events", `${date5}.jsonl`);
|
|
53355
|
+
if (!existsSync4(filePath))
|
|
52389
53356
|
return null;
|
|
52390
|
-
const stream =
|
|
53357
|
+
const stream = createReadStream3(filePath, { encoding: "utf-8" });
|
|
52391
53358
|
const rl = createInterface2({ input: stream, crlfDelay: Number.POSITIVE_INFINITY });
|
|
52392
53359
|
let lineNumber = 0;
|
|
52393
53360
|
for await (const lineText of rl) {
|
|
@@ -52497,11 +53464,11 @@ function createJsonExportStream(data) {
|
|
|
52497
53464
|
});
|
|
52498
53465
|
}
|
|
52499
53466
|
async function exportLogEvents(context2, input, format) {
|
|
52500
|
-
const data = await
|
|
53467
|
+
const data = await queryLogEventsInternal(context2, {
|
|
52501
53468
|
...input,
|
|
52502
53469
|
cursor: null,
|
|
52503
53470
|
limit: MAX_EXPORT_ROWS
|
|
52504
|
-
});
|
|
53471
|
+
}, MAX_EXPORT_ROWS);
|
|
52505
53472
|
const now2 = new Date().toISOString().replace(/[:.]/g, "-");
|
|
52506
53473
|
if (format === "csv") {
|
|
52507
53474
|
return {
|
|
@@ -52540,18 +53507,18 @@ function parseBooleanFlag(value) {
|
|
|
52540
53507
|
}
|
|
52541
53508
|
|
|
52542
53509
|
// src/log-sessions.ts
|
|
52543
|
-
import { createReadStream as
|
|
52544
|
-
import { join as
|
|
53510
|
+
import { createReadStream as createReadStream4, existsSync as existsSync5 } from "fs";
|
|
53511
|
+
import { join as join6 } from "path";
|
|
52545
53512
|
import { createInterface as createInterface3 } from "readline";
|
|
52546
53513
|
var MAX_LINES_SCANNED3 = 250000;
|
|
52547
53514
|
var MAX_Q_LENGTH2 = 200;
|
|
52548
|
-
function
|
|
53515
|
+
function toDayStart4(ms) {
|
|
52549
53516
|
const date5 = new Date(ms);
|
|
52550
53517
|
return Date.UTC(date5.getUTCFullYear(), date5.getUTCMonth(), date5.getUTCDate());
|
|
52551
53518
|
}
|
|
52552
|
-
function
|
|
53519
|
+
function listDateStrings4(fromMs, toMs) {
|
|
52553
53520
|
const result = [];
|
|
52554
|
-
for (let day =
|
|
53521
|
+
for (let day = toDayStart4(fromMs);day <= toDayStart4(toMs); day += 24 * 60 * 60 * 1000) {
|
|
52555
53522
|
result.push(new Date(day).toISOString().slice(0, 10));
|
|
52556
53523
|
}
|
|
52557
53524
|
return result;
|
|
@@ -52662,8 +53629,8 @@ async function queryLogSessions(context2, input) {
|
|
|
52662
53629
|
return createEmptyResult(normalized.fromMs, normalized.toMs);
|
|
52663
53630
|
}
|
|
52664
53631
|
const baseDir = resolveLogBaseDir(context2.logConfig);
|
|
52665
|
-
const eventsDir =
|
|
52666
|
-
if (!
|
|
53632
|
+
const eventsDir = join6(baseDir, "events");
|
|
53633
|
+
if (!existsSync5(eventsDir)) {
|
|
52667
53634
|
return createEmptyResult(normalized.fromMs, normalized.toMs);
|
|
52668
53635
|
}
|
|
52669
53636
|
const usersMap = new Map;
|
|
@@ -52675,17 +53642,17 @@ async function queryLogSessions(context2, input) {
|
|
|
52675
53642
|
let scannedLines = 0;
|
|
52676
53643
|
let parseErrors = 0;
|
|
52677
53644
|
let truncated = false;
|
|
52678
|
-
const dateStrings =
|
|
53645
|
+
const dateStrings = listDateStrings4(normalized.fromMs, normalized.toMs);
|
|
52679
53646
|
for (const date5 of dateStrings) {
|
|
52680
53647
|
if (scannedLines >= MAX_LINES_SCANNED3) {
|
|
52681
53648
|
truncated = true;
|
|
52682
53649
|
break;
|
|
52683
53650
|
}
|
|
52684
|
-
const filePath =
|
|
52685
|
-
if (!
|
|
53651
|
+
const filePath = join6(eventsDir, `${date5}.jsonl`);
|
|
53652
|
+
if (!existsSync5(filePath))
|
|
52686
53653
|
continue;
|
|
52687
53654
|
scannedFiles += 1;
|
|
52688
|
-
const stream =
|
|
53655
|
+
const stream = createReadStream4(filePath, { encoding: "utf-8" });
|
|
52689
53656
|
const rl = createInterface3({ input: stream, crlfDelay: Number.POSITIVE_INFINITY });
|
|
52690
53657
|
for await (const line2 of rl) {
|
|
52691
53658
|
if (scannedLines >= MAX_LINES_SCANNED3) {
|
|
@@ -52790,8 +53757,8 @@ async function queryLogSessions(context2, input) {
|
|
|
52790
53757
|
}
|
|
52791
53758
|
|
|
52792
53759
|
// src/log-storage.ts
|
|
52793
|
-
import { existsSync as
|
|
52794
|
-
import { join as
|
|
53760
|
+
import { existsSync as existsSync6, promises as fsPromises } from "fs";
|
|
53761
|
+
import { join as join7 } from "path";
|
|
52795
53762
|
var cachedStorage = null;
|
|
52796
53763
|
var calculationPromise = null;
|
|
52797
53764
|
var lastCalculationTime = 0;
|
|
@@ -52799,7 +53766,7 @@ var CACHE_TTL_MS2 = 60 * 60 * 1000;
|
|
|
52799
53766
|
var CALCULATION_INTERVAL_MS = 60 * 60 * 1000;
|
|
52800
53767
|
var MIN_CALCULATION_INTERVAL_MS = 5 * 60 * 1000;
|
|
52801
53768
|
async function calculateDirSize(dirPath) {
|
|
52802
|
-
if (!
|
|
53769
|
+
if (!existsSync6(dirPath)) {
|
|
52803
53770
|
return { bytes: 0, fileCount: 0 };
|
|
52804
53771
|
}
|
|
52805
53772
|
let bytes = 0;
|
|
@@ -52808,7 +53775,7 @@ async function calculateDirSize(dirPath) {
|
|
|
52808
53775
|
try {
|
|
52809
53776
|
const entries = await fsPromises.readdir(currentPath, { withFileTypes: true });
|
|
52810
53777
|
for (const entry of entries) {
|
|
52811
|
-
const fullPath =
|
|
53778
|
+
const fullPath = join7(currentPath, entry.name);
|
|
52812
53779
|
if (entry.isDirectory()) {
|
|
52813
53780
|
await walk(fullPath);
|
|
52814
53781
|
} else if (entry.isFile()) {
|
|
@@ -52831,6 +53798,7 @@ async function doCalculateStorage(logConfig) {
|
|
|
52831
53798
|
totalBytes: 0,
|
|
52832
53799
|
eventsBytes: 0,
|
|
52833
53800
|
streamsBytes: 0,
|
|
53801
|
+
indexBytes: 0,
|
|
52834
53802
|
fileCount: 0,
|
|
52835
53803
|
lastUpdatedAt: new Date().toISOString(),
|
|
52836
53804
|
isCalculating: false
|
|
@@ -52838,18 +53806,40 @@ async function doCalculateStorage(logConfig) {
|
|
|
52838
53806
|
}
|
|
52839
53807
|
const baseDir = resolveLogBaseDir(logConfig);
|
|
52840
53808
|
const [eventsResult, streamsResult] = await Promise.all([
|
|
52841
|
-
calculateDirSize(
|
|
52842
|
-
calculateDirSize(
|
|
53809
|
+
calculateDirSize(join7(baseDir, "events")),
|
|
53810
|
+
calculateDirSize(join7(baseDir, "streams"))
|
|
52843
53811
|
]);
|
|
53812
|
+
const indexResult = await calculateIndexSize(baseDir);
|
|
52844
53813
|
return {
|
|
52845
|
-
totalBytes: eventsResult.bytes + streamsResult.bytes,
|
|
53814
|
+
totalBytes: eventsResult.bytes + streamsResult.bytes + indexResult.bytes,
|
|
52846
53815
|
eventsBytes: eventsResult.bytes,
|
|
52847
53816
|
streamsBytes: streamsResult.bytes,
|
|
52848
|
-
|
|
53817
|
+
indexBytes: indexResult.bytes,
|
|
53818
|
+
fileCount: eventsResult.fileCount + streamsResult.fileCount + indexResult.fileCount,
|
|
52849
53819
|
lastUpdatedAt: new Date().toISOString(),
|
|
52850
53820
|
isCalculating: false
|
|
52851
53821
|
};
|
|
52852
53822
|
}
|
|
53823
|
+
async function calculateIndexSize(baseDir) {
|
|
53824
|
+
if (!existsSync6(baseDir)) {
|
|
53825
|
+
return { bytes: 0, fileCount: 0 };
|
|
53826
|
+
}
|
|
53827
|
+
let bytes = 0;
|
|
53828
|
+
let fileCount = 0;
|
|
53829
|
+
try {
|
|
53830
|
+
const entries = await fsPromises.readdir(baseDir, { withFileTypes: true });
|
|
53831
|
+
for (const entry of entries) {
|
|
53832
|
+
if (!entry.isFile() || !entry.name.startsWith("logs-index.sqlite"))
|
|
53833
|
+
continue;
|
|
53834
|
+
const stats = await fsPromises.stat(join7(baseDir, entry.name));
|
|
53835
|
+
bytes += stats.size;
|
|
53836
|
+
fileCount += 1;
|
|
53837
|
+
}
|
|
53838
|
+
} catch {
|
|
53839
|
+
return { bytes, fileCount };
|
|
53840
|
+
}
|
|
53841
|
+
return { bytes, fileCount };
|
|
53842
|
+
}
|
|
52853
53843
|
async function getLogStorageInfo(options) {
|
|
52854
53844
|
const { logConfig, forceRefresh = false, nowMs = Date.now() } = options;
|
|
52855
53845
|
if (!forceRefresh && cachedStorage && cachedStorage.expiresAt > nowMs) {
|
|
@@ -52892,10 +53882,25 @@ function startLogStorageBackgroundTask(logConfig) {
|
|
|
52892
53882
|
};
|
|
52893
53883
|
}
|
|
52894
53884
|
|
|
52895
|
-
// src/
|
|
52896
|
-
|
|
52897
|
-
|
|
53885
|
+
// src/log-tail.ts
|
|
53886
|
+
var subscribers = new Set;
|
|
53887
|
+
function publishLogEvent(event) {
|
|
53888
|
+
for (const subscriber of subscribers) {
|
|
53889
|
+
try {
|
|
53890
|
+
subscriber(event);
|
|
53891
|
+
} catch {}
|
|
53892
|
+
}
|
|
53893
|
+
}
|
|
53894
|
+
function subscribeLogEvents(subscriber) {
|
|
53895
|
+
subscribers.add(subscriber);
|
|
53896
|
+
return () => {
|
|
53897
|
+
subscribers.delete(subscriber);
|
|
53898
|
+
};
|
|
53899
|
+
}
|
|
52898
53900
|
|
|
53901
|
+
// src/logger.ts
|
|
53902
|
+
import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync3, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
53903
|
+
import { join as join8 } from "path";
|
|
52899
53904
|
class Logger {
|
|
52900
53905
|
baseDir;
|
|
52901
53906
|
eventsDir;
|
|
@@ -52910,8 +53915,8 @@ class Logger {
|
|
|
52910
53915
|
this._bodyPolicy = config2.bodyPolicy ?? "off";
|
|
52911
53916
|
this._streamsEnabled = config2.streams?.enabled !== false;
|
|
52912
53917
|
this.maxStreamBytes = config2.streams?.maxBytesPerRequest ?? 10 * 1024 * 1024;
|
|
52913
|
-
this.eventsDir =
|
|
52914
|
-
this.streamsDir =
|
|
53918
|
+
this.eventsDir = join8(baseDir, "events");
|
|
53919
|
+
this.streamsDir = join8(baseDir, "streams");
|
|
52915
53920
|
if (this._enabled)
|
|
52916
53921
|
this.ensureDirs();
|
|
52917
53922
|
}
|
|
@@ -52923,14 +53928,14 @@ class Logger {
|
|
|
52923
53928
|
}
|
|
52924
53929
|
ensureDirs() {
|
|
52925
53930
|
for (const dir of [this.baseDir, this.eventsDir, this.streamsDir]) {
|
|
52926
|
-
if (!
|
|
52927
|
-
|
|
53931
|
+
if (!existsSync7(dir))
|
|
53932
|
+
mkdirSync3(dir, { recursive: true });
|
|
52928
53933
|
}
|
|
52929
53934
|
}
|
|
52930
53935
|
ensureStreamDateDir(dateStr) {
|
|
52931
|
-
const dir =
|
|
52932
|
-
if (!
|
|
52933
|
-
|
|
53936
|
+
const dir = join8(this.streamsDir, dateStr);
|
|
53937
|
+
if (!existsSync7(dir))
|
|
53938
|
+
mkdirSync3(dir, { recursive: true });
|
|
52934
53939
|
return dir;
|
|
52935
53940
|
}
|
|
52936
53941
|
writeEvent(event) {
|
|
@@ -52939,9 +53944,21 @@ class Logger {
|
|
|
52939
53944
|
try {
|
|
52940
53945
|
this.ensureDirs();
|
|
52941
53946
|
const dateStr = event.ts_start.slice(0, 10);
|
|
52942
|
-
const filePath =
|
|
52943
|
-
|
|
52944
|
-
|
|
53947
|
+
const filePath = join8(this.eventsDir, `${dateStr}.jsonl`);
|
|
53948
|
+
const offset = existsSync7(filePath) ? statSync2(filePath).size : 0;
|
|
53949
|
+
const line2 = `${JSON.stringify(event)}
|
|
53950
|
+
`;
|
|
53951
|
+
appendFileSync(filePath, line2);
|
|
53952
|
+
const id = encodeOffsetLogEventId(dateStr, offset);
|
|
53953
|
+
enqueueLogEventForIndex({
|
|
53954
|
+
baseDir: this.baseDir,
|
|
53955
|
+
filePath,
|
|
53956
|
+
date: dateStr,
|
|
53957
|
+
offset,
|
|
53958
|
+
byteLength: Buffer.byteLength(line2),
|
|
53959
|
+
event
|
|
53960
|
+
});
|
|
53961
|
+
publishLogEvent({ id, date: dateStr, filePath, offset, event });
|
|
52945
53962
|
} catch (err) {
|
|
52946
53963
|
console.error("[logger] \u4E8B\u4EF6\u65E5\u5FD7\u5199\u5165\u5931\u8D25:", err);
|
|
52947
53964
|
}
|
|
@@ -52951,7 +53968,7 @@ class Logger {
|
|
|
52951
53968
|
return null;
|
|
52952
53969
|
try {
|
|
52953
53970
|
const dir = this.ensureStreamDateDir(dateStr);
|
|
52954
|
-
const filePath =
|
|
53971
|
+
const filePath = join8(dir, `${requestId}.sse.raw`);
|
|
52955
53972
|
const toWrite = content.length > this.maxStreamBytes ? `${content.slice(0, this.maxStreamBytes)}
|
|
52956
53973
|
[TRUNCATED]` : content;
|
|
52957
53974
|
writeFileSync3(filePath, toWrite);
|
|
@@ -52965,6 +53982,7 @@ class Logger {
|
|
|
52965
53982
|
var instance = null;
|
|
52966
53983
|
function initLogger(baseDir, config2) {
|
|
52967
53984
|
instance = new Logger(baseDir, config2);
|
|
53985
|
+
initLogIndex(baseDir, config2);
|
|
52968
53986
|
if (instance.enabled) {
|
|
52969
53987
|
console.log(`[logger] \u65E5\u5FD7\u7CFB\u7EDF\u5DF2\u521D\u59CB\u5316: ${baseDir}`);
|
|
52970
53988
|
}
|
|
@@ -52974,6 +53992,7 @@ function getLogger() {
|
|
|
52974
53992
|
}
|
|
52975
53993
|
function resetLogger() {
|
|
52976
53994
|
instance = null;
|
|
53995
|
+
disposeLogIndex();
|
|
52977
53996
|
}
|
|
52978
53997
|
function collectHeaders(headers) {
|
|
52979
53998
|
const result = {};
|
|
@@ -52994,6 +54013,85 @@ function normalizeUrl(rawUrl) {
|
|
|
52994
54013
|
return rawUrl;
|
|
52995
54014
|
}
|
|
52996
54015
|
|
|
54016
|
+
// src/network-access.ts
|
|
54017
|
+
var REMOTE_ADDRESS_ENV_KEY = "LOCAL_ROUTER_REMOTE_ADDRESS";
|
|
54018
|
+
function parseIpv4(address) {
|
|
54019
|
+
const parts = address.split(".");
|
|
54020
|
+
if (parts.length !== 4)
|
|
54021
|
+
return null;
|
|
54022
|
+
const octets = parts.map((part) => {
|
|
54023
|
+
if (!/^\d{1,3}$/.test(part))
|
|
54024
|
+
return Number.NaN;
|
|
54025
|
+
const value = Number.parseInt(part, 10);
|
|
54026
|
+
return value >= 0 && value <= 255 ? value : Number.NaN;
|
|
54027
|
+
});
|
|
54028
|
+
return octets.every(Number.isFinite) ? octets : null;
|
|
54029
|
+
}
|
|
54030
|
+
function normalizeIpAddress(raw2) {
|
|
54031
|
+
let address = raw2.trim().toLowerCase();
|
|
54032
|
+
if (address.startsWith("[")) {
|
|
54033
|
+
const end = address.indexOf("]");
|
|
54034
|
+
if (end !== -1)
|
|
54035
|
+
address = address.slice(1, end);
|
|
54036
|
+
}
|
|
54037
|
+
const mappedIpv4 = address.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/);
|
|
54038
|
+
if (mappedIpv4) {
|
|
54039
|
+
return mappedIpv4[1];
|
|
54040
|
+
}
|
|
54041
|
+
return address;
|
|
54042
|
+
}
|
|
54043
|
+
function isLoopbackAddress(raw2) {
|
|
54044
|
+
if (!raw2)
|
|
54045
|
+
return false;
|
|
54046
|
+
const address = normalizeIpAddress(raw2);
|
|
54047
|
+
const ipv43 = parseIpv4(address);
|
|
54048
|
+
if (ipv43)
|
|
54049
|
+
return ipv43[0] === 127;
|
|
54050
|
+
return address === "::1";
|
|
54051
|
+
}
|
|
54052
|
+
function isLanAddress(raw2) {
|
|
54053
|
+
if (!raw2)
|
|
54054
|
+
return false;
|
|
54055
|
+
const address = normalizeIpAddress(raw2);
|
|
54056
|
+
const ipv43 = parseIpv4(address);
|
|
54057
|
+
if (ipv43) {
|
|
54058
|
+
const [a, b] = ipv43;
|
|
54059
|
+
return a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 169 && b === 254;
|
|
54060
|
+
}
|
|
54061
|
+
return address.startsWith("fc") || address.startsWith("fd") || /^fe[89ab]/.test(address);
|
|
54062
|
+
}
|
|
54063
|
+
function decideNetworkAccess(serverConfig, rawRemoteAddress) {
|
|
54064
|
+
const remoteAddress = rawRemoteAddress ? normalizeIpAddress(rawRemoteAddress) : null;
|
|
54065
|
+
if (!remoteAddress || isLoopbackAddress(remoteAddress)) {
|
|
54066
|
+
return { allowed: true, remoteAddress };
|
|
54067
|
+
}
|
|
54068
|
+
const lanEnabled = serverConfig?.lanAccess?.enabled === true;
|
|
54069
|
+
if (!lanEnabled) {
|
|
54070
|
+
return { allowed: false, remoteAddress, reason: "lan-disabled" };
|
|
54071
|
+
}
|
|
54072
|
+
if (!isLanAddress(remoteAddress)) {
|
|
54073
|
+
return { allowed: false, remoteAddress, reason: "non-lan-address" };
|
|
54074
|
+
}
|
|
54075
|
+
return { allowed: true, remoteAddress };
|
|
54076
|
+
}
|
|
54077
|
+
function getRemoteAddressFromContext(c2) {
|
|
54078
|
+
const env = c2.env;
|
|
54079
|
+
const value = env?.[REMOTE_ADDRESS_ENV_KEY];
|
|
54080
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
54081
|
+
}
|
|
54082
|
+
function createNetworkAccessMiddleware(store) {
|
|
54083
|
+
return async (c2, next) => {
|
|
54084
|
+
const decision = decideNetworkAccess(store.get().server, getRemoteAddressFromContext(c2));
|
|
54085
|
+
if (!decision.allowed) {
|
|
54086
|
+
return c2.json({
|
|
54087
|
+
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",
|
|
54088
|
+
remoteAddress: decision.remoteAddress
|
|
54089
|
+
}, 403);
|
|
54090
|
+
}
|
|
54091
|
+
await next();
|
|
54092
|
+
};
|
|
54093
|
+
}
|
|
54094
|
+
|
|
52997
54095
|
// src/openapi.ts
|
|
52998
54096
|
var openAPISpec = {
|
|
52999
54097
|
openapi: "3.0.0",
|
|
@@ -53442,7 +54540,9 @@ var openAPISpec = {
|
|
|
53442
54540
|
type: "object",
|
|
53443
54541
|
additionalProperties: { type: "string" }
|
|
53444
54542
|
},
|
|
53445
|
-
requestBody: {
|
|
54543
|
+
requestBody: {
|
|
54544
|
+
type: ["object", "array", "string", "number", "boolean", "null"]
|
|
54545
|
+
}
|
|
53446
54546
|
}
|
|
53447
54547
|
},
|
|
53448
54548
|
response: {
|
|
@@ -54096,9 +55196,9 @@ var openAPISpec = {
|
|
|
54096
55196
|
};
|
|
54097
55197
|
|
|
54098
55198
|
// src/plugin-loader.ts
|
|
54099
|
-
import {
|
|
55199
|
+
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
54100
55200
|
import { tmpdir } from "os";
|
|
54101
|
-
import {
|
|
55201
|
+
import { join as join9, resolve as resolve5 } from "path";
|
|
54102
55202
|
function isLocalPath(pkg) {
|
|
54103
55203
|
return pkg.startsWith("./") || pkg.startsWith("../") || pkg.startsWith("/") || /^[A-Za-z]:[\\/]/.test(pkg);
|
|
54104
55204
|
}
|
|
@@ -54121,7 +55221,7 @@ var remoteTmpDir = null;
|
|
|
54121
55221
|
var remoteTmpFiles = [];
|
|
54122
55222
|
async function ensureRemoteTmpDir() {
|
|
54123
55223
|
if (!remoteTmpDir) {
|
|
54124
|
-
remoteTmpDir = await mkdtemp(
|
|
55224
|
+
remoteTmpDir = await mkdtemp(join9(tmpdir(), "local-router-plugins-"));
|
|
54125
55225
|
}
|
|
54126
55226
|
return remoteTmpDir;
|
|
54127
55227
|
}
|
|
@@ -54134,7 +55234,7 @@ async function fetchRemotePlugin(url2) {
|
|
|
54134
55234
|
const ext = inferExtension(url2, response.headers.get("content-type"));
|
|
54135
55235
|
const dir = await ensureRemoteTmpDir();
|
|
54136
55236
|
const fileName = `plugin_${Date.now()}_${Math.random().toString(36).slice(2, 8)}${ext}`;
|
|
54137
|
-
const filePath =
|
|
55237
|
+
const filePath = join9(dir, fileName);
|
|
54138
55238
|
await writeFile(filePath, content, "utf-8");
|
|
54139
55239
|
remoteTmpFiles.push(filePath);
|
|
54140
55240
|
return filePath;
|
|
@@ -54173,13 +55273,17 @@ class PluginManager {
|
|
|
54173
55273
|
constructor(configDir) {
|
|
54174
55274
|
this.configDir = configDir;
|
|
54175
55275
|
}
|
|
54176
|
-
async loadPluginsForProvider(providerName, pluginConfigs) {
|
|
55276
|
+
async loadPluginsForProvider(providerName, providerConfig, pluginConfigs) {
|
|
54177
55277
|
const loaded = [];
|
|
54178
55278
|
const failures = [];
|
|
54179
55279
|
for (const config2 of pluginConfigs) {
|
|
54180
55280
|
try {
|
|
54181
55281
|
const definition = await importPlugin(config2.package, this.configDir);
|
|
54182
|
-
const
|
|
55282
|
+
const params = { ...config2.params ?? {} };
|
|
55283
|
+
if (definition.name === "protocol-adapter" && params.targetFormat === undefined) {
|
|
55284
|
+
params.targetFormat = providerConfig.type;
|
|
55285
|
+
}
|
|
55286
|
+
const instance2 = await definition.create(params);
|
|
54183
55287
|
loaded.push({ config: config2, definition, instance: instance2 });
|
|
54184
55288
|
} catch (err) {
|
|
54185
55289
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -54195,7 +55299,7 @@ class PluginManager {
|
|
|
54195
55299
|
const oldPluginsToDispose = [];
|
|
54196
55300
|
for (const [providerName, providerConfig] of Object.entries(providers)) {
|
|
54197
55301
|
if (providerConfig.plugins && providerConfig.plugins.length > 0) {
|
|
54198
|
-
const { loaded, failures } = await this.loadPluginsForProvider(providerName, providerConfig.plugins);
|
|
55302
|
+
const { loaded, failures } = await this.loadPluginsForProvider(providerName, providerConfig, providerConfig.plugins);
|
|
54199
55303
|
allFailures.push(...failures);
|
|
54200
55304
|
if (failures.length > 0) {
|
|
54201
55305
|
console.warn(`[plugin] provider "${providerName}" \u6709\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25\uFF0C\u4FDD\u7559\u65E7\u63D2\u4EF6\u94FE`);
|
|
@@ -54264,19 +55368,12 @@ class PluginManager {
|
|
|
54264
55368
|
}
|
|
54265
55369
|
}
|
|
54266
55370
|
}
|
|
54267
|
-
async disposePluginMap(pluginMap) {
|
|
54268
|
-
const allPlugins = [];
|
|
54269
|
-
for (const [, loadedPlugins] of pluginMap) {
|
|
54270
|
-
allPlugins.push(...loadedPlugins);
|
|
54271
|
-
}
|
|
54272
|
-
await this.disposePluginList(allPlugins);
|
|
54273
|
-
}
|
|
54274
55371
|
}
|
|
54275
55372
|
|
|
54276
55373
|
// src/proxy.ts
|
|
54277
55374
|
import { appendFile, readFile, unlink } from "fs/promises";
|
|
54278
|
-
import { join as join9 } from "path";
|
|
54279
55375
|
import { tmpdir as tmpdir2 } from "os";
|
|
55376
|
+
import { join as join10 } from "path";
|
|
54280
55377
|
|
|
54281
55378
|
// src/plugin-engine.ts
|
|
54282
55379
|
async function executeRequestPlugins(plugins, ctx, url2, headers, body) {
|
|
@@ -54468,7 +55565,7 @@ function buildLogEvent(logMeta, targetUrl, proxyUrl, tsEnd, overrides) {
|
|
|
54468
55565
|
};
|
|
54469
55566
|
}
|
|
54470
55567
|
function createTempStreamCapturePath(requestId) {
|
|
54471
|
-
return
|
|
55568
|
+
return join10(tmpdir2(), `local-router-stream-${requestId}-${Date.now()}.sse.raw`);
|
|
54472
55569
|
}
|
|
54473
55570
|
async function appendTempStreamCapture(filePath, chunk) {
|
|
54474
55571
|
await appendFile(filePath, chunk);
|
|
@@ -54787,47 +55884,34 @@ function createOpenaiResponsesRoutes(routeType, store, pluginManager) {
|
|
|
54787
55884
|
return routes;
|
|
54788
55885
|
}
|
|
54789
55886
|
|
|
54790
|
-
// src/
|
|
54791
|
-
|
|
54792
|
-
|
|
54793
|
-
|
|
54794
|
-
|
|
54795
|
-
function getBundledSchemaPath() {
|
|
54796
|
-
return resolveBundledAssetPath("../config.schema.json");
|
|
54797
|
-
}
|
|
54798
|
-
function getBundledWebRoot() {
|
|
54799
|
-
return resolveBundledAssetPath("../dist/web/");
|
|
54800
|
-
}
|
|
54801
|
-
|
|
54802
|
-
// src/config-validate.ts
|
|
54803
|
-
var import__2020 = __toESM(require_2020(), 1);
|
|
54804
|
-
var import_ajv_formats = __toESM(require_dist2(), 1);
|
|
54805
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
54806
|
-
function validateBusinessRules(config2) {
|
|
54807
|
-
for (const [routeType, modelMap] of Object.entries(config2.routes)) {
|
|
54808
|
-
if (!modelMap["*"]) {
|
|
54809
|
-
throw new Error(`\u8DEF\u7531 "${routeType}" \u7F3A\u5C11 "*" \u515C\u5E95\u89C4\u5219`);
|
|
54810
|
-
}
|
|
54811
|
-
for (const target of Object.values(modelMap)) {
|
|
54812
|
-
if (!config2.providers[target.provider]) {
|
|
54813
|
-
throw new Error(`\u8DEF\u7531 "${routeType}" \u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684 provider "${target.provider}"`);
|
|
54814
|
-
}
|
|
54815
|
-
}
|
|
55887
|
+
// src/server-address.ts
|
|
55888
|
+
function formatUrlHost(host) {
|
|
55889
|
+
const trimmed = host.trim();
|
|
55890
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
55891
|
+
return trimmed;
|
|
54816
55892
|
}
|
|
55893
|
+
return trimmed.includes(":") ? `[${trimmed}]` : trimmed;
|
|
54817
55894
|
}
|
|
54818
|
-
function
|
|
54819
|
-
|
|
54820
|
-
|
|
54821
|
-
|
|
54822
|
-
const schemaJson = JSON.parse(readFileSync3(getBundledSchemaPath(), "utf-8"));
|
|
54823
|
-
const validateBySchema = ajv.compile(schemaJson);
|
|
54824
|
-
const valid = validateBySchema(config2);
|
|
54825
|
-
if (!valid) {
|
|
54826
|
-
const firstError = validateBySchema.errors?.[0];
|
|
54827
|
-
const path = firstError?.instancePath || "(root)";
|
|
54828
|
-
const message = firstError?.message ?? "unknown schema validation error";
|
|
54829
|
-
throw new Error(`Schema \u6821\u9A8C\u5931\u8D25: ${path} ${message}`);
|
|
55895
|
+
function resolveLocalAccessHost(listenHost) {
|
|
55896
|
+
const host = listenHost.trim().toLowerCase();
|
|
55897
|
+
if (host === "0.0.0.0" || host === "") {
|
|
55898
|
+
return "127.0.0.1";
|
|
54830
55899
|
}
|
|
55900
|
+
if (host === "::" || host === "[::]") {
|
|
55901
|
+
return "::1";
|
|
55902
|
+
}
|
|
55903
|
+
return listenHost.trim();
|
|
55904
|
+
}
|
|
55905
|
+
function createServerAddressInfo(listenHost, port) {
|
|
55906
|
+
const normalizedListenHost = listenHost.trim() || "0.0.0.0";
|
|
55907
|
+
const localHost = resolveLocalAccessHost(normalizedListenHost);
|
|
55908
|
+
return {
|
|
55909
|
+
listenHost: normalizedListenHost,
|
|
55910
|
+
localHost,
|
|
55911
|
+
port,
|
|
55912
|
+
listenUrl: `http://${formatUrlHost(normalizedListenHost)}:${port}`,
|
|
55913
|
+
localUrl: `http://${formatUrlHost(localHost)}:${port}`
|
|
55914
|
+
};
|
|
54831
55915
|
}
|
|
54832
55916
|
|
|
54833
55917
|
// src/index.ts
|
|
@@ -54869,13 +55953,18 @@ var ROUTE_REGISTRY = {
|
|
|
54869
55953
|
create: createAnthropicMessagesRoutes
|
|
54870
55954
|
}
|
|
54871
55955
|
};
|
|
54872
|
-
function printIntegrationGuide(config2) {
|
|
54873
|
-
const host = process.env.HOST ?? "
|
|
54874
|
-
const
|
|
54875
|
-
const
|
|
55956
|
+
function printIntegrationGuide(config2, listen = {}) {
|
|
55957
|
+
const host = listen.host ?? process.env.HOST ?? "0.0.0.0";
|
|
55958
|
+
const parsedPort = listen.port ?? Number.parseInt(process.env.PORT ?? "4099", 10);
|
|
55959
|
+
const address = createServerAddressInfo(host, Number.isFinite(parsedPort) ? parsedPort : 4099);
|
|
55960
|
+
const baseUrl = address.localUrl;
|
|
54876
55961
|
console.log(`
|
|
54877
55962
|
================ local-router \u63A5\u5165\u6307\u5357 ================`);
|
|
54878
|
-
console.log(`\
|
|
55963
|
+
console.log(`\u670D\u52A1\u76D1\u542C\u5730\u5740: ${address.listenUrl}`);
|
|
55964
|
+
console.log(`\u672C\u673A\u8BBF\u95EE\u5730\u5740: ${address.localUrl}`);
|
|
55965
|
+
if (address.listenHost === "0.0.0.0" || address.listenHost === "::" || address.listenHost === "[::]") {
|
|
55966
|
+
console.log("\u5C40\u57DF\u7F51\u8BBF\u95EE: \u5F00\u542F\u5C40\u57DF\u7F51\u670D\u52A1\u540E\uFF0C\u4F7F\u7528\u672C\u673A\u5C40\u57DF\u7F51 IP \u66FF\u6362\u4E0A\u9762\u7684\u672C\u673A\u5730\u5740\u3002");
|
|
55967
|
+
}
|
|
54879
55968
|
console.log("\u5065\u5EB7\u68C0\u67E5: GET /");
|
|
54880
55969
|
console.log(`API \u6587\u6863: ${baseUrl}/api/docs`);
|
|
54881
55970
|
console.log(`\u7BA1\u7406\u9762\u677F: ${baseUrl}/admin`);
|
|
@@ -55302,7 +56391,6 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
55302
56391
|
}
|
|
55303
56392
|
});
|
|
55304
56393
|
api2.get("/logs/tail", async (c2) => {
|
|
55305
|
-
const config2 = store.get();
|
|
55306
56394
|
const target = c2.req.raw;
|
|
55307
56395
|
const windowRaw = c2.req.query("window") ?? "1h";
|
|
55308
56396
|
if (!isLogQueryWindow(windowRaw)) {
|
|
@@ -55330,11 +56418,15 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
55330
56418
|
}
|
|
55331
56419
|
const encoder = new TextEncoder;
|
|
55332
56420
|
let closed = false;
|
|
55333
|
-
|
|
56421
|
+
const maxPendingItems = 500;
|
|
55334
56422
|
let closeStream = null;
|
|
55335
56423
|
const stream = new ReadableStream({
|
|
55336
56424
|
start(controller) {
|
|
55337
|
-
let
|
|
56425
|
+
let heartbeatTimer = null;
|
|
56426
|
+
let unsubscribe = null;
|
|
56427
|
+
let flushQueued = false;
|
|
56428
|
+
let droppedItems = 0;
|
|
56429
|
+
const pending = [];
|
|
55338
56430
|
const push2 = (event, payload) => {
|
|
55339
56431
|
if (closed)
|
|
55340
56432
|
return;
|
|
@@ -55348,60 +56440,106 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
55348
56440
|
if (closed)
|
|
55349
56441
|
return;
|
|
55350
56442
|
closed = true;
|
|
55351
|
-
if (
|
|
55352
|
-
clearInterval(
|
|
55353
|
-
|
|
56443
|
+
if (heartbeatTimer) {
|
|
56444
|
+
clearInterval(heartbeatTimer);
|
|
56445
|
+
heartbeatTimer = null;
|
|
55354
56446
|
}
|
|
56447
|
+
unsubscribe?.();
|
|
56448
|
+
unsubscribe = null;
|
|
55355
56449
|
target.signal.removeEventListener("abort", close);
|
|
55356
56450
|
try {
|
|
55357
56451
|
controller.close();
|
|
55358
56452
|
} catch {}
|
|
55359
56453
|
};
|
|
55360
56454
|
closeStream = close;
|
|
55361
|
-
|
|
55362
|
-
|
|
56455
|
+
const buildTailQuery = () => ({
|
|
56456
|
+
...resolveLogQueryRange({
|
|
56457
|
+
window: windowRaw,
|
|
56458
|
+
from: c2.req.query("from"),
|
|
56459
|
+
to: c2.req.query("to")
|
|
56460
|
+
}),
|
|
56461
|
+
levels,
|
|
56462
|
+
providers: parseCommaSeparated(c2.req.query("provider")),
|
|
56463
|
+
routeTypes: parseCommaSeparated(c2.req.query("routeType")),
|
|
56464
|
+
models: parseCommaSeparated(c2.req.query("model")),
|
|
56465
|
+
modelIns: parseCommaSeparated(c2.req.query("modelIn")),
|
|
56466
|
+
modelOuts: parseCommaSeparated(c2.req.query("modelOut")),
|
|
56467
|
+
users: parseCommaSeparated(c2.req.query("user")),
|
|
56468
|
+
sessions: parseCommaSeparated(c2.req.query("session")),
|
|
56469
|
+
statusClasses,
|
|
56470
|
+
hasError,
|
|
56471
|
+
q: c2.req.query("q") ?? "",
|
|
56472
|
+
sort: sortRaw,
|
|
56473
|
+
limit: 100,
|
|
56474
|
+
cursor: null
|
|
56475
|
+
});
|
|
56476
|
+
const flush = () => {
|
|
56477
|
+
flushQueued = false;
|
|
56478
|
+
if (closed || pending.length === 0)
|
|
56479
|
+
return;
|
|
56480
|
+
const items = pending.splice(0, pending.length);
|
|
56481
|
+
const overflowMessage = droppedItems > 0 ? `\u5B9E\u65F6\u8FFD\u8E2A\u961F\u5217\u5DF2\u4E22\u5F03 ${droppedItems} \u6761\u4E8B\u4EF6\uFF0C\u8BF7\u91CD\u65B0\u67E5\u8BE2\u4EE5\u8865\u9F50\u3002` : undefined;
|
|
56482
|
+
droppedItems = 0;
|
|
56483
|
+
push2("events", {
|
|
56484
|
+
items,
|
|
56485
|
+
nextCursor: null,
|
|
56486
|
+
hasMore: false,
|
|
56487
|
+
stats: {
|
|
56488
|
+
total: 0,
|
|
56489
|
+
errorCount: 0,
|
|
56490
|
+
errorRate: 0,
|
|
56491
|
+
avgLatencyMs: 0,
|
|
56492
|
+
p95LatencyMs: 0
|
|
56493
|
+
},
|
|
56494
|
+
meta: {
|
|
56495
|
+
scannedFiles: 0,
|
|
56496
|
+
scannedLines: 0,
|
|
56497
|
+
parseErrors: 0,
|
|
56498
|
+
truncated: false,
|
|
56499
|
+
indexUsed: true,
|
|
56500
|
+
indexFresh: true,
|
|
56501
|
+
usesFts: false,
|
|
56502
|
+
queryMs: 0,
|
|
56503
|
+
rowsReturned: items.length,
|
|
56504
|
+
fallbackReason: overflowMessage,
|
|
56505
|
+
statsMode: "none"
|
|
56506
|
+
}
|
|
56507
|
+
});
|
|
56508
|
+
};
|
|
56509
|
+
const queueFlush = () => {
|
|
56510
|
+
if (flushQueued)
|
|
56511
|
+
return;
|
|
56512
|
+
flushQueued = true;
|
|
56513
|
+
queueMicrotask(flush);
|
|
56514
|
+
};
|
|
56515
|
+
unsubscribe = subscribeLogEvents((published) => {
|
|
55363
56516
|
if (closed)
|
|
55364
56517
|
return;
|
|
55365
56518
|
try {
|
|
55366
|
-
const
|
|
55367
|
-
|
|
55368
|
-
fromMs: Math.max(lastSeenTs, toMs - 60 * 60 * 1000),
|
|
55369
|
-
toMs,
|
|
55370
|
-
levels,
|
|
55371
|
-
providers: parseCommaSeparated(c2.req.query("provider")),
|
|
55372
|
-
routeTypes: parseCommaSeparated(c2.req.query("routeType")),
|
|
55373
|
-
models: parseCommaSeparated(c2.req.query("model")),
|
|
55374
|
-
modelIns: parseCommaSeparated(c2.req.query("modelIn")),
|
|
55375
|
-
modelOuts: parseCommaSeparated(c2.req.query("modelOut")),
|
|
55376
|
-
users: parseCommaSeparated(c2.req.query("user")),
|
|
55377
|
-
sessions: parseCommaSeparated(c2.req.query("session")),
|
|
55378
|
-
statusClasses,
|
|
55379
|
-
hasError,
|
|
55380
|
-
q: c2.req.query("q") ?? "",
|
|
55381
|
-
sort: sortRaw,
|
|
55382
|
-
limit: 100
|
|
55383
|
-
});
|
|
55384
|
-
if (closed)
|
|
56519
|
+
const query = buildTailQuery();
|
|
56520
|
+
if (!logEventMatchesQuery(published.event, query))
|
|
55385
56521
|
return;
|
|
55386
|
-
if (
|
|
55387
|
-
|
|
55388
|
-
|
|
55389
|
-
lastSeenTs = Math.max(lastSeenTs, maxTs + 1);
|
|
55390
|
-
}
|
|
55391
|
-
push2("events", {
|
|
55392
|
-
items: data.items,
|
|
55393
|
-
stats: data.stats,
|
|
55394
|
-
meta: data.meta
|
|
55395
|
-
});
|
|
55396
|
-
} else {
|
|
55397
|
-
push2("heartbeat", { ts: new Date().toISOString() });
|
|
56522
|
+
if (pending.length >= maxPendingItems) {
|
|
56523
|
+
pending.shift();
|
|
56524
|
+
droppedItems += 1;
|
|
55398
56525
|
}
|
|
56526
|
+
pending.push(createLogEventSummaryFromEvent(published.event, {
|
|
56527
|
+
id: published.id,
|
|
56528
|
+
date: published.date,
|
|
56529
|
+
line: null
|
|
56530
|
+
}));
|
|
56531
|
+
queueFlush();
|
|
55399
56532
|
} catch (err) {
|
|
55400
|
-
if (closed)
|
|
55401
|
-
return;
|
|
55402
56533
|
push2("error", { error: err instanceof Error ? err.message : String(err) });
|
|
55403
56534
|
}
|
|
55404
|
-
}
|
|
56535
|
+
});
|
|
56536
|
+
push2("ready", { ok: true, now: new Date().toISOString() });
|
|
56537
|
+
heartbeatTimer = setInterval(() => {
|
|
56538
|
+
if (closed)
|
|
56539
|
+
return;
|
|
56540
|
+
push2("heartbeat", { ts: new Date().toISOString() });
|
|
56541
|
+
}, 15000);
|
|
56542
|
+
heartbeatTimer.unref?.();
|
|
55405
56543
|
target.signal.addEventListener("abort", close);
|
|
55406
56544
|
},
|
|
55407
56545
|
cancel() {
|
|
@@ -55514,8 +56652,9 @@ async function createApp(store, options) {
|
|
|
55514
56652
|
options?.registerCleanup?.(() => {
|
|
55515
56653
|
pluginManager.disposeAll().catch(() => {});
|
|
55516
56654
|
});
|
|
55517
|
-
printIntegrationGuide(config2);
|
|
56655
|
+
printIntegrationGuide(config2, options?.listen);
|
|
55518
56656
|
const app = new Hono2;
|
|
56657
|
+
app.use("*", createNetworkAccessMiddleware(store));
|
|
55519
56658
|
app.get("/", (c2) => c2.text("local-router is running"));
|
|
55520
56659
|
for (const [routeType, entry] of Object.entries(ROUTE_REGISTRY)) {
|
|
55521
56660
|
const subApp = entry.create(routeType, store, pluginManager);
|
|
@@ -55554,12 +56693,26 @@ async function createApp(store, options) {
|
|
|
55554
56693
|
async function createDefaultAppFromProcessArgs() {
|
|
55555
56694
|
const configPath = parseConfigPath();
|
|
55556
56695
|
const store = new ConfigStore(configPath);
|
|
55557
|
-
return createApp(store
|
|
56696
|
+
return createApp(store, {
|
|
56697
|
+
listen: {
|
|
56698
|
+
host: process.env.HOST ?? "0.0.0.0",
|
|
56699
|
+
port: Number.parseInt(process.env.PORT ?? "4099", 10)
|
|
56700
|
+
}
|
|
56701
|
+
});
|
|
55558
56702
|
}
|
|
55559
56703
|
|
|
55560
56704
|
// src/entry.ts
|
|
55561
56705
|
var app = await createDefaultAppFromProcessArgs();
|
|
55562
|
-
var entry_default =
|
|
56706
|
+
var entry_default = {
|
|
56707
|
+
hostname: process.env.HOST ?? "0.0.0.0",
|
|
56708
|
+
port: Number.parseInt(process.env.PORT ?? "4099", 10),
|
|
56709
|
+
fetch(request, server) {
|
|
56710
|
+
const remoteAddress = server.requestIP(request)?.address ?? null;
|
|
56711
|
+
return app.fetch(request, {
|
|
56712
|
+
[REMOTE_ADDRESS_ENV_KEY]: remoteAddress
|
|
56713
|
+
});
|
|
56714
|
+
}
|
|
56715
|
+
};
|
|
55563
56716
|
export {
|
|
55564
56717
|
entry_default as default
|
|
55565
56718
|
};
|