baiduwangpan-cli 1.6.0 → 1.7.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/LICENSE +21 -21
- package/README.md +504 -471
- package/bin/bdp.js +1647 -1452
- package/lib/browser-login.js +427 -427
- package/lib/config.js +213 -213
- package/lib/console.js +99 -99
- package/lib/file-type.js +109 -0
- package/lib/group.js +929 -925
- package/lib/http.js +130 -130
- package/lib/index.js +7 -7
- package/lib/mcp.js +287 -0
- package/lib/name-match.js +67 -67
- package/lib/pan.js +1034 -767
- package/lib/partial.js +53 -53
- package/lib/pcs-extra.js +327 -318
- package/lib/table.js +173 -0
- package/package.json +48 -48
- package/scripts/postinstall.js +340 -340
package/lib/table.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// lib/table.js — BaiduPCS-Go pcstable 表格解析(共享模块)
|
|
2
|
+
//
|
|
3
|
+
// BaiduPCS-Go 用 tablewriter 渲染表格:无边框、无列分隔符、列按**显示宽度**对齐。
|
|
4
|
+
// 含 CJK 时各行的码元偏移并不一致(一个汉字占 2 列),必须先换算成显示宽度再切片。
|
|
5
|
+
//
|
|
6
|
+
// 本模块被 pan.js(ls/search 结构化)与 pcs-extra.js(share list 等)共用。
|
|
7
|
+
|
|
8
|
+
const SIZE_UNITS = { B: 1, KB: 1024, MB: 1024 ** 2, GB: 1024 ** 3, TB: 1024 ** 4, PB: 1024 ** 5 };
|
|
9
|
+
|
|
10
|
+
// 解析 "1.234567GB" / "129B" / "100 MB" → 字节数;无法解析返回 null
|
|
11
|
+
function parseSizeText(text) {
|
|
12
|
+
const m = String(text || "").trim().match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB|PB)?$/i);
|
|
13
|
+
if (!m) return null;
|
|
14
|
+
const unit = (m[2] || "B").toUpperCase();
|
|
15
|
+
return Math.round(parseFloat(m[1]) * SIZE_UNITS[unit]);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 东亚宽字符(CJK/全角等)显示宽度近似,对应 go-runewidth 的 East Asian Width W/F:
|
|
19
|
+
// 计 2 列,其余字符计 1 列。表头/数据行均按此规则渲染,切分即自洽。
|
|
20
|
+
const WIDE_CHAR_RE = /[\u1100-\u115f\u2e80-\ua4cf\uac00-\ud7a3\uf900-\ufaff\ufe30-\ufe4f\uff00-\uff60\uffe0-\uffe6]/;
|
|
21
|
+
|
|
22
|
+
function dispWidth(str) {
|
|
23
|
+
let w = 0;
|
|
24
|
+
for (const ch of String(str)) w += WIDE_CHAR_RE.test(ch) ? 2 : 1;
|
|
25
|
+
return w;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 在 line 中取显示宽度 [startDisp, endDisp) 覆盖的码元区间
|
|
29
|
+
function sliceByDisplay(line, startDisp, endDisp) {
|
|
30
|
+
const len = line.length;
|
|
31
|
+
const dispAt = new Array(len + 1);
|
|
32
|
+
let d = 0;
|
|
33
|
+
for (let i = 0; i < len; i++) {
|
|
34
|
+
dispAt[i] = d;
|
|
35
|
+
d += dispWidth(line[i]);
|
|
36
|
+
}
|
|
37
|
+
dispAt[len] = d;
|
|
38
|
+
let s = len;
|
|
39
|
+
let e = len;
|
|
40
|
+
for (let i = 0; i <= len; i++) {
|
|
41
|
+
if (s === len && dispAt[i] >= startDisp) s = i;
|
|
42
|
+
if (dispAt[i] >= endDisp) {
|
|
43
|
+
e = i;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return line.slice(s, e);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 解析 BaiduPCS-Go 的 pcstable 表格。
|
|
51
|
+
// 原理: 表头与数据行在同一表格渲染下列起始显示偏移一致 → 用表头标题的显示位置切分每行。
|
|
52
|
+
// 表头找不到返回 null。
|
|
53
|
+
function parseTable(output, titles) {
|
|
54
|
+
const lines = String(output).split(/\r?\n/).filter((l) => l.trim() !== "");
|
|
55
|
+
const headerIdx = lines.findIndex((l) => titles.every((t) => l.includes(t)));
|
|
56
|
+
if (headerIdx === -1) return null;
|
|
57
|
+
const header = lines[headerIdx];
|
|
58
|
+
const offsets = [];
|
|
59
|
+
let pos = 0;
|
|
60
|
+
for (const t of titles) {
|
|
61
|
+
const idx = header.indexOf(t, pos);
|
|
62
|
+
if (idx === -1) return null;
|
|
63
|
+
offsets.push(dispWidth(header.slice(0, idx)));
|
|
64
|
+
pos = idx + t.length;
|
|
65
|
+
}
|
|
66
|
+
const rows = [];
|
|
67
|
+
for (let i = headerIdx + 1; i < lines.length; i++) {
|
|
68
|
+
const cells = [];
|
|
69
|
+
for (let c = 0; c < titles.length; c++) {
|
|
70
|
+
const start = offsets[c];
|
|
71
|
+
const end = c + 1 < titles.length ? offsets[c + 1] : dispWidth(lines[i]);
|
|
72
|
+
cells.push(sliceByDisplay(lines[i], start, end).trim());
|
|
73
|
+
}
|
|
74
|
+
rows.push(cells);
|
|
75
|
+
}
|
|
76
|
+
return rows;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// "2026-01-07 14:15:59" → 本地时区 epoch 毫秒;无法解析返回 null
|
|
80
|
+
function parseMtime(text) {
|
|
81
|
+
const s = String(text || "").trim();
|
|
82
|
+
if (!/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}$/.test(s)) return null;
|
|
83
|
+
const t = new Date(s.replace(" ", "T")).getTime();
|
|
84
|
+
return Number.isNaN(t) ? null : t;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 引擎数据行的结构:<序号> <大小> <日期时间> <名称/路径>
|
|
88
|
+
//
|
|
89
|
+
// 关键:不能用"表头标题的显示偏移"来切列 —— go-olekukonko/tablewriter 的
|
|
90
|
+
// 表头是**居中**的,数据单元格按类型对齐(数字右对齐、字符串左对齐),
|
|
91
|
+
// 因此表头与数据的列偏移并不一致(实测 ls 表头"修改日期"在 disp 25,
|
|
92
|
+
// 数据日期在 disp 19),按偏移切会整体错位。
|
|
93
|
+
//
|
|
94
|
+
// 可靠锚点是日期列:"YYYY-MM-DD HH:MM:SS" 格式唯一且固定宽度。
|
|
95
|
+
const ROW_RE = /^\s*(\d+)\s+(.*?)\s*(\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2})\s+(.*?)\s*$/;
|
|
96
|
+
|
|
97
|
+
// 解析一行 → { index, sizeText, mtimeText, rest };不是数据行返回 null
|
|
98
|
+
function parseEngineRow(line) {
|
|
99
|
+
const m = String(line).match(ROW_RE);
|
|
100
|
+
if (!m) return null;
|
|
101
|
+
let sizeText = m[2].trim();
|
|
102
|
+
if (sizeText === "-" || sizeText === "") sizeText = null;
|
|
103
|
+
return {
|
|
104
|
+
index: parseInt(m[1], 10),
|
|
105
|
+
sizeText,
|
|
106
|
+
mtimeText: m[3],
|
|
107
|
+
rest: m[4],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ls / search 共用的行 → 结构化条目
|
|
112
|
+
function rowToEntry(row, { pathPrefix, isSearch }) {
|
|
113
|
+
if (!row) return null;
|
|
114
|
+
const isDir = row.rest.endsWith("/") || row.sizeText === null;
|
|
115
|
+
const name = isSearch
|
|
116
|
+
? (row.rest.split("/").pop() || row.rest)
|
|
117
|
+
: row.rest.replace(/\/+$/, "");
|
|
118
|
+
const fullPath = isSearch ? row.rest : joinPath(pathPrefix, row.rest);
|
|
119
|
+
return {
|
|
120
|
+
index: row.index,
|
|
121
|
+
name,
|
|
122
|
+
path: fullPath,
|
|
123
|
+
isDir,
|
|
124
|
+
sizeText: row.sizeText,
|
|
125
|
+
size: row.sizeText ? parseSizeText(row.sizeText) : null,
|
|
126
|
+
mtimeText: row.mtimeText,
|
|
127
|
+
mtime: parseMtime(row.mtimeText),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function joinPath(prefix, name) {
|
|
132
|
+
if (!prefix || prefix === "/") return "/" + name;
|
|
133
|
+
return prefix.replace(/\/+$/, "") + "/" + name;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 表尾汇总行: "总: 239.34MB 文件总数: 5, 目录总数: 19"
|
|
137
|
+
function parseFooter(output) {
|
|
138
|
+
const m = String(output).match(/总:\s*([^\s]+)\s+文件总数:\s*(\d+),\s*目录总数:\s*(\d+)/);
|
|
139
|
+
if (!m) return { totalText: null, fileCount: null, dirCount: null };
|
|
140
|
+
return {
|
|
141
|
+
totalText: m[1],
|
|
142
|
+
fileCount: parseInt(m[2], 10),
|
|
143
|
+
dirCount: parseInt(m[3], 10),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// bdp ls 输出 → { entries, totalText, fileCount, dirCount }
|
|
148
|
+
function parseLsTable(output, pathPrefix = "/") {
|
|
149
|
+
const lines = String(output).split(/\r?\n/);
|
|
150
|
+
const entries = [];
|
|
151
|
+
for (const line of lines) {
|
|
152
|
+
const e = rowToEntry(parseEngineRow(line), { pathPrefix, isSearch: false });
|
|
153
|
+
if (e) entries.push(e);
|
|
154
|
+
}
|
|
155
|
+
return { entries, ...parseFooter(output) };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// bdp search 输出 → { entries, totalText, fileCount, dirCount }
|
|
159
|
+
function parseSearchTable(output) {
|
|
160
|
+
const lines = String(output).split(/\r?\n/);
|
|
161
|
+
const entries = [];
|
|
162
|
+
for (const line of lines) {
|
|
163
|
+
const e = rowToEntry(parseEngineRow(line), { pathPrefix: null, isSearch: true });
|
|
164
|
+
if (e) entries.push(e);
|
|
165
|
+
}
|
|
166
|
+
return { entries, ...parseFooter(output) };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = {
|
|
170
|
+
dispWidth, sliceByDisplay, parseTable,
|
|
171
|
+
parseSizeText, parseMtime, parseEngineRow,
|
|
172
|
+
parseLsTable, parseSearchTable,
|
|
173
|
+
};
|
package/package.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "baiduwangpan-cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "百度网盘 CLI — 全盘读写 · 免下载读取文件内容 · 群聊文件浏览 (Agent 友好)",
|
|
5
|
-
"main": "lib/index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"bdp": "bin/bdp.js"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"start": "node bin/bdp.js",
|
|
11
|
-
"test": "node --test test/*.test.js",
|
|
12
|
-
"postinstall": "node scripts/postinstall.js"
|
|
13
|
-
},
|
|
14
|
-
"engines": {
|
|
15
|
-
"node": ">=14"
|
|
16
|
-
},
|
|
17
|
-
"dependencies": {
|
|
18
|
-
"ws": "^8.18.0"
|
|
19
|
-
},
|
|
20
|
-
"license": "MIT",
|
|
21
|
-
"keywords": [
|
|
22
|
-
"baidu",
|
|
23
|
-
"netdisk",
|
|
24
|
-
"pan",
|
|
25
|
-
"cli",
|
|
26
|
-
"agent",
|
|
27
|
-
"bduss",
|
|
28
|
-
"baidupcs",
|
|
29
|
-
"baidu-pan",
|
|
30
|
-
"baiduyunpan",
|
|
31
|
-
"ai-agent",
|
|
32
|
-
"mcp"
|
|
33
|
-
],
|
|
34
|
-
"files": [
|
|
35
|
-
"bin/",
|
|
36
|
-
"lib/",
|
|
37
|
-
"scripts/postinstall.js"
|
|
38
|
-
],
|
|
39
|
-
"repository": {
|
|
40
|
-
"type": "git",
|
|
41
|
-
"url": "git+https://github.com/NkAntony777/baiduwangpan-cli.git"
|
|
42
|
-
},
|
|
43
|
-
"homepage": "https://github.com/NkAntony777/baiduwangpan-cli",
|
|
44
|
-
"bugs": {
|
|
45
|
-
"url": "https://github.com/NkAntony777/baiduwangpan-cli/issues"
|
|
46
|
-
},
|
|
47
|
-
"author": "NkAntony777"
|
|
48
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "baiduwangpan-cli",
|
|
3
|
+
"version": "1.7.0",
|
|
4
|
+
"description": "百度网盘 CLI — 全盘读写 · 免下载读取文件内容 · 群聊文件浏览 (Agent 友好)",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"bdp": "bin/bdp.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/bdp.js",
|
|
11
|
+
"test": "node --test test/*.test.js",
|
|
12
|
+
"postinstall": "node scripts/postinstall.js"
|
|
13
|
+
},
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=14"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"ws": "^8.18.0"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"keywords": [
|
|
22
|
+
"baidu",
|
|
23
|
+
"netdisk",
|
|
24
|
+
"pan",
|
|
25
|
+
"cli",
|
|
26
|
+
"agent",
|
|
27
|
+
"bduss",
|
|
28
|
+
"baidupcs",
|
|
29
|
+
"baidu-pan",
|
|
30
|
+
"baiduyunpan",
|
|
31
|
+
"ai-agent",
|
|
32
|
+
"mcp"
|
|
33
|
+
],
|
|
34
|
+
"files": [
|
|
35
|
+
"bin/",
|
|
36
|
+
"lib/",
|
|
37
|
+
"scripts/postinstall.js"
|
|
38
|
+
],
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/NkAntony777/baiduwangpan-cli.git"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/NkAntony777/baiduwangpan-cli",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/NkAntony777/baiduwangpan-cli/issues"
|
|
46
|
+
},
|
|
47
|
+
"author": "NkAntony777"
|
|
48
|
+
}
|