@sovovs/bycli 2.1.39 → 2.1.40
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/cli-manifest.json +46 -1
- package/clis/ima/knowledge-list.js +48 -0
- package/clis/ima/knowledge.js +23 -0
- package/clis/ima/native-api.js +67 -10
- package/clis/ima/native-client.js +23 -7
- package/clis/ima/utils.js +1 -0
- package/package.json +1 -1
package/cli-manifest.json
CHANGED
|
@@ -13570,7 +13570,30 @@
|
|
|
13570
13570
|
"title",
|
|
13571
13571
|
"url",
|
|
13572
13572
|
"contentType",
|
|
13573
|
-
"addedDate"
|
|
13573
|
+
"addedDate",
|
|
13574
|
+
"mediaId",
|
|
13575
|
+
"mediaType",
|
|
13576
|
+
"mediaState",
|
|
13577
|
+
"mediaAuditStatus",
|
|
13578
|
+
"mediaTypeInfo",
|
|
13579
|
+
"sourcePath",
|
|
13580
|
+
"jumpUrl",
|
|
13581
|
+
"createTime",
|
|
13582
|
+
"updateTime",
|
|
13583
|
+
"lastModifyTime",
|
|
13584
|
+
"lastOpenTime",
|
|
13585
|
+
"fileSize",
|
|
13586
|
+
"abstract",
|
|
13587
|
+
"introduction",
|
|
13588
|
+
"tags",
|
|
13589
|
+
"isTop",
|
|
13590
|
+
"accessStatus",
|
|
13591
|
+
"accessStatusUpdateTs",
|
|
13592
|
+
"parseProgress",
|
|
13593
|
+
"parseErrInfo",
|
|
13594
|
+
"summaryState",
|
|
13595
|
+
"coverUrls",
|
|
13596
|
+
"logo"
|
|
13574
13597
|
],
|
|
13575
13598
|
"defaultFormat": "json",
|
|
13576
13599
|
"type": "js",
|
|
@@ -13578,6 +13601,28 @@
|
|
|
13578
13601
|
"sourceFile": "ima/knowledge.js",
|
|
13579
13602
|
"navigateBefore": false
|
|
13580
13603
|
},
|
|
13604
|
+
{
|
|
13605
|
+
"site": "ima",
|
|
13606
|
+
"name": "knowledge-list",
|
|
13607
|
+
"description": "获取全部 ima 知识库的常用字段与原始元数据",
|
|
13608
|
+
"access": "read",
|
|
13609
|
+
"domain": "ima.qq.com",
|
|
13610
|
+
"strategy": "cookie",
|
|
13611
|
+
"browser": true,
|
|
13612
|
+
"args": [],
|
|
13613
|
+
"columns": [
|
|
13614
|
+
"id",
|
|
13615
|
+
"name",
|
|
13616
|
+
"type",
|
|
13617
|
+
"typeName",
|
|
13618
|
+
"raw"
|
|
13619
|
+
],
|
|
13620
|
+
"defaultFormat": "json",
|
|
13621
|
+
"type": "js",
|
|
13622
|
+
"modulePath": "ima/knowledge-list.js",
|
|
13623
|
+
"sourceFile": "ima/knowledge-list.js",
|
|
13624
|
+
"navigateBefore": false
|
|
13625
|
+
},
|
|
13581
13626
|
{
|
|
13582
13627
|
"site": "imdb",
|
|
13583
13628
|
"name": "person",
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { cli, Strategy } from '@sovovs/bycli/registry';
|
|
2
|
+
import { CommandExecutionError, ConfigError } from '@sovovs/bycli/errors';
|
|
3
|
+
|
|
4
|
+
import { readKnowledgeBasesFromChrome } from './native-client.js';
|
|
5
|
+
|
|
6
|
+
const COMMAND = 'ima knowledge-list';
|
|
7
|
+
|
|
8
|
+
function throwDriverError(error) {
|
|
9
|
+
if (error?.code === 'IMA_CHROME_AUTH_REQUIRED') {
|
|
10
|
+
throw new ConfigError(
|
|
11
|
+
error.message || 'ima reader authentication is required',
|
|
12
|
+
'Open https://ima.qq.com/wikis in Chrome, sign in, then retry with the latest bycli Browser Bridge extension.',
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
throw new CommandExecutionError(
|
|
16
|
+
error?.message || `${COMMAND} failed to read knowledge bases`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function runKnowledgeListCommand(read) {
|
|
21
|
+
try {
|
|
22
|
+
const items = await read();
|
|
23
|
+
if (!Array.isArray(items)) {
|
|
24
|
+
throw new CommandExecutionError(`${COMMAND} returned malformed knowledge-base rows`);
|
|
25
|
+
}
|
|
26
|
+
return items;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error instanceof ConfigError || error instanceof CommandExecutionError) throw error;
|
|
29
|
+
throwDriverError(error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const knowledgeListCommand = cli({
|
|
34
|
+
site: 'ima',
|
|
35
|
+
name: 'knowledge-list',
|
|
36
|
+
access: 'read',
|
|
37
|
+
description: '获取全部 ima 知识库的常用字段与原始元数据',
|
|
38
|
+
domain: 'ima.qq.com',
|
|
39
|
+
defaultFormat: 'json',
|
|
40
|
+
args: [],
|
|
41
|
+
columns: ['id', 'name', 'type', 'typeName', 'raw'],
|
|
42
|
+
strategy: Strategy.COOKIE,
|
|
43
|
+
browser: true,
|
|
44
|
+
navigateBefore: false,
|
|
45
|
+
func: async (page) => runKnowledgeListCommand(
|
|
46
|
+
() => readKnowledgeBasesFromChrome(page),
|
|
47
|
+
),
|
|
48
|
+
});
|
package/clis/ima/knowledge.js
CHANGED
|
@@ -84,6 +84,29 @@ export const knowledgeCommand = cli({
|
|
|
84
84
|
'url',
|
|
85
85
|
'contentType',
|
|
86
86
|
'addedDate',
|
|
87
|
+
'mediaId',
|
|
88
|
+
'mediaType',
|
|
89
|
+
'mediaState',
|
|
90
|
+
'mediaAuditStatus',
|
|
91
|
+
'mediaTypeInfo',
|
|
92
|
+
'sourcePath',
|
|
93
|
+
'jumpUrl',
|
|
94
|
+
'createTime',
|
|
95
|
+
'updateTime',
|
|
96
|
+
'lastModifyTime',
|
|
97
|
+
'lastOpenTime',
|
|
98
|
+
'fileSize',
|
|
99
|
+
'abstract',
|
|
100
|
+
'introduction',
|
|
101
|
+
'tags',
|
|
102
|
+
'isTop',
|
|
103
|
+
'accessStatus',
|
|
104
|
+
'accessStatusUpdateTs',
|
|
105
|
+
'parseProgress',
|
|
106
|
+
'parseErrInfo',
|
|
107
|
+
'summaryState',
|
|
108
|
+
'coverUrls',
|
|
109
|
+
'logo',
|
|
87
110
|
],
|
|
88
111
|
strategy: Strategy.COOKIE,
|
|
89
112
|
browser: true,
|
package/clis/ima/native-api.js
CHANGED
|
@@ -4,6 +4,10 @@ const MEDIA_TYPE_NAMES = new Map([
|
|
|
4
4
|
[12, '问答'], [13, 'TXT'], [14, 'XMIND'], [15, '音频'], [16, '视频网站'],
|
|
5
5
|
[19, '播客'], [20, 'HTML'], [21, 'EPUB'], [98, '源代码'], [99, '文件夹'],
|
|
6
6
|
]);
|
|
7
|
+
const KNOWLEDGE_BASE_TYPE_NAMES = new Map([
|
|
8
|
+
[1001, '我的知识库'], [1002, '共享知识库'],
|
|
9
|
+
[1004, '团队知识库'], [1005, '其他知识库'],
|
|
10
|
+
]);
|
|
7
11
|
function field(value, camelName, snakeName) {
|
|
8
12
|
return value?.[camelName] ?? value?.[snakeName];
|
|
9
13
|
}
|
|
@@ -20,6 +24,15 @@ function knowledgeBaseFromRaw(raw) {
|
|
|
20
24
|
};
|
|
21
25
|
}
|
|
22
26
|
|
|
27
|
+
function knowledgeBaseRow(raw, type) {
|
|
28
|
+
return {
|
|
29
|
+
...knowledgeBaseFromRaw(raw),
|
|
30
|
+
type,
|
|
31
|
+
typeName: KNOWLEDGE_BASE_TYPE_NAMES.get(type) ?? String(type),
|
|
32
|
+
raw,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
function basesFromGroups(response) {
|
|
24
37
|
const groups = field(response, 'results', 'results');
|
|
25
38
|
if (!Array.isArray(groups)) throw new Error('ima API returned malformed knowledge-base groups');
|
|
@@ -29,7 +42,17 @@ function basesFromGroups(response) {
|
|
|
29
42
|
});
|
|
30
43
|
}
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
function entriesFromGroups(response) {
|
|
46
|
+
const groups = field(response, 'results', 'results');
|
|
47
|
+
if (!Array.isArray(groups)) throw new Error('ima API returned malformed knowledge-base groups');
|
|
48
|
+
return groups.flatMap((group) => {
|
|
49
|
+
const list = field(group, 'knowledgeBaseList', 'knowledge_base_list');
|
|
50
|
+
const type = Number(field(group, 'type', 'type'));
|
|
51
|
+
return Array.isArray(list) ? list.map((raw) => ({ raw, type })) : [];
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function collectKnowledgeBaseEntries(request) {
|
|
33
56
|
const initialGroups = [
|
|
34
57
|
{ type: 1001, limit: 20 },
|
|
35
58
|
{ type: 1002, limit: 20 },
|
|
@@ -47,7 +70,7 @@ export async function findKnowledgeBase(query, request) {
|
|
|
47
70
|
if (Number(response?.code) !== 0) {
|
|
48
71
|
throw new Error(response?.msg || `ima API error ${response?.code ?? 'unknown'}`);
|
|
49
72
|
}
|
|
50
|
-
all.push(...
|
|
73
|
+
all.push(...entriesFromGroups(response));
|
|
51
74
|
const groups = field(response, 'results', 'results');
|
|
52
75
|
for (const group of groups) {
|
|
53
76
|
const cursor = field(group, 'nextCursor', 'next_cursor');
|
|
@@ -61,19 +84,29 @@ export async function findKnowledgeBase(query, request) {
|
|
|
61
84
|
throw new Error(`ima API returned a repeated cursor for knowledge-base group ${type}`);
|
|
62
85
|
}
|
|
63
86
|
queuedPages.add(pageKey);
|
|
64
|
-
pendingPages.push({
|
|
65
|
-
type,
|
|
66
|
-
cursor: String(cursor),
|
|
67
|
-
limit: 10,
|
|
68
|
-
});
|
|
87
|
+
pendingPages.push({ type, cursor: String(cursor), limit: 10 });
|
|
69
88
|
}
|
|
70
89
|
}
|
|
71
90
|
const next = pendingPages.shift();
|
|
72
91
|
if (!next) break;
|
|
73
|
-
response = await request('/get_knowledge_base_list', {
|
|
74
|
-
params: [next],
|
|
75
|
-
});
|
|
92
|
+
response = await request('/get_knowledge_base_list', { params: [next] });
|
|
76
93
|
}
|
|
94
|
+
return all;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function listKnowledgeBases(request) {
|
|
98
|
+
const entries = await collectKnowledgeBaseEntries(request);
|
|
99
|
+
const unique = new Map();
|
|
100
|
+
for (const { raw, type } of entries) {
|
|
101
|
+
const row = knowledgeBaseRow(raw, type);
|
|
102
|
+
if (!unique.has(row.id)) unique.set(row.id, row);
|
|
103
|
+
}
|
|
104
|
+
return [...unique.values()];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function findKnowledgeBase(query, request) {
|
|
108
|
+
const all = (await collectKnowledgeBaseEntries(request))
|
|
109
|
+
.map(({ raw }) => knowledgeBaseFromRaw(raw));
|
|
77
110
|
|
|
78
111
|
const matches = all.filter((base) => base.id === query || base.name === query);
|
|
79
112
|
if (matches.length === 0) {
|
|
@@ -100,6 +133,30 @@ function articleFromRaw(raw, knowledgeBaseId, knowledgeBaseName, folderPath) {
|
|
|
100
133
|
addedDate: field(raw, 'timeWording', 'time_wording')
|
|
101
134
|
|| field(raw, 'createTime', 'create_time')
|
|
102
135
|
|| null,
|
|
136
|
+
mediaId: field(raw, 'mediaId', 'media_id') ?? null,
|
|
137
|
+
mediaType,
|
|
138
|
+
mediaState: field(raw, 'mediaState', 'media_state') ?? null,
|
|
139
|
+
mediaAuditStatus: field(raw, 'mediaAuditStatus', 'media_audit_status') ?? null,
|
|
140
|
+
mediaTypeInfo: field(raw, 'mediaTypeInfo', 'media_type_info') ?? null,
|
|
141
|
+
sourcePath: sourcePath ?? null,
|
|
142
|
+
jumpUrl: jumpUrl ?? null,
|
|
143
|
+
createTime: field(raw, 'createTime', 'create_time') ?? null,
|
|
144
|
+
updateTime: field(raw, 'updateTime', 'update_time') ?? null,
|
|
145
|
+
lastModifyTime: field(raw, 'lastModifyTime', 'last_modify_time') ?? null,
|
|
146
|
+
lastOpenTime: field(raw, 'lastOpenTime', 'last_open_time') ?? null,
|
|
147
|
+
fileSize: field(raw, 'fileSize', 'file_size') ?? null,
|
|
148
|
+
abstract: field(raw, 'abstract', 'abstract') ?? null,
|
|
149
|
+
introduction: field(raw, 'introduction', 'introduction') ?? null,
|
|
150
|
+
tags: Array.isArray(field(raw, 'tags', 'tags')) ? field(raw, 'tags', 'tags') : [],
|
|
151
|
+
isTop: field(raw, 'isTop', 'is_top') ?? null,
|
|
152
|
+
accessStatus: field(raw, 'accessStatus', 'access_status') ?? null,
|
|
153
|
+
accessStatusUpdateTs: field(raw, 'accessStatusUpdateTs', 'access_status_update_ts') ?? null,
|
|
154
|
+
parseProgress: field(raw, 'parseProgress', 'parse_progress') ?? null,
|
|
155
|
+
parseErrInfo: field(raw, 'parseErrInfo', 'parse_err_info') ?? null,
|
|
156
|
+
summaryState: field(raw, 'summaryState', 'summary_state') ?? null,
|
|
157
|
+
coverUrls: Array.isArray(field(raw, 'coverUrls', 'cover_urls'))
|
|
158
|
+
? field(raw, 'coverUrls', 'cover_urls') : [],
|
|
159
|
+
logo: field(raw, 'logo', 'logo') ?? null,
|
|
103
160
|
};
|
|
104
161
|
}
|
|
105
162
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readKnowledgeBaseFromApi } from './native-api.js';
|
|
1
|
+
import { listKnowledgeBases, readKnowledgeBaseFromApi } from './native-api.js';
|
|
2
2
|
|
|
3
3
|
const IMA_WIKIS_URL = 'https://ima.qq.com/wikis';
|
|
4
4
|
|
|
@@ -33,7 +33,7 @@ async function triggerImaAuthRequest(page, query) {
|
|
|
33
33
|
}, query);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
async function acquireImaChromeAuth(page, query, dependencies = {}) {
|
|
37
37
|
if (!page || typeof page.startImaAuthCapture !== 'function'
|
|
38
38
|
|| typeof page.readImaAuth !== 'function' || typeof page.requestImaReader !== 'function'
|
|
39
39
|
|| typeof page.evaluate !== 'function') {
|
|
@@ -44,12 +44,11 @@ export async function readKnowledgeBaseFromChrome(page, query, dependencies = {}
|
|
|
44
44
|
}
|
|
45
45
|
const timeoutMs = dependencies.timeoutMs ?? 30_000;
|
|
46
46
|
const sleep = dependencies.sleep ?? wait;
|
|
47
|
-
let authId;
|
|
48
47
|
try {
|
|
49
48
|
await page.startImaAuthCapture();
|
|
50
49
|
await page.goto(IMA_WIKIS_URL);
|
|
51
50
|
await triggerImaAuthRequest(page, query);
|
|
52
|
-
|
|
51
|
+
return await waitForImaAuth(page, timeoutMs, sleep);
|
|
53
52
|
} catch (error) {
|
|
54
53
|
if (error?.code === 'IMA_CHROME_AUTH_REQUIRED') throw error;
|
|
55
54
|
throw codedError(
|
|
@@ -57,14 +56,31 @@ export async function readKnowledgeBaseFromChrome(page, query, dependencies = {}
|
|
|
57
56
|
`Chrome Browser Bridge could not acquire ima reader authentication: ${error instanceof Error ? error.message : String(error)}`,
|
|
58
57
|
);
|
|
59
58
|
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function releaseImaAuth(page, authId) {
|
|
62
|
+
if (typeof page.releaseImaAuth === 'function') {
|
|
63
|
+
await page.releaseImaAuth(authId).catch(() => {});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function readKnowledgeBasesFromChrome(page, dependencies = {}) {
|
|
68
|
+
const authId = await acquireImaChromeAuth(page, undefined, dependencies);
|
|
69
|
+
try {
|
|
70
|
+
return await listKnowledgeBases((path, body) => page.requestImaReader(authId, path, body));
|
|
71
|
+
} finally {
|
|
72
|
+
await releaseImaAuth(page, authId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function readKnowledgeBaseFromChrome(page, query, dependencies = {}) {
|
|
77
|
+
const authId = await acquireImaChromeAuth(page, query, dependencies);
|
|
60
78
|
try {
|
|
61
79
|
return await readKnowledgeBaseFromApi(
|
|
62
80
|
query,
|
|
63
81
|
(path, body) => page.requestImaReader(authId, path, body),
|
|
64
82
|
);
|
|
65
83
|
} finally {
|
|
66
|
-
|
|
67
|
-
await page.releaseImaAuth(authId).catch(() => {});
|
|
68
|
-
}
|
|
84
|
+
await releaseImaAuth(page, authId);
|
|
69
85
|
}
|
|
70
86
|
}
|
package/clis/ima/utils.js
CHANGED
|
@@ -37,6 +37,7 @@ export function normalizeArticleUrl(value) {
|
|
|
37
37
|
|
|
38
38
|
export function toKnowledgeRow(item) {
|
|
39
39
|
return {
|
|
40
|
+
...item,
|
|
40
41
|
knowledgeBaseId: item.knowledgeBaseId || null,
|
|
41
42
|
knowledgeBase: item.knowledgeBase,
|
|
42
43
|
folderPath: Array.isArray(item.folderPath) ? item.folderPath : [],
|