deepfish-ai 1.0.28 → 2.0.2

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.
Files changed (62) hide show
  1. package/README.md +126 -368
  2. package/dist/index.js +23320 -0
  3. package/dist/serve/client/assets/index-DUHYC91l.js +1 -0
  4. package/dist/serve/client/assets/index-zcrdNLZs.css +1 -0
  5. package/dist/serve/client/index.html +14 -0
  6. package/dist/serve/pm2-server.js +21015 -0
  7. package/dist/serve/server/entry-server.mjs +224 -0
  8. package/package.json +50 -70
  9. package/LICENSE +0 -21
  10. package/README_CN.md +0 -460
  11. package/src/AgentRobot/AgentRobotFactory/MainAgentRobot.js +0 -88
  12. package/src/AgentRobot/AgentRobotFactory/SubAgentRobot.js +0 -45
  13. package/src/AgentRobot/AgentRobotFactory/SubSkillAgentRobot.js +0 -45
  14. package/src/AgentRobot/AgentRobotFactory/index.js +0 -48
  15. package/src/AgentRobot/BaseAgentRobot/Brain.js +0 -210
  16. package/src/AgentRobot/BaseAgentRobot/BrainEvent.js +0 -17
  17. package/src/AgentRobot/BaseAgentRobot/Hand.js +0 -119
  18. package/src/AgentRobot/BaseAgentRobot/Heart.js +0 -0
  19. package/src/AgentRobot/BaseAgentRobot/Logger.js +0 -114
  20. package/src/AgentRobot/BaseAgentRobot/ScreenPrinter.js +0 -49
  21. package/src/AgentRobot/BaseAgentRobot/index.js +0 -266
  22. package/src/AgentRobot/BaseAgentRobot/lazy-tools/AliBailian.js +0 -997
  23. package/src/AgentRobot/BaseAgentRobot/lazy-tools/doc-transform.js +0 -204
  24. package/src/AgentRobot/BaseAgentRobot/lazy-tools/docx.js +0 -1286
  25. package/src/AgentRobot/BaseAgentRobot/lazy-tools/img.js +0 -765
  26. package/src/AgentRobot/BaseAgentRobot/lazy-tools/pdf.js +0 -736
  27. package/src/AgentRobot/BaseAgentRobot/lazy-tools/pptx.js +0 -567
  28. package/src/AgentRobot/BaseAgentRobot/lazy-tools/video.js +0 -36
  29. package/src/AgentRobot/BaseAgentRobot/lazy-tools/xlsx.js +0 -611
  30. package/src/AgentRobot/BaseAgentRobot/tools/BaseTools.js +0 -13
  31. package/src/AgentRobot/BaseAgentRobot/tools/CreateAgentTools.js +0 -158
  32. package/src/AgentRobot/BaseAgentRobot/tools/FileTools.js +0 -768
  33. package/src/AgentRobot/BaseAgentRobot/tools/GenerateTools.js +0 -441
  34. package/src/AgentRobot/BaseAgentRobot/tools/InquirerTools.js +0 -244
  35. package/src/AgentRobot/BaseAgentRobot/tools/SystemTools.js +0 -263
  36. package/src/AgentRobot/BaseAgentRobot/tools/TaskTools.js +0 -211
  37. package/src/AgentRobot/BaseAgentRobot/tools/TestTools.js +0 -100
  38. package/src/AgentRobot/BaseAgentRobot/tools/UserTool.js +0 -87
  39. package/src/AgentRobot/BaseAgentRobot/tools/WebTools.js +0 -257
  40. package/src/AgentRobot/BaseAgentRobot/utils/AIRequest.js +0 -297
  41. package/src/AgentRobot/BaseAgentRobot/utils/AIToolManager.js +0 -131
  42. package/src/AgentRobot/BaseAgentRobot/utils/AgentTree.js +0 -66
  43. package/src/AgentRobot/BaseAgentRobot/utils/AttachmentToolScanner.js +0 -235
  44. package/src/AgentRobot/BaseAgentRobot/utils/MessageCompresser.js +0 -236
  45. package/src/AgentRobot/BaseAgentRobot/utils/aiConsole.js +0 -74
  46. package/src/AgentRobot/BaseAgentRobot/utils/aiInquirer.js +0 -78
  47. package/src/AgentRobot/BaseAgentRobot/utils/analyzeReturn.test.js +0 -104
  48. package/src/AgentRobot/BaseAgentRobot/utils/copilot.js +0 -117
  49. package/src/AgentRobot/BaseAgentRobot/utils/node-root.js +0 -138
  50. package/src/AgentRobot/BaseAgentRobot/utils/normal.js +0 -399
  51. package/src/cli/ConfigManager.js +0 -283
  52. package/src/cli/DefaultConfig.js +0 -98
  53. package/src/cli/GlobalVariable.js +0 -8
  54. package/src/cli/MemoryManager.js +0 -53
  55. package/src/cli/SkillConfigManager.js +0 -343
  56. package/src/cli/SkillParser.js +0 -58
  57. package/src/cli/ai-auth.js +0 -231
  58. package/src/cli/ai-config.js +0 -225
  59. package/src/cli/ai-memory.js +0 -22
  60. package/src/cli/ai-skill.js +0 -65
  61. package/src/cli/index.js +0 -77
  62. package/src/index.js +0 -54
