feishu-docs-cli 0.1.0-beta.8 → 1.0.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 +70 -13
- package/README.zh.md +53 -13
- package/dist/auth.js +31 -17
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +16 -4
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +4 -2
- package/dist/client.js +177 -61
- package/dist/client.js.map +1 -1
- package/dist/commands/authorize.d.ts +3 -0
- package/dist/commands/authorize.js +16 -34
- package/dist/commands/authorize.js.map +1 -1
- package/dist/commands/cp.d.ts +9 -0
- package/dist/commands/cp.js +70 -0
- package/dist/commands/cp.js.map +1 -0
- package/dist/commands/create.js +21 -7
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/delete.js +53 -54
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/login.js +6 -2
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/ls.js +38 -38
- package/dist/commands/ls.js.map +1 -1
- package/dist/commands/mkdir.d.ts +6 -0
- package/dist/commands/mkdir.js +49 -0
- package/dist/commands/mkdir.js.map +1 -0
- package/dist/commands/mv.d.ts +9 -0
- package/dist/commands/mv.js +72 -0
- package/dist/commands/mv.js.map +1 -0
- package/dist/commands/read.d.ts +1 -1
- package/dist/commands/read.js +17 -354
- package/dist/commands/read.js.map +1 -1
- package/dist/commands/search.js +57 -55
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/share.d.ts +1 -1
- package/dist/commands/share.js +164 -91
- package/dist/commands/share.js.map +1 -1
- package/dist/commands/update.js +43 -60
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/wiki.js +8 -20
- package/dist/commands/wiki.js.map +1 -1
- package/dist/parser/block-types.d.ts +0 -1
- package/dist/parser/block-types.js +0 -22
- package/dist/parser/block-types.js.map +1 -1
- package/dist/parser/blocks-to-md.d.ts +10 -18
- package/dist/parser/blocks-to-md.js +341 -450
- package/dist/parser/blocks-to-md.js.map +1 -1
- package/dist/scopes.d.ts +5 -47
- package/dist/scopes.js +9 -54
- package/dist/scopes.js.map +1 -1
- package/dist/services/block-writer.d.ts +3 -2
- package/dist/services/block-writer.js +29 -13
- package/dist/services/block-writer.js.map +1 -1
- package/dist/services/doc-blocks.d.ts +1 -1
- package/dist/services/doc-blocks.js +1 -1
- package/dist/services/doc-blocks.js.map +1 -1
- package/dist/services/doc-enrichment.d.ts +64 -0
- package/dist/services/doc-enrichment.js +397 -0
- package/dist/services/doc-enrichment.js.map +1 -0
- package/dist/services/image-download.d.ts +31 -0
- package/dist/services/image-download.js +127 -0
- package/dist/services/image-download.js.map +1 -0
- package/dist/services/markdown-convert.d.ts +20 -0
- package/dist/services/markdown-convert.js +55 -1
- package/dist/services/markdown-convert.js.map +1 -1
- package/dist/services/wiki-nodes.d.ts +1 -1
- package/dist/services/wiki-nodes.js +2 -3
- package/dist/services/wiki-nodes.js.map +1 -1
- package/dist/types/api-responses.d.ts +34 -0
- package/dist/types/api-responses.js +8 -0
- package/dist/types/api-responses.js.map +1 -0
- package/dist/types/index.d.ts +9 -17
- package/dist/utils/concurrency.d.ts +12 -0
- package/dist/utils/concurrency.js +37 -0
- package/dist/utils/concurrency.js.map +1 -0
- package/dist/utils/errors.d.ts +3 -1
- package/dist/utils/errors.js +11 -7
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/retry.d.ts +49 -0
- package/dist/utils/retry.js +70 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/scope-prompt.d.ts +23 -18
- package/dist/utils/scope-prompt.js +62 -51
- package/dist/utils/scope-prompt.js.map +1 -1
- package/package.json +3 -1
- package/skills/feishu-docs/SKILL.md +37 -2
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document enrichment service.
|
|
3
|
+
*
|
|
4
|
+
* Extracts and resolves embedded content from document blocks:
|
|
5
|
+
* images, bitable tables, spreadsheets, whiteboards, and @mentions.
|
|
6
|
+
* All enrichment runs in parallel with a configurable concurrency limit.
|
|
7
|
+
*/
|
|
8
|
+
import { createClient, fetchWithAuth, fetchBinaryWithAuth, getTenantToken, } from "../client.js";
|
|
9
|
+
import { writeFile, mkdir } from "node:fs/promises";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { BlockType } from "../parser/block-types.js";
|
|
13
|
+
import { CliError } from "../utils/errors.js";
|
|
14
|
+
import { withScopeRecovery } from "../utils/scope-prompt.js";
|
|
15
|
+
import { downloadImages } from "./image-download.js";
|
|
16
|
+
import { pLimit } from "../utils/concurrency.js";
|
|
17
|
+
// ── Constants ──
|
|
18
|
+
const TMP_URL_BATCH_SIZE = 5;
|
|
19
|
+
// ── Token extraction helpers (internal) ──
|
|
20
|
+
function extractFileTokens(blocks) {
|
|
21
|
+
const tokens = [];
|
|
22
|
+
for (const block of blocks) {
|
|
23
|
+
if (block.block_type === BlockType.IMAGE && block.image?.token) {
|
|
24
|
+
tokens.push(block.image.token);
|
|
25
|
+
}
|
|
26
|
+
if (block.block_type === BlockType.FILE && block.file?.token) {
|
|
27
|
+
tokens.push(block.file.token);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return tokens;
|
|
31
|
+
}
|
|
32
|
+
function extractBitableTokens(blocks) {
|
|
33
|
+
const tokens = [];
|
|
34
|
+
for (const block of blocks) {
|
|
35
|
+
if (block.block_type === BlockType.BITABLE && block.bitable?.token) {
|
|
36
|
+
tokens.push(block.bitable.token);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return tokens;
|
|
40
|
+
}
|
|
41
|
+
function extractSheetTokens(blocks) {
|
|
42
|
+
const tokens = [];
|
|
43
|
+
for (const block of blocks) {
|
|
44
|
+
if (block.block_type === BlockType.SHEET && block.sheet?.token) {
|
|
45
|
+
tokens.push(block.sheet.token);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return tokens;
|
|
49
|
+
}
|
|
50
|
+
function extractBoardTokens(blocks) {
|
|
51
|
+
const tokens = [];
|
|
52
|
+
for (const block of blocks) {
|
|
53
|
+
if (block.block_type === BlockType.BOARD &&
|
|
54
|
+
block.board?.token) {
|
|
55
|
+
tokens.push(block.board.token);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return tokens;
|
|
59
|
+
}
|
|
60
|
+
function extractMentionUserIds(blocks) {
|
|
61
|
+
const ids = new Set();
|
|
62
|
+
for (const block of blocks) {
|
|
63
|
+
const elementSources = [
|
|
64
|
+
block.text?.elements,
|
|
65
|
+
block.heading1?.elements,
|
|
66
|
+
block.heading2?.elements,
|
|
67
|
+
block.heading3?.elements,
|
|
68
|
+
block.heading4?.elements,
|
|
69
|
+
block.heading5?.elements,
|
|
70
|
+
block.heading6?.elements,
|
|
71
|
+
block.heading7?.elements,
|
|
72
|
+
block.heading8?.elements,
|
|
73
|
+
block.heading9?.elements,
|
|
74
|
+
block.bullet?.elements,
|
|
75
|
+
block.ordered?.elements,
|
|
76
|
+
block.todo?.elements,
|
|
77
|
+
block.quote?.elements,
|
|
78
|
+
];
|
|
79
|
+
for (const elements of elementSources) {
|
|
80
|
+
if (!elements)
|
|
81
|
+
continue;
|
|
82
|
+
for (const el of elements) {
|
|
83
|
+
if (el.mention_user?.user_id) {
|
|
84
|
+
ids.add(el.mention_user.user_id);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return [...ids];
|
|
90
|
+
}
|
|
91
|
+
// ── Data fetching functions (exported for testing) ──
|
|
92
|
+
/**
|
|
93
|
+
* Batch get temporary download URLs for file tokens (images, files).
|
|
94
|
+
* The API accepts at most 5 file_tokens per request, so larger sets
|
|
95
|
+
* are split into chunks automatically.
|
|
96
|
+
*/
|
|
97
|
+
async function batchGetTmpUrls(authInfo, fileTokens) {
|
|
98
|
+
if (fileTokens.length === 0)
|
|
99
|
+
return new Map();
|
|
100
|
+
const urlMap = new Map();
|
|
101
|
+
for (let i = 0; i < fileTokens.length; i += TMP_URL_BATCH_SIZE) {
|
|
102
|
+
const chunk = fileTokens.slice(i, i + TMP_URL_BATCH_SIZE);
|
|
103
|
+
const res = await fetchWithAuth(authInfo, "/open-apis/drive/v1/medias/batch_get_tmp_download_url", { params: { file_tokens: chunk } });
|
|
104
|
+
const data = res?.data;
|
|
105
|
+
const items = (data?.tmp_download_urls || []);
|
|
106
|
+
for (const item of items) {
|
|
107
|
+
urlMap.set(item.file_token, item.tmp_download_url);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return urlMap;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Fetch bitable fields and records, return as renderable data.
|
|
114
|
+
*/
|
|
115
|
+
export async function fetchBitableData(authInfo, fullToken) {
|
|
116
|
+
const idx = fullToken.lastIndexOf("_tbl");
|
|
117
|
+
if (idx === -1) {
|
|
118
|
+
process.stderr.write(`feishu-docs: warning: 多维表格 token 格式无法解析: ${fullToken}\n`);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const appToken = fullToken.slice(0, idx);
|
|
122
|
+
const tableId = fullToken.slice(idx + 1);
|
|
123
|
+
const [fieldsRes, recordsRes] = await Promise.all([
|
|
124
|
+
fetchWithAuth(authInfo, `/open-apis/bitable/v1/apps/${encodeURIComponent(appToken)}/tables/${encodeURIComponent(tableId)}/fields`, {}),
|
|
125
|
+
fetchWithAuth(authInfo, `/open-apis/bitable/v1/apps/${encodeURIComponent(appToken)}/tables/${encodeURIComponent(tableId)}/records`, { params: { page_size: 100 } }),
|
|
126
|
+
]);
|
|
127
|
+
const fieldsData = fieldsRes.data;
|
|
128
|
+
const recordsData = recordsRes.data;
|
|
129
|
+
const fields = (fieldsData?.items || []).map((f) => f.field_name);
|
|
130
|
+
const records = (recordsData?.items || []).map((r) => {
|
|
131
|
+
return fields.map((name) => {
|
|
132
|
+
const val = r.fields?.[name];
|
|
133
|
+
if (val === undefined || val === null)
|
|
134
|
+
return "";
|
|
135
|
+
if (typeof val === "object")
|
|
136
|
+
return JSON.stringify(val);
|
|
137
|
+
return String(val);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
return { fields, records };
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Fetch sheet metadata and cell values, return as renderable data.
|
|
144
|
+
*/
|
|
145
|
+
export async function fetchSheetData(authInfo, sheetToken) {
|
|
146
|
+
// Sheet tokens embedded in docs have format: {spreadsheetToken}_{sheetId}
|
|
147
|
+
// The sheets API needs just the spreadsheet token; sheetId selects the tab.
|
|
148
|
+
const underscoreIdx = sheetToken.lastIndexOf("_");
|
|
149
|
+
const spreadsheetToken = underscoreIdx > 0 ? sheetToken.slice(0, underscoreIdx) : sheetToken;
|
|
150
|
+
const embeddedSheetId = underscoreIdx > 0 ? sheetToken.slice(underscoreIdx + 1) : undefined;
|
|
151
|
+
const metaRes = await fetchWithAuth(authInfo, `/open-apis/sheets/v2/spreadsheets/${encodeURIComponent(spreadsheetToken)}/metainfo`, {});
|
|
152
|
+
const metaData = metaRes.data;
|
|
153
|
+
const sheets = (metaData?.sheets || []);
|
|
154
|
+
if (sheets.length === 0)
|
|
155
|
+
return null;
|
|
156
|
+
// Prefer the embedded sheet id; fall back to first sheet
|
|
157
|
+
// Note: metainfo API returns camelCase field names (sheetId, not sheet_id)
|
|
158
|
+
const targetSheet = embeddedSheetId
|
|
159
|
+
? sheets.find((s) => s.sheetId === embeddedSheetId) || sheets[0]
|
|
160
|
+
: sheets[0];
|
|
161
|
+
const sheetId = targetSheet.sheetId;
|
|
162
|
+
// Suppress title when it equals sheetId (default meaningless title)
|
|
163
|
+
const rawTitle = targetSheet.title || "";
|
|
164
|
+
const title = rawTitle === sheetId ? "" : rawTitle;
|
|
165
|
+
const valuesRes = await fetchWithAuth(authInfo, `/open-apis/sheets/v2/spreadsheets/${encodeURIComponent(spreadsheetToken)}/values/${encodeURIComponent(sheetId)}`, { params: { valueRenderOption: "ToString" } });
|
|
166
|
+
const valuesData = valuesRes.data;
|
|
167
|
+
const rows = (valuesData?.valueRange?.values ||
|
|
168
|
+
[]);
|
|
169
|
+
if (rows.length === 0)
|
|
170
|
+
return null;
|
|
171
|
+
const maxRows = 101; // first row = header + 100 data rows
|
|
172
|
+
const limitedRows = rows.length > maxRows ? rows.slice(0, maxRows) : rows;
|
|
173
|
+
const fields = limitedRows[0].map((cell) => String(cell ?? ""));
|
|
174
|
+
const records = limitedRows
|
|
175
|
+
.slice(1)
|
|
176
|
+
.map((row) => fields.map((_, i) => String(row[i] ?? "")));
|
|
177
|
+
return { fields, records, title, truncated: rows.length > maxRows };
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Download board/whiteboard as image and save to temp file.
|
|
181
|
+
* Returns the local file path, or null on failure.
|
|
182
|
+
*/
|
|
183
|
+
export async function fetchBoardImage(authInfo, boardToken) {
|
|
184
|
+
const buf = await fetchBinaryWithAuth(authInfo, `/open-apis/board/v1/whiteboards/${encodeURIComponent(boardToken)}/download_as_image`);
|
|
185
|
+
if (buf.byteLength === 0)
|
|
186
|
+
return null;
|
|
187
|
+
const dir = join(tmpdir(), "feishu-docs");
|
|
188
|
+
await mkdir(dir, { recursive: true });
|
|
189
|
+
const filePath = join(dir, `board-${boardToken}.png`);
|
|
190
|
+
await writeFile(filePath, Buffer.from(buf));
|
|
191
|
+
return filePath;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Batch resolve user IDs to display names.
|
|
195
|
+
* Uses tenant_access_token + contact:user.base:readonly to get name fields.
|
|
196
|
+
*/
|
|
197
|
+
export async function resolveUserNames(authInfo, userIds) {
|
|
198
|
+
const nameMap = new Map();
|
|
199
|
+
// Use tenant token for contact API (contact:user.base:readonly is app-identity permission)
|
|
200
|
+
let tenantToken;
|
|
201
|
+
try {
|
|
202
|
+
tenantToken = await getTenantToken(authInfo);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Fallback: try authen API with user token for current user
|
|
206
|
+
try {
|
|
207
|
+
const self = await fetchWithAuth(authInfo, "/open-apis/authen/v1/user_info", {});
|
|
208
|
+
const selfData = self?.data;
|
|
209
|
+
if (selfData?.open_id && selfData?.name) {
|
|
210
|
+
nameMap.set(selfData.open_id, selfData.name);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// no way to resolve
|
|
215
|
+
}
|
|
216
|
+
return nameMap;
|
|
217
|
+
}
|
|
218
|
+
// Batch query users (up to 50 per request)
|
|
219
|
+
const BATCH = 50;
|
|
220
|
+
for (let i = 0; i < userIds.length; i += BATCH) {
|
|
221
|
+
const batch = userIds.slice(i, i + BATCH);
|
|
222
|
+
try {
|
|
223
|
+
const params = {
|
|
224
|
+
user_id_type: "open_id",
|
|
225
|
+
user_ids: batch,
|
|
226
|
+
};
|
|
227
|
+
const tenantAuthInfo = {
|
|
228
|
+
...authInfo,
|
|
229
|
+
mode: "tenant",
|
|
230
|
+
tenantToken,
|
|
231
|
+
};
|
|
232
|
+
const res = await fetchWithAuth(tenantAuthInfo, "/open-apis/contact/v3/users/batch", { params });
|
|
233
|
+
const items = (res?.data?.user_list ||
|
|
234
|
+
[]);
|
|
235
|
+
for (const user of items) {
|
|
236
|
+
if (user.open_id && user.name) {
|
|
237
|
+
nameMap.set(user.open_id, user.name);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// skip batch errors
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return nameMap;
|
|
246
|
+
}
|
|
247
|
+
// ── Resolve image URLs (images + download) ──
|
|
248
|
+
async function resolveImageUrls(authInfo, blocks, globalOpts) {
|
|
249
|
+
const fileTokens = extractFileTokens(blocks);
|
|
250
|
+
if (fileTokens.length === 0)
|
|
251
|
+
return new Map();
|
|
252
|
+
const tmpUrlMap = await withScopeRecovery(async () => {
|
|
253
|
+
const { authInfo: freshAuth } = await createClient(globalOpts);
|
|
254
|
+
return batchGetTmpUrls(freshAuth, fileTokens);
|
|
255
|
+
}, globalOpts, ["drive:drive"]);
|
|
256
|
+
return downloadImages(tmpUrlMap);
|
|
257
|
+
}
|
|
258
|
+
// ── Main entry point ──
|
|
259
|
+
/**
|
|
260
|
+
* Enrich document blocks with embedded content data.
|
|
261
|
+
*
|
|
262
|
+
* Resolves images, bitable tables, spreadsheets, board images,
|
|
263
|
+
* and @mention user names in parallel with concurrency control.
|
|
264
|
+
*
|
|
265
|
+
* @param authInfo - Authentication context
|
|
266
|
+
* @param blocks - Document blocks to enrich
|
|
267
|
+
* @param globalOpts - Global CLI options
|
|
268
|
+
* @param options - Control which enrichments to run and concurrency
|
|
269
|
+
* @returns Enrichment maps for blocksToMarkdown rendering
|
|
270
|
+
*/
|
|
271
|
+
export async function enrichBlocks(authInfo, blocks, globalOpts, options = {}) {
|
|
272
|
+
const opts = {
|
|
273
|
+
images: options.images ?? true,
|
|
274
|
+
bitable: options.bitable ?? true,
|
|
275
|
+
sheet: options.sheet ?? true,
|
|
276
|
+
board: options.board ?? true,
|
|
277
|
+
mentions: options.mentions ?? true,
|
|
278
|
+
concurrency: options.concurrency ?? 5,
|
|
279
|
+
};
|
|
280
|
+
const limit = pLimit(opts.concurrency);
|
|
281
|
+
const result = {
|
|
282
|
+
imageUrlMap: new Map(),
|
|
283
|
+
userNameMap: new Map(),
|
|
284
|
+
bitableDataMap: new Map(),
|
|
285
|
+
boardImageMap: new Map(),
|
|
286
|
+
sheetDataMap: new Map(),
|
|
287
|
+
};
|
|
288
|
+
const tasks = [];
|
|
289
|
+
// Image enrichment
|
|
290
|
+
if (opts.images) {
|
|
291
|
+
const fileTokens = extractFileTokens(blocks);
|
|
292
|
+
if (fileTokens.length > 0) {
|
|
293
|
+
tasks.push(limit(async () => {
|
|
294
|
+
try {
|
|
295
|
+
const map = await resolveImageUrls(authInfo, blocks, globalOpts);
|
|
296
|
+
for (const [k, v] of map)
|
|
297
|
+
result.imageUrlMap.set(k, v);
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
process.stderr.write("feishu-docs: warning: 获取图片/文件链接失败,链接将为空\n");
|
|
301
|
+
}
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// Mention enrichment
|
|
306
|
+
if (opts.mentions) {
|
|
307
|
+
const mentionUserIds = extractMentionUserIds(blocks);
|
|
308
|
+
if (mentionUserIds.length > 0) {
|
|
309
|
+
tasks.push(limit(async () => {
|
|
310
|
+
try {
|
|
311
|
+
const map = await resolveUserNames(authInfo, mentionUserIds);
|
|
312
|
+
for (const [k, v] of map)
|
|
313
|
+
result.userNameMap.set(k, v);
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
process.stderr.write("feishu-docs: warning: 解析 @用户 名称失败\n");
|
|
317
|
+
}
|
|
318
|
+
}));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// Bitable enrichment
|
|
322
|
+
if (opts.bitable) {
|
|
323
|
+
const bitableTokens = extractBitableTokens(blocks);
|
|
324
|
+
for (const token of bitableTokens) {
|
|
325
|
+
tasks.push(limit(async () => {
|
|
326
|
+
try {
|
|
327
|
+
const data = await fetchBitableData(authInfo, token);
|
|
328
|
+
if (data)
|
|
329
|
+
result.bitableDataMap.set(token, data);
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
if (err instanceof CliError &&
|
|
333
|
+
(err.errorType === "PERMISSION_DENIED" ||
|
|
334
|
+
err.errorType === "SCOPE_MISSING")) {
|
|
335
|
+
process.stderr.write(`feishu-docs: warning: 获取多维表格数据权限不足: ${token}\n` +
|
|
336
|
+
' 请在飞书开发者后台开通权限后运行 feishu-docs authorize --scope "bitable:app:readonly"\n');
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
process.stderr.write(`feishu-docs: warning: 获取多维表格数据失败: ${token}\n`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}));
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Board enrichment
|
|
346
|
+
if (opts.board) {
|
|
347
|
+
const boardTokens = extractBoardTokens(blocks);
|
|
348
|
+
for (const token of boardTokens) {
|
|
349
|
+
tasks.push(limit(async () => {
|
|
350
|
+
try {
|
|
351
|
+
const filePath = await fetchBoardImage(authInfo, token);
|
|
352
|
+
if (filePath)
|
|
353
|
+
result.boardImageMap.set(token, filePath);
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
if (err instanceof CliError &&
|
|
357
|
+
(err.errorType === "PERMISSION_DENIED" ||
|
|
358
|
+
err.errorType === "SCOPE_MISSING")) {
|
|
359
|
+
process.stderr.write(`feishu-docs: warning: 获取画板图片权限不足: ${token}\n` +
|
|
360
|
+
' 请在飞书开发者后台开通权限后运行 feishu-docs authorize --scope "board:whiteboard:node:read"\n');
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
process.stderr.write(`feishu-docs: warning: 获取画板图片失败: ${token}\n`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// Sheet enrichment
|
|
370
|
+
if (opts.sheet) {
|
|
371
|
+
const sheetTokens = extractSheetTokens(blocks);
|
|
372
|
+
for (const token of sheetTokens) {
|
|
373
|
+
tasks.push(limit(async () => {
|
|
374
|
+
try {
|
|
375
|
+
const data = await fetchSheetData(authInfo, token);
|
|
376
|
+
if (data)
|
|
377
|
+
result.sheetDataMap.set(token, data);
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
if (err instanceof CliError &&
|
|
381
|
+
(err.errorType === "PERMISSION_DENIED" ||
|
|
382
|
+
err.errorType === "SCOPE_MISSING")) {
|
|
383
|
+
process.stderr.write(`feishu-docs: warning: 获取电子表格数据权限不足: ${token}\n` +
|
|
384
|
+
' 请在飞书开发者后台开通权限后运行 feishu-docs authorize --scope "sheets:spreadsheet:readonly"\n');
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
388
|
+
process.stderr.write(`feishu-docs: warning: 获取电子表格数据失败: ${token} (${msg})\n`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
await Promise.allSettled(tasks);
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
//# sourceMappingURL=doc-enrichment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-enrichment.js","sourceRoot":"","sources":["../../src/services/doc-enrichment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Download document images to local persistent storage.
|
|
3
|
+
*
|
|
4
|
+
* Images are saved to ~/.feishu-docs/images/{fileToken}.{ext} and served
|
|
5
|
+
* via local file paths in Markdown output. Already-downloaded images are
|
|
6
|
+
* skipped (disk cache by file_token).
|
|
7
|
+
*/
|
|
8
|
+
export declare const IMAGES_DIR: string;
|
|
9
|
+
export declare const CONTENT_TYPE_EXT: Record<string, string>;
|
|
10
|
+
/** Maximum age for cached images (30 days in milliseconds). */
|
|
11
|
+
export declare const IMAGE_TTL_MS: number;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve content-type to file extension. Defaults to ".png".
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveExtension(contentType: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Find a cached image file for the given token (any known extension).
|
|
18
|
+
* Returns the file path if found, or null.
|
|
19
|
+
*/
|
|
20
|
+
export declare function findCachedImage(fileToken: string, dir?: string): Promise<string | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Remove cached image files older than IMAGE_TTL_MS.
|
|
23
|
+
* Errors are caught and logged to stderr -- never throws.
|
|
24
|
+
*/
|
|
25
|
+
export declare function cleanExpiredImages(dir: string): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Download images from temporary URLs and save to local directory.
|
|
28
|
+
* Returns a map of file_token → local file path. Already-downloaded images
|
|
29
|
+
* are skipped (simple disk cache by file_token).
|
|
30
|
+
*/
|
|
31
|
+
export declare function downloadImages(tmpUrlMap: Map<string, string>, dir?: string): Promise<Map<string, string>>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Download document images to local persistent storage.
|
|
3
|
+
*
|
|
4
|
+
* Images are saved to ~/.feishu-docs/images/{fileToken}.{ext} and served
|
|
5
|
+
* via local file paths in Markdown output. Already-downloaded images are
|
|
6
|
+
* skipped (disk cache by file_token).
|
|
7
|
+
*/
|
|
8
|
+
import { writeFile, mkdir, stat, readdir, unlink } from "node:fs/promises";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { validateToken } from "../utils/validate.js";
|
|
12
|
+
export const IMAGES_DIR = join(homedir(), ".feishu-docs", "images");
|
|
13
|
+
export const CONTENT_TYPE_EXT = {
|
|
14
|
+
"image/png": ".png",
|
|
15
|
+
"image/jpeg": ".jpg",
|
|
16
|
+
"image/gif": ".gif",
|
|
17
|
+
"image/webp": ".webp",
|
|
18
|
+
"image/svg+xml": ".svg",
|
|
19
|
+
};
|
|
20
|
+
/** Maximum age for cached images (30 days in milliseconds). */
|
|
21
|
+
export const IMAGE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
22
|
+
const KNOWN_EXTENSIONS = Object.values(CONTENT_TYPE_EXT);
|
|
23
|
+
/**
|
|
24
|
+
* Check if a file already exists.
|
|
25
|
+
*/
|
|
26
|
+
async function fileExists(path) {
|
|
27
|
+
try {
|
|
28
|
+
await stat(path);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve content-type to file extension. Defaults to ".png".
|
|
37
|
+
*/
|
|
38
|
+
export function resolveExtension(contentType) {
|
|
39
|
+
return CONTENT_TYPE_EXT[contentType.split(";")[0].trim()] || ".png";
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Find a cached image file for the given token (any known extension).
|
|
43
|
+
* Returns the file path if found, or null.
|
|
44
|
+
*/
|
|
45
|
+
export async function findCachedImage(fileToken, dir = IMAGES_DIR) {
|
|
46
|
+
for (const ext of KNOWN_EXTENSIONS) {
|
|
47
|
+
const p = join(dir, `${fileToken}${ext}`);
|
|
48
|
+
if (await fileExists(p))
|
|
49
|
+
return p;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Remove cached image files older than IMAGE_TTL_MS.
|
|
55
|
+
* Errors are caught and logged to stderr -- never throws.
|
|
56
|
+
*/
|
|
57
|
+
export async function cleanExpiredImages(dir) {
|
|
58
|
+
try {
|
|
59
|
+
const files = await readdir(dir);
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
let cleaned = 0;
|
|
62
|
+
for (const file of files) {
|
|
63
|
+
try {
|
|
64
|
+
const filePath = join(dir, file);
|
|
65
|
+
const fileStat = await stat(filePath);
|
|
66
|
+
if (now - fileStat.mtimeMs > IMAGE_TTL_MS) {
|
|
67
|
+
await unlink(filePath);
|
|
68
|
+
cleaned++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Individual file cleanup failure -- skip silently
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (cleaned > 0) {
|
|
76
|
+
process.stderr.write(`feishu-docs: info: 已清理 ${cleaned} 个过期图片缓存\n`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
process.stderr.write("feishu-docs: warning: 图片缓存清理失败\n");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Download images from temporary URLs and save to local directory.
|
|
85
|
+
* Returns a map of file_token → local file path. Already-downloaded images
|
|
86
|
+
* are skipped (simple disk cache by file_token).
|
|
87
|
+
*/
|
|
88
|
+
export async function downloadImages(tmpUrlMap, dir = IMAGES_DIR) {
|
|
89
|
+
if (tmpUrlMap.size === 0)
|
|
90
|
+
return new Map();
|
|
91
|
+
await mkdir(dir, { recursive: true });
|
|
92
|
+
void cleanExpiredImages(dir);
|
|
93
|
+
const localMap = new Map();
|
|
94
|
+
for (const [fileToken, tmpUrl] of tmpUrlMap) {
|
|
95
|
+
// Validate token before using as filename to prevent path traversal
|
|
96
|
+
try {
|
|
97
|
+
validateToken(fileToken, "file_token");
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
// Check disk cache (any known extension)
|
|
103
|
+
const cached = await findCachedImage(fileToken, dir);
|
|
104
|
+
if (cached) {
|
|
105
|
+
localMap.set(fileToken, cached);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const res = await fetch(tmpUrl);
|
|
110
|
+
if (!res.ok)
|
|
111
|
+
continue;
|
|
112
|
+
const contentType = res.headers.get("content-type") || "";
|
|
113
|
+
const ext = resolveExtension(contentType);
|
|
114
|
+
const filePath = join(dir, `${fileToken}${ext}`);
|
|
115
|
+
const buf = await res.arrayBuffer();
|
|
116
|
+
if (buf.byteLength === 0)
|
|
117
|
+
continue;
|
|
118
|
+
await writeFile(filePath, Buffer.from(buf));
|
|
119
|
+
localMap.set(fileToken, filePath);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Download failed for this image — skip it
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return localMap;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=image-download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-download.js","sourceRoot":"","sources":["../../src/services/image-download.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;AAEpE,MAAM,CAAC,MAAM,gBAAgB,GAA2B;IACtD,WAAW,EAAE,MAAM;IACnB,YAAY,EAAE,MAAM;IACpB,WAAW,EAAE,MAAM;IACnB,YAAY,EAAE,OAAO;IACrB,eAAe,EAAE,MAAM;CACxB,CAAC;AAEF,+DAA+D;AAC/D,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAErD,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAEzD;;GAEG;AACH,KAAK,UAAU,UAAU,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,OAAO,gBAAgB,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,MAAM,CAAC;AACtE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAAiB,EACjB,MAAc,UAAU;IAExB,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,GAAG,EAAE,CAAC,CAAC;QAC1C,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAW;IAClD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,OAAO,GAAG,CAAC,CAAC;QAEhB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBACjC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACtC,IAAI,GAAG,GAAG,QAAQ,CAAC,OAAO,GAAG,YAAY,EAAE,CAAC;oBAC1C,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;oBACvB,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,mDAAmD;YACrD,CAAC;QACH,CAAC;QAED,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0BAA0B,OAAO,YAAY,CAC9C,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,SAA8B,EAC9B,MAAc,UAAU;IAExB,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,IAAI,GAAG,EAAE,CAAC;IAE3C,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,KAAK,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAE7B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,KAAK,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC5C,oEAAoE;QACpE,IAAI,CAAC;YACH,aAAa,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,yCAAyC;QACzC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACrD,IAAI,MAAM,EAAE,CAAC;YACX,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAChC,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,SAAS;YAEtB,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;YAC1D,MAAM,GAAG,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;YAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,GAAG,EAAE,CAAC,CAAC;YAEjD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,UAAU,KAAK,CAAC;gBAAE,SAAS;YAEnC,MAAM,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5C,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,2CAA2C;QAC7C,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -19,6 +19,26 @@ import { AuthInfo, ConvertedBlocks, Block } from "../types/index.js";
|
|
|
19
19
|
* Replaces unrecognized aliases with their recognized equivalents.
|
|
20
20
|
*/
|
|
21
21
|
export declare function normalizeLangNames(markdown: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Extract the first top-level heading (# title) from markdown.
|
|
24
|
+
*
|
|
25
|
+
* Returns the title text and the remaining body with the heading line removed.
|
|
26
|
+
* Only matches `# heading` (H1), not `## heading` (H2+).
|
|
27
|
+
* Ignores leading blank lines before the heading.
|
|
28
|
+
* If no H1 heading is found, returns null title and the original markdown.
|
|
29
|
+
*/
|
|
30
|
+
export declare function extractMarkdownTitle(markdown: string): {
|
|
31
|
+
title: string | null;
|
|
32
|
+
body: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Replace literal `\n` with `<br>` inside mermaid code blocks.
|
|
36
|
+
*
|
|
37
|
+
* Claude and other AI tools generate mermaid node labels with `\n` for
|
|
38
|
+
* line breaks (e.g. `A[Line 1\nLine 2]`), but standard mermaid syntax
|
|
39
|
+
* requires `<br>` (e.g. `A[Line 1<br>Line 2]`).
|
|
40
|
+
*/
|
|
41
|
+
export declare function normalizeMermaidLineBreaks(markdown: string): string;
|
|
22
42
|
/**
|
|
23
43
|
* Convert markdown string to Feishu block array via Convert API.
|
|
24
44
|
* Requires scope: docx:document.block:convert
|