@maiyunnet/kebab 9.9.0 → 9.10.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/doc/kebab-rag.md +1 -1
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +1 -1
- package/sys/master.js +109 -35
package/doc/kebab-rag.md
CHANGED
|
@@ -1360,7 +1360,7 @@ index/variables/VER.md
|
|
|
1360
1360
|
|
|
1361
1361
|
# Variable: VER
|
|
1362
1362
|
|
|
1363
|
-
> `const` **VER**: `"9.
|
|
1363
|
+
> `const` **VER**: `"9.10.0"` = `'9.10.0'`
|
|
1364
1364
|
|
|
1365
1365
|
Defined in: [index.ts:10](https://github.com/maiyunnet/kebab/blob/master/index.ts#L10)
|
|
1366
1366
|
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* --- 本文件用来定义每个目录实体地址的常量 ---
|
|
7
7
|
*/
|
|
8
8
|
/** --- 当前系统版本号 --- */
|
|
9
|
-
export const VER = '9.
|
|
9
|
+
export const VER = '9.10.0';
|
|
10
10
|
// --- 服务端用的路径 ---
|
|
11
11
|
const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
|
|
12
12
|
/** --- /xxx/xxx --- */
|
package/package.json
CHANGED
package/sys/master.js
CHANGED
|
@@ -519,8 +519,88 @@ function createRpcListener() {
|
|
|
519
519
|
}));
|
|
520
520
|
return;
|
|
521
521
|
}
|
|
522
|
-
|
|
522
|
+
let limit = msg.limit ?? 100;
|
|
523
|
+
const offset = msg.offset ?? 0;
|
|
523
524
|
let total = 0;
|
|
525
|
+
if (msg.search) {
|
|
526
|
+
// === 搜索模式:total 为匹配行数,offset/limit 均基于匹配行 ===
|
|
527
|
+
// --- shell 单引号安全转义(防止特殊字符破坏命令)---
|
|
528
|
+
const escaped = msg.search.replace(/'/g, "'\\''");
|
|
529
|
+
// --- 获取匹配行总数(grep -c 无匹配时退出码 1,|| echo 0 兜底)---
|
|
530
|
+
const countCmd = format === 'csv'
|
|
531
|
+
? `tail -n +2 "${path}" | grep -F -c '${escaped}' || echo 0`
|
|
532
|
+
: `grep -F -c '${escaped}' "${path}" || echo 0`;
|
|
533
|
+
const countRtn = await lCore.exec(countCmd);
|
|
534
|
+
if (countRtn !== false) {
|
|
535
|
+
total = parseInt(countRtn.trim()) || 0;
|
|
536
|
+
}
|
|
537
|
+
// --- 获取分页数据:第 offset+1 到 offset+limit 条匹配行(1-indexed)---
|
|
538
|
+
const from = offset + 1;
|
|
539
|
+
const to = offset + limit;
|
|
540
|
+
const dataCmd = format === 'csv'
|
|
541
|
+
? `tail -n +2 "${path}" | grep -F '${escaped}' | sed -n '${from},${to}p'`
|
|
542
|
+
: `grep -F '${escaped}' "${path}" | sed -n '${from},${to}p'`;
|
|
543
|
+
const dataRtn = await lCore.exec(dataCmd);
|
|
544
|
+
if (dataRtn === false) {
|
|
545
|
+
res.end(lText.stringifyJson({
|
|
546
|
+
'result': 1,
|
|
547
|
+
'list': [],
|
|
548
|
+
'total': total,
|
|
549
|
+
}));
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* --- csv 格式:string[][] 每行是字段数组,顺序与表头一致 ---
|
|
554
|
+
* --- [['H:i:s', unix, url, cookie, session, userAgent, realIp, cfIp, xIp, osMem, procMem, message], ...] ---
|
|
555
|
+
* --- jsonl 格式:object[] 每行是解析后的 JSON 对象 ---
|
|
556
|
+
* --- [{ time, unix, url, cookie, session, userAgent, realIp, cfIp, xIp, osMem, procMem, message }, ...] ---
|
|
557
|
+
*/
|
|
558
|
+
const sList = [];
|
|
559
|
+
for (const rawLine of dataRtn.split('\n')) {
|
|
560
|
+
if (!rawLine) {
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
if (format === 'jsonl') {
|
|
564
|
+
const obj = lText.parseJson(rawLine);
|
|
565
|
+
if (obj) {
|
|
566
|
+
sList.push(obj);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
const result = [];
|
|
571
|
+
let currentField = '';
|
|
572
|
+
let inQuotes = false;
|
|
573
|
+
for (let i = 0; i < rawLine.length; ++i) {
|
|
574
|
+
const char = rawLine[i];
|
|
575
|
+
if (char === '"') {
|
|
576
|
+
if (inQuotes && rawLine[i + 1] === '"') {
|
|
577
|
+
currentField += '"';
|
|
578
|
+
++i;
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
inQuotes = !inQuotes;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
else if (char === ',' && !inQuotes) {
|
|
585
|
+
result.push(currentField);
|
|
586
|
+
currentField = '';
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
currentField += char;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
result.push(currentField);
|
|
593
|
+
sList.push(result);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
res.end(lText.stringifyJson({
|
|
597
|
+
'result': 1,
|
|
598
|
+
'list': sList,
|
|
599
|
+
'total': total,
|
|
600
|
+
}));
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
// === 无搜索模式:wc -l 获取总行数,grep -b '^' 定位字节偏移直接跳至 offset ===
|
|
524
604
|
const wclRtn = await lCore.exec(`wc -l "${path}"`);
|
|
525
605
|
if (wclRtn !== false) {
|
|
526
606
|
const wclMatch = /^\s*(\d+)/.exec(wclRtn);
|
|
@@ -532,10 +612,6 @@ function createRpcListener() {
|
|
|
532
612
|
}
|
|
533
613
|
}
|
|
534
614
|
}
|
|
535
|
-
/** --- 剩余 limit --- */
|
|
536
|
-
let limit = msg.limit ?? 100;
|
|
537
|
-
/** --- offset --- */
|
|
538
|
-
const offset = msg.offset ?? 0;
|
|
539
615
|
/**
|
|
540
616
|
* --- 用 grep -b '^' 获取目标行的字节偏移,直接传给 createReadStream start 跳过 offset ---
|
|
541
617
|
* --- jsonl 无表头:跳过 offset 行,从第 offset+1 行开始读 ---
|
|
@@ -560,7 +636,7 @@ function createRpcListener() {
|
|
|
560
636
|
// --- offset 超出文件范围,返回空列表 ---
|
|
561
637
|
res.end(lText.stringifyJson({
|
|
562
638
|
'result': 1,
|
|
563
|
-
'
|
|
639
|
+
'list': [],
|
|
564
640
|
'total': total,
|
|
565
641
|
}));
|
|
566
642
|
return;
|
|
@@ -602,41 +678,39 @@ function createRpcListener() {
|
|
|
602
678
|
// --- startByte > 0 时已跳过 offset(及 csv 表头),从第一行起均为数据行 ---
|
|
603
679
|
// --- startByte === 0 且 csv 时仍需跳过表头(line > 1)---
|
|
604
680
|
if (format === 'csv' && startByte === 0 ? line > 1 : line >= 1) {
|
|
605
|
-
if (
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
--limit;
|
|
611
|
-
}
|
|
681
|
+
if (format === 'jsonl') {
|
|
682
|
+
const obj = lText.parseJson(packet);
|
|
683
|
+
if (obj) {
|
|
684
|
+
list.push(obj);
|
|
685
|
+
--limit;
|
|
612
686
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
else {
|
|
625
|
-
inQuotes = !inQuotes;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
else if (char === ',' && !inQuotes) {
|
|
629
|
-
result.push(currentField);
|
|
630
|
-
currentField = '';
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
const result = [];
|
|
690
|
+
let currentField = '';
|
|
691
|
+
let inQuotes = false;
|
|
692
|
+
for (let i = 0; i < packet.length; ++i) {
|
|
693
|
+
const char = packet[i];
|
|
694
|
+
if (char === '"') {
|
|
695
|
+
if (inQuotes && packet[i + 1] === '"') {
|
|
696
|
+
currentField += '"';
|
|
697
|
+
++i;
|
|
631
698
|
}
|
|
632
699
|
else {
|
|
633
|
-
|
|
700
|
+
inQuotes = !inQuotes;
|
|
634
701
|
}
|
|
635
702
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
703
|
+
else if (char === ',' && !inQuotes) {
|
|
704
|
+
result.push(currentField);
|
|
705
|
+
currentField = '';
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
currentField += char;
|
|
709
|
+
}
|
|
639
710
|
}
|
|
711
|
+
result.push(currentField);
|
|
712
|
+
list.push(result);
|
|
713
|
+
--limit;
|
|
640
714
|
}
|
|
641
715
|
}
|
|
642
716
|
// --- 处理结束 ---
|