@@ -1,736 +0,0 @@
1
- /**
2
- * @Author: Roman 306863030@qq.com
3
- * @Description: PDF 文档处理工具集(pdf-parse / pdf-lib / pdfkit)
4
- */
5
- const path = require('path')
6
- const fs = require('fs-extra')
7
- const pdfParse = require('pdf-parse')
8
- const { PDFDocument, degrees, rgb, StandardFonts } = require('pdf-lib')
9
- const PDFKit = require('pdfkit')
10
- const { pdf } = require('pdf-to-img')
11
- const sharp = require('sharp')
12
-
13
- // ─── 统一返回结构 ─────────────────────────────────────────────────────────────
14
-
15
- function ok(data = null) {
16
- return { success: true, data }
17
- }
18
-
19
- function fail(error, data = null) {
20
- return { success: false, error: error?.message || String(error), data }
21
- }
22
-
23
- function resolvePath(filePath) {
24
- return path.resolve(process.cwd(), filePath)
25
- }
26
-
27
- // ─── 工具函数 ─────────────────────────────────────────────────────────────────
28
-
29
- /**
30
- * 读取 PDF 中的全部文本内容
31
- */
32
- async function readPdfText(filePath) {
33
- try {
34
- const fullPath = resolvePath(filePath)
35
- if (!fs.existsSync(fullPath)) return fail(`File does not exist: ${fullPath}`, { filePath: fullPath })
36
- const dataBuffer = fs.readFileSync(fullPath)
37
- const data = await pdfParse(dataBuffer)
38
- return ok({
39
- filePath: fullPath,
40
- text: data.text,
41
- pageCount: data.numpages,
42
- wordCount: data.text.trim().split(/\s+/).filter(Boolean).length,
43
- charCount: data.text.length,
44
- })
45
- } catch (error) {
46
- return fail(error, { filePath })
47
- }
48
- }
49
-
50
- /**
51
- * 获取 PDF 文档元信息(页数、作者、标题、创建时间等)
52
- */
53
- async function getPdfInfo(filePath) {
54
- try {
55
- const fullPath = resolvePath(filePath)
56
- if (!fs.existsSync(fullPath)) return fail(`File does not exist: ${fullPath}`, { filePath: fullPath })
57
- const dataBuffer = fs.readFileSync(fullPath)
58
- const parseData = await pdfParse(dataBuffer)
59
- const pdfDoc = await PDFDocument.load(dataBuffer)
60
- const stat = fs.statSync(fullPath)
61
- return ok({
62
- filePath: fullPath,
63
- pageCount: parseData.numpages,
64
- size: stat.size,
65
- mtime: stat.mtime,
66
- title: pdfDoc.getTitle() || null,
67
- author: pdfDoc.getAuthor() || null,
68
- subject: pdfDoc.getSubject() || null,
69
- keywords: pdfDoc.getKeywords() || null,
70
- creator: pdfDoc.getCreator() || null,
71
- producer: pdfDoc.getProducer() || null,
72
- creationDate: pdfDoc.getCreationDate() || null,
73
- modificationDate: pdfDoc.getModificationDate() || null,
74
- pdfVersion: parseData.info?.PDFFormatVersion || null,
75
- })
76
- } catch (error) {
77
- return fail(error, { filePath })
78
- }
79
- }
80
-
81
- /**
82
- * 仅获取 PDF 总页数(轻量调用)
83
- */
84
- async function getPdfPageCount(filePath) {
85
- try {
86
- const fullPath = resolvePath(filePath)
87
- if (!fs.existsSync(fullPath)) return fail(`File does not exist: ${fullPath}`, { filePath: fullPath })
88
- const pdfDoc = await PDFDocument.load(fs.readFileSync(fullPath))
89
- return ok({ filePath: fullPath, pageCount: pdfDoc.getPageCount() })
90
- } catch (error) {
91
- return fail(error, { filePath })
92
- }
93
- }
94
-
95
- /**
96
- * 在 PDF 中搜索关键词,返回包含该词的行
97
- */
98
- async function searchPdfText(filePath, keyword) {
99
- try {
100
- const readResult = await readPdfText(filePath)
101
- if (!readResult.success) return readResult
102
- const lines = readResult.data.text.split('\n')
103
- const matches = lines
104
- .map((line, idx) => ({ line: idx + 1, text: line }))
105
- .filter(({ text }) => text.includes(keyword))
106
- return ok({
107
- filePath: resolvePath(filePath),
108
- keyword,
109
- matchCount: matches.length,
110
- matches,
111
- })
112
- } catch (error) {
113
- return fail(error, { filePath, keyword })
114
- }
115
- }
116
-
117
- /**
118
- * 合并多个 PDF 文件为一个
119
- */
120
- async function mergePdfs(inputPaths, outputPath) {
121
- try {
122
- const fullOutput = resolvePath(outputPath)
123
- fs.ensureDirSync(path.dirname(fullOutput))
124
- const mergedDoc = await PDFDocument.create()
125
- for (const inputPath of inputPaths) {
126
- const fullInput = resolvePath(inputPath)
127
- if (!fs.existsSync(fullInput)) return fail(`File does not exist: ${fullInput}`, { filePath: fullInput })
128
- const srcDoc = await PDFDocument.load(fs.readFileSync(fullInput))
129
- const pages = await mergedDoc.copyPages(srcDoc, srcDoc.getPageIndices())
130
- pages.forEach((p) => mergedDoc.addPage(p))
131
- }
132
- fs.writeFileSync(fullOutput, await mergedDoc.save())
133
- return ok({ outputPath: fullOutput, pageCount: mergedDoc.getPageCount(), inputCount: inputPaths.length })
134
- } catch (error) {
135
- return fail(error, { outputPath })
136
- }
137
- }
138
-
139
- /**
140
- * 将 PDF 按每页拆分为独立文件,保存到输出目录
141
- */
142
- async function splitPdf(filePath, outputDir) {
143
- try {
144
- const fullInput = resolvePath(filePath)
145
- const fullOutputDir = resolvePath(outputDir)
146
- if (!fs.existsSync(fullInput)) return fail(`File does not exist: ${fullInput}`, { filePath: fullInput })
147
- fs.ensureDirSync(fullOutputDir)
148
- const srcDoc = await PDFDocument.load(fs.readFileSync(fullInput))
149
- const pageCount = srcDoc.getPageCount()
150
- const baseName = path.basename(fullInput, path.extname(fullInput))
151
- const results = []
152
- for (let i = 0; i < pageCount; i++) {
153
- const newDoc = await PDFDocument.create()
154
- const [page] = await newDoc.copyPages(srcDoc, [i])
155
- newDoc.addPage(page)
156
- const outFile = path.join(fullOutputDir, `${baseName}_page${i + 1}.pdf`)
157
- fs.writeFileSync(outFile, await newDoc.save())
158
- results.push(outFile)
159
- }
160
- return ok({ filePath: fullInput, outputDir: fullOutputDir, pageCount, files: results })
161
- } catch (error) {
162
- return fail(error, { filePath, outputDir })
163
- }
164
- }
165
-
166
- /**
167
- * 从 PDF 中提取指定页码(1-based)生成新文件
168
- */
169
- async function extractPdfPages(filePath, outputPath, pages) {
170
- try {
171
- const fullInput = resolvePath(filePath)
172
- const fullOutput = resolvePath(outputPath)
173
- if (!fs.existsSync(fullInput)) return fail(`File does not exist: ${fullInput}`, { filePath: fullInput })
174
- fs.ensureDirSync(path.dirname(fullOutput))
175
- const srcDoc = await PDFDocument.load(fs.readFileSync(fullInput))
176
- const total = srcDoc.getPageCount()
177
- const indices = (pages || []).map((p) => p - 1).filter((i) => i >= 0 && i < total)
178
- if (indices.length === 0) return fail('No valid page numbers provided', { filePath: fullInput, pages })
179
- const newDoc = await PDFDocument.create()
180
- const copied = await newDoc.copyPages(srcDoc, indices)
181
- copied.forEach((p) => newDoc.addPage(p))
182
- fs.writeFileSync(fullOutput, await newDoc.save())
183
- return ok({ filePath: fullInput, outputPath: fullOutput, extractedPages: indices.map((i) => i + 1) })
184
- } catch (error) {
185
- return fail(error, { filePath, outputPath })
186
- }
187
- }
188
-
189
- /**
190
- * 旋转 PDF 页面(可指定特定页或全部页)
191
- */
192
- async function rotatePdfPages(filePath, outputPath, rotateDegrees, pageNumbers) {
193
- try {
194
- const fullInput = resolvePath(filePath)
195
- const fullOutput = resolvePath(outputPath)
196
- if (!fs.existsSync(fullInput)) return fail(`File does not exist: ${fullInput}`, { filePath: fullInput })
197
- fs.ensureDirSync(path.dirname(fullOutput))
198
- const pdfDoc = await PDFDocument.load(fs.readFileSync(fullInput))
199
- const deg = rotateDegrees || 90
200
- const total = pdfDoc.getPageCount()
201
- const targets = Array.isArray(pageNumbers) && pageNumbers.length > 0
202
- ? pageNumbers.map((p) => p - 1).filter((i) => i >= 0 && i < total)
203
- : Array.from({ length: total }, (_, i) => i)
204
- for (const idx of targets) {
205
- const page = pdfDoc.getPage(idx)
206
- page.setRotation(degrees((page.getRotation().angle + deg) % 360))
207
- }
208
- fs.writeFileSync(fullOutput, await pdfDoc.save())
209
- return ok({ filePath: fullInput, outputPath: fullOutput, degrees: deg, rotatedPages: targets.map((i) => i + 1) })
210
- } catch (error) {
211
- return fail(error, { filePath, outputPath })
212
- }
213
- }
214
-
215
- /**
216
- * 向 PDF 全部页面添加文字水印
217
- */
218
- async function addWatermarkText(filePath, outputPath, text, options = {}) {
219
- try {
220
- const fullInput = resolvePath(filePath)
221
- const fullOutput = resolvePath(outputPath)
222
- if (!fs.existsSync(fullInput)) return fail(`File does not exist: ${fullInput}`, { filePath: fullInput })
223
- fs.ensureDirSync(path.dirname(fullOutput))
224
- const pdfDoc = await PDFDocument.load(fs.readFileSync(fullInput))
225
- const font = await pdfDoc.embedFont(StandardFonts.Helvetica)
226
- const fontSize = options.fontSize || 48
227
- const opacity = options.opacity !== undefined ? options.opacity : 0.2
228
- const color = options.color
229
- ? rgb(options.color.r / 255, options.color.g / 255, options.color.b / 255)
230
- : rgb(0.5, 0.5, 0.5)
231
- const rotateAngle = options.angle !== undefined ? options.angle : 45
232
- const pages = pdfDoc.getPages()
233
- for (const page of pages) {
234
- const { width, height } = page.getSize()
235
- page.drawText(text, {
236
- x: width / 2 - (font.widthOfTextAtSize(text, fontSize) / 2),
237
- y: height / 2,
238
- size: fontSize,
239
- font,
240
- color,
241
- opacity,
242
- rotate: degrees(rotateAngle),
243
- })
244
- }
245
- fs.writeFileSync(fullOutput, await pdfDoc.save())
246
- return ok({ filePath: fullInput, outputPath: fullOutput, text, pageCount: pages.length })
247
- } catch (error) {
248
- return fail(error, { filePath, outputPath })
249
- }
250
- }
251
-
252
- /**
253
- * 创建新 PDF(支持文本段落、标题、分页)
254
- * content 为数组,每项:{ type: 'text'|'heading'|'pageBreak', text, fontSize, bold, color, align }
255
- */
256
- async function createPdf(outputPath, content = [], options = {}) {
257
- try {
258
- const fullOutput = resolvePath(outputPath)
259
- fs.ensureDirSync(path.dirname(fullOutput))
260
- return await new Promise((resolve, reject) => {
261
- const doc = new PDFKit({
262
- size: options.pageSize || 'A4',
263
- margin: options.margin || 50,
264
- info: {
265
- Title: options.title || '',
266
- Author: options.author || '',
267
- Subject: options.subject || '',
268
- },
269
- })
270
- const stream = fs.createWriteStream(fullOutput)
271
- doc.pipe(stream)
272
-
273
- for (const sec of content) {
274
- switch (sec.type) {
275
- case 'heading':
276
- doc.font('Helvetica-Bold').fontSize(sec.fontSize || 18)
277
- if (sec.color) doc.fillColor(sec.color)
278
- doc.text(sec.text || '', { align: sec.align || 'left' })
279
- doc.fillColor('#000000').font('Helvetica')
280
- doc.moveDown(0.5)
281
- break
282
- case 'pageBreak':
283
- doc.addPage()
284
- break
285
- case 'text':
286
- default:
287
- doc.font(sec.bold ? 'Helvetica-Bold' : 'Helvetica').fontSize(sec.fontSize || 12)
288
- if (sec.color) doc.fillColor(sec.color)
289
- doc.text(sec.text || '', { align: sec.align || 'left' })
290
- doc.fillColor('#000000').font('Helvetica')
291
- doc.moveDown(0.3)
292
- break
293
- }
294
- }
295
-
296
- doc.end()
297
- stream.on('finish', () => resolve(ok({ outputPath: fullOutput, itemCount: content.length })))
298
- stream.on('error', (err) => reject(err))
299
- })
300
- } catch (error) {
301
- return fail(error, { outputPath })
302
- }
303
- }
304
-
305
- /**
306
- * 将纯文本文件转换为 PDF
307
- */
308
- async function textToPdf(inputPath, outputPath, options = {}) {
309
- try {
310
- const fullInput = resolvePath(inputPath)
311
- if (!fs.existsSync(fullInput)) return fail(`File does not exist: ${fullInput}`, { filePath: fullInput })
312
- const text = fs.readFileSync(fullInput, 'utf8')
313
- const content = [{ type: 'text', text }]
314
- return await createPdf(outputPath, content, options)
315
- } catch (error) {
316
- return fail(error, { inputPath, outputPath })
317
- }
318
- }
319
-
320
- /**
321
- * 在 PDF 末尾追加空白页
322
- */
323
- async function appendBlankPages(filePath, outputPath, count) {
324
- try {
325
- const fullInput = resolvePath(filePath)
326
- const fullOutput = resolvePath(outputPath)
327
- if (!fs.existsSync(fullInput)) return fail(`File does not exist: ${fullInput}`, { filePath: fullInput })
328
- fs.ensureDirSync(path.dirname(fullOutput))
329
- const pdfDoc = await PDFDocument.load(fs.readFileSync(fullInput))
330
- const n = count || 1
331
- for (let i = 0; i < n; i++) pdfDoc.addPage()
332
- fs.writeFileSync(fullOutput, await pdfDoc.save())
333
- return ok({ filePath: fullInput, outputPath: fullOutput, addedPages: n, totalPages: pdfDoc.getPageCount() })
334
- } catch (error) {
335
- return fail(error, { filePath, outputPath })
336
- }
337
- }
338
-
339
- /**
340
- * 设置 PDF 文档元信息(标题、作者、主题、关键词)
341
- */
342
- async function setPdfMetadata(filePath, outputPath, metadata) {
343
- try {
344
- const fullInput = resolvePath(filePath)
345
- const fullOutput = resolvePath(outputPath)
346
- if (!fs.existsSync(fullInput)) return fail(`File does not exist: ${fullInput}`, { filePath: fullInput })
347
- fs.ensureDirSync(path.dirname(fullOutput))
348
- const pdfDoc = await PDFDocument.load(fs.readFileSync(fullInput))
349
- if (metadata.title !== undefined) pdfDoc.setTitle(metadata.title)
350
- if (metadata.author !== undefined) pdfDoc.setAuthor(metadata.author)
351
- if (metadata.subject !== undefined) pdfDoc.setSubject(metadata.subject)
352
- if (metadata.keywords !== undefined) pdfDoc.setKeywords(Array.isArray(metadata.keywords) ? metadata.keywords : [metadata.keywords])
353
- if (metadata.creator !== undefined) pdfDoc.setCreator(metadata.creator)
354
- fs.writeFileSync(fullOutput, await pdfDoc.save())
355
- return ok({ filePath: fullInput, outputPath: fullOutput, metadata })
356
- } catch (error) {
357
- return fail(error, { filePath, outputPath })
358
- }
359
- }
360
-
361
- /**
362
- * 统计 PDF 各页文字字符数(基于整体文本按页估算)
363
- */
364
- async function getPdfTextStats(filePath) {
365
- try {
366
- const readResult = await readPdfText(filePath)
367
- if (!readResult.success) return readResult
368
- const { text, pageCount } = readResult.data
369
- const totalChars = text.length
370
- const totalWords = text.trim().split(/\s+/).filter(Boolean).length
371
- const lines = text.split('\n').filter(Boolean)
372
- return ok({
373
- filePath: resolvePath(filePath),
374
- pageCount,
375
- totalChars,
376
- totalWords,
377
- totalLines: lines.length,
378
- avgCharsPerPage: pageCount > 0 ? Math.round(totalChars / pageCount) : 0,
379
- avgWordsPerPage: pageCount > 0 ? Math.round(totalWords / pageCount) : 0,
380
- })
381
- } catch (error) {
382
- return fail(error, { filePath })
383
- }
384
- }
385
-
386
-
387
- async function pdfToLongImage(pdfPath, outputImagePath) {
388
- try {
389
- pdfPath = resolvePath(pdfPath)
390
- outputImagePath = resolvePath(outputImagePath)
391
- if (!fs.existsSync(pdfPath)) return fail(`File does not exist: ${pdfPath}`, { pdfPath })
392
- // 0. 确保输出目录存在
393
- fs.ensureDirSync(path.dirname(outputImagePath));
394
- // 1. 读取 PDF 文档,设置 scale 参数可提高图片清晰度
395
- const document = await pdf(pdfPath, { scale: 2 });
396
- const pages = [];
397
-
398
- // 2. 遍历每一页,将图片 Buffer 收集起来
399
- for await (const pageBuffer of document) {
400
- pages.push(pageBuffer);
401
- }
402
-
403
- if (pages.length === 0) {
404
- return fail(`PDF has no pages`, { pdfPath })
405
- }
406
-
407
- // 3. 获取所有页面的尺寸,计算最大宽度和总高度
408
- let maxWidth = 0;
409
- let totalHeight = 0;
410
- const pageHeights = [];
411
-
412
- for (const pageBuffer of pages) {
413
- const metadata = await sharp(pageBuffer).metadata();
414
- maxWidth = Math.max(maxWidth, metadata.width);
415
- totalHeight += metadata.height;
416
- pageHeights.push(metadata.height);
417
- }
418
-
419
- // 4. 构建 sharp 的输入:将图片 Buffer 数组叠加,并设置垂直偏移
420
- const sharpInput = [];
421
- let currentHeight = 0;
422
-
423
- for (let i = 0; i < pages.length; i++) {
424
- const pageBuffer = pages[i];
425
- const pageHeight = pageHeights[i];
426
-
427
- // 获取当前页面的实际宽度
428
- const pageMetadata = await sharp(pageBuffer).metadata();
429
- const pageWidth = pageMetadata.width;
430
-
431
- // 计算水平居中的偏移量
432
- const leftOffset = Math.floor((maxWidth - pageWidth) / 2);
433
-
434
- sharpInput.push({
435
- input: pageBuffer,
436
- top: currentHeight, // 每页依次向下排列
437
- left: leftOffset, // 水平居中
438
- });
439
-
440
- currentHeight += pageHeight;
441
- }
442
-
443
- // 5. 使用 sharp 合成长图
444
- await sharp({
445
- create: {
446
- width: maxWidth,
447
- height: totalHeight, // 总高度 = 所有页面高度之和
448
- channels: 4, // RGBA
449
- background: { r: 255, g: 255, b: 255, alpha: 1 }, // 白色背景
450
- },
451
- })
452
- .composite(sharpInput)
453
- .png() // 输出为 PNG 格式
454
- .toFile(outputImagePath)
455
-
456
- return ok({ pdfPath, outputImagePath, pageCount: pages.length, width: maxWidth, height: totalHeight })
457
- } catch (error) {
458
- console.error(error)
459
- return fail(error, { pdfPath, outputImagePath })
460
- }
461
- }
462
-
463
- // ─── 工具描述 ─────────────────────────────────────────────────────────────────
464
-
465
- const descriptions = [
466
- {
467
- type: 'function',
468
- function: {
469
- name: 'readPdfText',
470
- description: '读取 PDF 文件并提取全部文本内容。参数:filePath 为 PDF 路径。返回值:对象,包含 success、data(含 filePath、text、pageCount、wordCount、charCount)、error。',
471
- parameters: {
472
- type: 'object',
473
- properties: {
474
- filePath: { type: 'string', description: 'PDF 文件路径。' },
475
- },
476
- required: ['filePath'],
477
- },
478
- },
479
- },
480
- {
481
- type: 'function',
482
- function: {
483
- name: 'getPdfInfo',
484
- description: '获取 PDF 文档元信息(页数、标题、作者、创建时间、文件大小等)。参数:filePath 为 PDF 路径。返回值:对象,包含 success、data(含 pageCount、size、title、author、subject、keywords、creator、producer、creationDate、modificationDate)、error。',
485
- parameters: {
486
- type: 'object',
487
- properties: {
488
- filePath: { type: 'string', description: 'PDF 文件路径。' },
489
- },
490
- required: ['filePath'],
491
- },
492
- },
493
- },
494
- {
495
- type: 'function',
496
- function: {
497
- name: 'getPdfPageCount',
498
- description: '快速获取 PDF 总页数。参数:filePath 为 PDF 路径。返回值:对象,包含 success、data(含 filePath、pageCount)、error。',
499
- parameters: {
500
- type: 'object',
501
- properties: {
502
- filePath: { type: 'string', description: 'PDF 文件路径。' },
503
- },
504
- required: ['filePath'],
505
- },
506
- },
507
- },
508
- {
509
- type: 'function',
510
- function: {
511
- name: 'searchPdfText',
512
- description: '在 PDF 内容中搜索关键词,返回所有包含该词的行。参数:filePath 为 PDF 路径;keyword 为搜索关键词。返回值:对象,包含 success、data(含 keyword、matchCount、matches 数组)、error。',
513
- parameters: {
514
- type: 'object',
515
- properties: {
516
- filePath: { type: 'string', description: 'PDF 文件路径。' },
517
- keyword: { type: 'string', description: '搜索关键词。' },
518
- },
519
- required: ['filePath', 'keyword'],
520
- },
521
- },
522
- },
523
- {
524
- type: 'function',
525
- function: {
526
- name: 'mergePdfs',
527
- description: '将多个 PDF 文件合并为一个。参数:inputPaths 为 PDF 路径数组(按顺序合并);outputPath 为输出路径。返回值:对象,包含 success、data(含 outputPath、pageCount、inputCount)、error。',
528
- parameters: {
529
- type: 'object',
530
- properties: {
531
- inputPaths: { type: 'array', items: { type: 'string' }, description: '要合并的 PDF 文件路径数组。' },
532
- outputPath: { type: 'string', description: '合并后输出的 PDF 文件路径。' },
533
- },
534
- required: ['inputPaths', 'outputPath'],
535
- },
536
- },
537
- },
538
- {
539
- type: 'function',
540
- function: {
541
- name: 'splitPdf',
542
- description: '将 PDF 按页拆分,每页生成独立 PDF 文件。参数:filePath 为源 PDF 路径;outputDir 为输出目录。返回值:对象,包含 success、data(含 outputDir、pageCount、files 数组)、error。',
543
- parameters: {
544
- type: 'object',
545
- properties: {
546
- filePath: { type: 'string', description: '要拆分的 PDF 文件路径。' },
547
- outputDir: { type: 'string', description: '拆分后各页 PDF 的输出目录。' },
548
- },
549
- required: ['filePath', 'outputDir'],
550
- },
551
- },
552
- },
553
- {
554
- type: 'function',
555
- function: {
556
- name: 'extractPdfPages',
557
- description: '从 PDF 中提取指定页码(1-based)生成新 PDF。参数:filePath 为源 PDF 路径;outputPath 为输出路径;pages 为页码数组(如 [1, 3, 5])。返回值:对象,包含 success、data(含 outputPath、extractedPages)、error。',
558
- parameters: {
559
- type: 'object',
560
- properties: {
561
- filePath: { type: 'string', description: '源 PDF 文件路径。' },
562
- outputPath: { type: 'string', description: '输出 PDF 文件路径。' },
563
- pages: { type: 'array', items: { type: 'number' }, description: '要提取的页码数组(从 1 开始),如 [1, 2, 5]。' },
564
- },
565
- required: ['filePath', 'outputPath', 'pages'],
566
- },
567
- },
568
- },
569
- {
570
- type: 'function',
571
- function: {
572
- name: 'rotatePdfPages',
573
- description: '旋转 PDF 页面。参数:filePath 为源 PDF 路径;outputPath 为输出路径;rotateDegrees 为旋转角度(90/180/270,默认 90);pageNumbers 为要旋转的页码数组(1-based,省略则旋转全部页)。返回值:对象,包含 success、data(含 outputPath、degrees、rotatedPages)、error。',
574
- parameters: {
575
- type: 'object',
576
- properties: {
577
- filePath: { type: 'string', description: '源 PDF 文件路径。' },
578
- outputPath: { type: 'string', description: '输出 PDF 文件路径。' },
579
- rotateDegrees: { type: 'number', description: '旋转角度(90/180/270),默认 90。' },
580
- pageNumbers: { type: 'array', items: { type: 'number' }, description: '要旋转的页码数组(从 1 开始),省略则旋转全部页。' },
581
- },
582
- required: ['filePath', 'outputPath'],
583
- },
584
- },
585
- },
586
- {
587
- type: 'function',
588
- function: {
589
- name: 'addWatermarkText',
590
- description: '向 PDF 全部页面添加文字水印。参数:filePath 为源 PDF 路径;outputPath 为输出路径;text 为水印文字;options 可选(fontSize 默认 48,opacity 默认 0.2,angle 默认 45,color: {r,g,b})。返回值:对象,包含 success、data(含 outputPath、text、pageCount)、error。',
591
- parameters: {
592
- type: 'object',
593
- properties: {
594
- filePath: { type: 'string', description: '源 PDF 文件路径。' },
595
- outputPath: { type: 'string', description: '输出 PDF 文件路径。' },
596
- text: { type: 'string', description: '水印文字内容。' },
597
- options: { type: 'object', description: '可选:{ fontSize, opacity, angle, color: {r,g,b} }。' },
598
- },
599
- required: ['filePath', 'outputPath', 'text'],
600
- },
601
- },
602
- },
603
- {
604
- type: 'function',
605
- function: {
606
- name: 'createPdf',
607
- description: `创建新的 PDF 文档,支持多种内容类型。
608
- 参数:outputPath 为输出路径;content 为内容数组,每项格式:
609
- - 文本:{ type:'text', text, fontSize, bold, color, align }
610
- - 标题:{ type:'heading', text, fontSize, color, align }
611
- - 分页:{ type:'pageBreak' }
612
- options 可选:{ pageSize: A4/A3/Letter, margin, title, author, subject }。
613
- 返回值:对象,包含 success、data(含 outputPath、itemCount)、error。`,
614
- parameters: {
615
- type: 'object',
616
- properties: {
617
- outputPath: { type: 'string', description: '输出 PDF 文件路径。' },
618
- content: { type: 'array', description: '内容块数组,支持 text/heading/pageBreak 类型。', items: { type: 'object' } },
619
- options: { type: 'object', description: '可选:{ pageSize, margin, title, author, subject }。' },
620
- },
621
- required: ['outputPath'],
622
- },
623
- },
624
- },
625
- {
626
- type: 'function',
627
- function: {
628
- name: 'textToPdf',
629
- description: '将纯文本文件(.txt)转换为 PDF 文档。参数:inputPath 为 .txt 文件路径;outputPath 为输出 PDF 路径;options 可选(同 createPdf options)。返回值:对象,包含 success、data(含 outputPath)、error。',
630
- parameters: {
631
- type: 'object',
632
- properties: {
633
- inputPath: { type: 'string', description: '源 .txt 文件路径。' },
634
- outputPath: { type: 'string', description: '输出 PDF 文件路径。' },
635
- options: { type: 'object', description: '可选:{ pageSize, margin, title, author }。' },
636
- },
637
- required: ['inputPath', 'outputPath'],
638
- },
639
- },
640
- },
641
- {
642
- type: 'function',
643
- function: {
644
- name: 'appendBlankPages',
645
- description: '在 PDF 末尾追加指定数量的空白页。参数:filePath 为源 PDF 路径;outputPath 为输出路径;count 为追加页数(默认 1)。返回值:对象,包含 success、data(含 outputPath、addedPages、totalPages)、error。',
646
- parameters: {
647
- type: 'object',
648
- properties: {
649
- filePath: { type: 'string', description: '源 PDF 文件路径。' },
650
- outputPath: { type: 'string', description: '输出 PDF 文件路径。' },
651
- count: { type: 'number', description: '要追加的空白页数量,默认 1。' },
652
- },
653
- required: ['filePath', 'outputPath'],
654
- },
655
- },
656
- },
657
- {
658
- type: 'function',
659
- function: {
660
- name: 'setPdfMetadata',
661
- description: '设置 PDF 文档的元信息(标题、作者、主题、关键词)。参数:filePath 为源 PDF 路径;outputPath 为输出路径;metadata 为元信息对象(含 title/author/subject/keywords/creator 字段)。返回值:对象,包含 success、data(含 outputPath、metadata)、error。',
662
- parameters: {
663
- type: 'object',
664
- properties: {
665
- filePath: { type: 'string', description: '源 PDF 文件路径。' },
666
- outputPath: { type: 'string', description: '输出 PDF 文件路径。' },
667
- metadata: {
668
- type: 'object',
669
- description: '元信息对象,如 { title, author, subject, keywords, creator }。',
670
- },
671
- },
672
- required: ['filePath', 'outputPath', 'metadata'],
673
- },
674
- },
675
- },
676
- {
677
- type: 'function',
678
- function: {
679
- name: 'getPdfTextStats',
680
- description: '统计 PDF 文档的文字数量(总字数、总字符数、总行数及每页平均值)。参数:filePath 为 PDF 路径。返回值:对象,包含 success、data(含 pageCount、totalChars、totalWords、totalLines、avgCharsPerPage、avgWordsPerPage)、error。',
681
- parameters: {
682
- type: 'object',
683
- properties: {
684
- filePath: { type: 'string', description: 'PDF 文件路径。' },
685
- },
686
- required: ['filePath'],
687
- },
688
- },
689
- },
690
- {
691
- type: 'function',
692
- function: {
693
- name: 'pdfToLongImage',
694
- description: '将 PDF 所有页面依次拼接为一张竖向长图(PNG 格式)。参数:pdfPath 为 PDF 文件路径;outputImagePath 为输出 PNG 图片路径。返回值:对象,包含 success、data(含 pdfPath、outputImagePath、pageCount、width、height)、error。',
695
- parameters: {
696
- type: 'object',
697
- properties: {
698
- pdfPath: { type: 'string', description: '源 PDF 文件路径。' },
699
- outputImagePath: { type: 'string', description: '输出长图的 PNG 文件路径。' },
700
- },
701
- required: ['pdfPath', 'outputImagePath'],
702
- },
703
- },
704
- },
705
- ]
706
-
707
- // ─── 导出 ──────────────────────────────────────────────────────────────────────
708
-
709
- const functions = {
710
- readPdfText,
711
- getPdfInfo,
712
- getPdfPageCount,
713
- searchPdfText,
714
- mergePdfs,
715
- splitPdf,
716
- extractPdfPages,
717
- rotatePdfPages,
718
- addWatermarkText,
719
- createPdf,
720
- textToPdf,
721
- appendBlankPages,
722
- setPdfMetadata,
723
- getPdfTextStats,
724
- pdfToLongImage,
725
- }
726
-
727
- const PdfTool = {
728
- name: 'PdfTool',
729
- description: '提供 PDF 文档的文本提取、搜索、元信息读写、合并、拆分、提取页面、旋转、添加水印、创建、文本转 PDF、PDF 转长图等全面处理能力',
730
- platform: 'all',
731
- descriptions,
732
- functions,
733
- isSystem: true
734
- }
735
-
736
- module.exports = PdfTool