agent-reader 1.1.3 → 1.1.4

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/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ ## 1.1.3 - 2026-03-14
4
+
5
+ - Fixed slideshow landscape PDF crash by removing unstable in-page canvas recompression.
6
+ - Improved custom theme error message (`theme not found`) for better novice UX.
7
+ - Added MCP `export_slideshow` tool to export image directories directly to HTML/PDF.
8
+ - Expanded OpenClaw skill docs/schema to include slideshow export tool and parameter hints.
9
+
10
+ ## 1.1.2 - 2026-03-14
11
+
12
+ - Added auto fallback retry (`--no-sandbox`) for PDF export in `sandbox=auto` mode.
13
+
14
+ ## 1.1.1 - 2026-03-14
15
+
16
+ - Fixed PDF export stability (`networkidle0` to `domcontentloaded` + font/image readiness wait).
17
+ - Registered `mcp` command in CLI.
18
+ - Fixed stdin TTY handling.
19
+
20
+ ## 1.1.0 - 2026-03-14
21
+
22
+ - Implemented v2 plan: sandbox modes, DOCX fallback quality upgrades, path guard, skill/schema packaging, tests, and docs updates.
package/README.md CHANGED
@@ -193,13 +193,14 @@ Claude Desktop 配置(`claude_desktop_config.json`):
193
193
  }
194
194
  ```
195
195
 
196
- 提供 6 个工具:
196
+ 提供 7 个工具:
197
197
 
198
198
  | 工具 | 功能 |
199
199
  |------|------|
200
200
  | `render_markdown` | Markdown → 网页预览 |
201
201
  | `export_document` | Markdown → PDF / Word |
202
202
  | `create_slideshow` | 图片目录 → 幻灯片 |
203
+ | `export_slideshow` | 图片目录 → 幻灯片 HTML / PDF |
203
204
  | `open_file` | 智能打开(按偏好自动选格式) |
204
205
  | `configure_user_preferences` | 设置默认偏好 |
205
206
  | `get_user_preferences` | 读取当前偏好 |
@@ -226,7 +227,7 @@ Claude Desktop 配置(`claude_desktop_config.json`):
226
227
  | 依赖 | 必需? | 用途 |
227
228
  |------|--------|------|
228
229
  | Node.js 18+ | 是 | 运行环境 |
229
- | Puppeteer | | PDF 导出(安装时自动下载 Chromium) |
230
+ | Puppeteer | 否(按需) | PDF 导出(可选依赖;未安装时仅影响 PDF) |
230
231
  | Pandoc | 否 | Word 导出更好看(没有会自动降级为纯 JS 方案) |
231
232
 
232
233
  ## 云环境部署
package/SKILL.md CHANGED
@@ -9,7 +9,7 @@ description: 把 Markdown 渲染成漂亮网页、导出 Word/PDF、图片做幻
9
9
 
10
10
  - Markdown 渲染为可阅读网页(目录、代码高亮、表格样式)
11
11
  - 导出 PDF / DOCX 文档
12
- - 图片目录生成幻灯片并支持导出 PDF
12
+ - 图片目录生成幻灯片并支持直接导出 PDF
13
13
 
14
14
  ## 触发条件
15
15
 
@@ -31,8 +31,17 @@ agent-reader slides ./images --auto 5
31
31
  - `render_markdown`
32
32
  - `export_document`
33
33
  - `create_slideshow`
34
+ - `export_slideshow`
34
35
  - `open_file`
35
36
 
37
+ ### MCP 参数速查
38
+
39
+ - `render_markdown`: `content`(必填), `source_path`, `theme`, `return_content`
40
+ - `export_document`: `content`(必填), `format`=`pdf|docx`(必填), `source_path`, `return_content`
41
+ - `create_slideshow`: `image_dir`(必填), `auto_play`, `return_content`
42
+ - `export_slideshow`: `image_dir`(必填), `format`=`pdf|html`, `auto_play`, `return_content`
43
+ - `open_file`: `file_path`(必填), `open_as`=`auto|web|word|pdf|ppt`, `theme`, `auto_play`, `return_content`
44
+
36
45
  ## 安装
37
46
 
38
47
  ```bash
@@ -17,7 +17,7 @@ const program = new Command();
17
17
  program
18
18
  .name('agent-reader')
19
19
  .description('AI Agent output beautifier and slideshow generator')
20
- .version('1.1.3');
20
+ .version('1.1.4');
21
21
 
22
22
  setupCommonCommandOptions(
23
23
  program
@@ -9,7 +9,7 @@ description: 把 Markdown 渲染成漂亮网页、导出 Word/PDF、图片做幻
9
9
 
10
10
  - Markdown 渲染为可阅读网页(目录、代码高亮、表格样式)
11
11
  - 导出 PDF / DOCX 文档
12
- - 图片目录生成幻灯片并支持导出 PDF
12
+ - 图片目录生成幻灯片并支持直接导出 PDF
13
13
 
14
14
  ## 触发条件
15
15
 
@@ -31,8 +31,17 @@ agent-reader slides ./images --auto 5
31
31
  - `render_markdown`
32
32
  - `export_document`
33
33
  - `create_slideshow`
34
+ - `export_slideshow`
34
35
  - `open_file`
35
36
 
37
+ ### MCP 参数速查
38
+
39
+ - `render_markdown`: `content`(必填), `source_path`, `theme`, `return_content`
40
+ - `export_document`: `content`(必填), `format`=`pdf|docx`(必填), `source_path`, `return_content`
41
+ - `create_slideshow`: `image_dir`(必填), `auto_play`, `return_content`
42
+ - `export_slideshow`: `image_dir`(必填), `format`=`pdf|html`, `auto_play`, `return_content`
43
+ - `open_file`: `file_path`(必填), `open_as`=`auto|web|word|pdf|ppt`, `theme`, `auto_play`, `return_content`
44
+
36
45
  ## 安装
37
46
 
38
47
  ```bash
@@ -192,6 +192,74 @@
192
192
  ],
193
193
  "additionalProperties": true
194
194
  }
195
+ },
196
+ "export_slideshow": {
197
+ "description": "Export slideshow from an image directory into HTML or PDF",
198
+ "input": {
199
+ "type": "object",
200
+ "properties": {
201
+ "image_dir": {
202
+ "type": "string",
203
+ "description": "Absolute or relative image directory path"
204
+ },
205
+ "format": {
206
+ "type": "string",
207
+ "enum": [
208
+ "html",
209
+ "pdf"
210
+ ],
211
+ "description": "Export format, default pdf"
212
+ },
213
+ "auto_play": {
214
+ "type": "number",
215
+ "description": "Autoplay interval in seconds"
216
+ },
217
+ "return_content": {
218
+ "type": "boolean",
219
+ "description": "Return file content directly"
220
+ }
221
+ },
222
+ "required": [
223
+ "image_dir"
224
+ ],
225
+ "additionalProperties": false
226
+ },
227
+ "output": {
228
+ "type": "object",
229
+ "properties": {
230
+ "file_path": {
231
+ "type": "string"
232
+ },
233
+ "content_data": {
234
+ "type": "string"
235
+ },
236
+ "format": {
237
+ "type": "string",
238
+ "enum": [
239
+ "html",
240
+ "pdf"
241
+ ]
242
+ },
243
+ "size": {
244
+ "type": "number"
245
+ },
246
+ "warnings": {
247
+ "type": "array",
248
+ "items": {
249
+ "type": "string"
250
+ }
251
+ },
252
+ "image_count": {
253
+ "type": "number"
254
+ }
255
+ },
256
+ "required": [
257
+ "format",
258
+ "size",
259
+ "warnings"
260
+ ],
261
+ "additionalProperties": true
262
+ }
195
263
  }
196
264
  }
197
265
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-reader",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "AI Agent 的文档美化引擎 — 一键把 Markdown 变成漂亮网页、Word、PDF 和幻灯片",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -173,12 +173,20 @@ async function loadStyles(theme) {
173
173
  : path.resolve(themeName);
174
174
 
175
175
  const highlightCssPath = themeName === 'dark' ? HIGHLIGHT_CSS_DARK : HIGHLIGHT_CSS_LIGHT;
176
+ const customThemePromise = customThemePath
177
+ ? fs.readFile(customThemePath, 'utf8').catch((error) => {
178
+ if (error && error.code === 'ENOENT') {
179
+ throw new Error(`theme not found: ${themeName}. Use "light", "dark", or a valid CSS file path.`);
180
+ }
181
+ throw error;
182
+ })
183
+ : Promise.resolve('');
176
184
 
177
185
  const [builtInTheme, githubCss, highlightCss, customTheme = ''] = await Promise.all([
178
186
  fs.readFile(builtInThemePath, 'utf8'),
179
187
  fs.readFile(GITHUB_MARKDOWN_CSS_PATH, 'utf8'),
180
188
  fs.readFile(highlightCssPath, 'utf8'),
181
- customThemePath ? fs.readFile(customThemePath, 'utf8') : Promise.resolve(''),
189
+ customThemePromise,
182
190
  ]);
183
191
 
184
192
  const mergedCss = `${githubCss}\n${highlightCss}\n${builtInTheme}\n${TOC_BASE_CSS}\n${customTheme}`;
package/src/mcp/server.js CHANGED
@@ -63,6 +63,10 @@ function maybeTooLargePayload(textOrBase64) {
63
63
  return byteLengthOfString(textOrBase64) > MAX_CONTENT_BYTES;
64
64
  }
65
65
 
66
+ function uniqueWarnings(warnings = []) {
67
+ return [...new Set(warnings)];
68
+ }
69
+
66
70
  async function saveHtmlResult(html, outputDir, name = 'output') {
67
71
  const htmlPath = path.join(outputDir, `${name}.html`);
68
72
  await fs.writeFile(htmlPath, html, 'utf8');
@@ -72,7 +76,7 @@ async function saveHtmlResult(html, outputDir, name = 'output') {
72
76
 
73
77
  const server = new McpServer({
74
78
  name: 'agent-reader',
75
- version: '1.1.3',
79
+ version: '1.1.4',
76
80
  });
77
81
 
78
82
  server.registerTool(
@@ -253,6 +257,110 @@ server.registerTool(
253
257
  },
254
258
  );
255
259
 
260
+ server.registerTool(
261
+ 'export_slideshow',
262
+ MCP_TOOL_SCHEMAS.export_slideshow,
263
+ async ({
264
+ image_dir,
265
+ format = 'pdf',
266
+ auto_play,
267
+ return_content,
268
+ }) => {
269
+ try {
270
+ const outputDir = await createOutputDir('export-slideshow');
271
+ const wantsContent = Boolean(return_content);
272
+ const targetFormat = format === 'html' ? 'html' : 'pdf';
273
+ const slideshow = await createSlideshow(image_dir, {
274
+ autoPlay: auto_play,
275
+ inlineAll: wantsContent || targetFormat === 'pdf',
276
+ outDir: outputDir,
277
+ });
278
+ const warnings = [...(slideshow.warnings || [])];
279
+
280
+ if (targetFormat === 'html') {
281
+ const htmlPath = path.join(outputDir, 'slideshow.html');
282
+ await fs.writeFile(htmlPath, slideshow.html, 'utf8');
283
+ const fileSize = (await fs.stat(htmlPath)).size;
284
+
285
+ if (wantsContent) {
286
+ if (!maybeTooLargePayload(slideshow.html)) {
287
+ return toTextResult({
288
+ content_data: slideshow.html,
289
+ format: 'html',
290
+ size: byteLengthOfString(slideshow.html),
291
+ warnings: uniqueWarnings(warnings),
292
+ image_count: slideshow.imageCount,
293
+ });
294
+ }
295
+
296
+ return toTextResult({
297
+ file_path: htmlPath,
298
+ format: 'html',
299
+ size: fileSize,
300
+ warnings: uniqueWarnings([...warnings, 'content_too_large']),
301
+ image_count: slideshow.imageCount,
302
+ });
303
+ }
304
+
305
+ return toTextResult({
306
+ file_path: htmlPath,
307
+ format: 'html',
308
+ size: fileSize,
309
+ warnings: uniqueWarnings(warnings),
310
+ image_count: slideshow.imageCount,
311
+ });
312
+ }
313
+
314
+ const dirName = path.basename(path.resolve(image_dir));
315
+ const printHtmlPath = path.join(outputDir, '_print.html');
316
+ await fs.writeFile(printHtmlPath, slideshow.printHtml, 'utf8');
317
+
318
+ const pdf = await exportPDF(slideshow.printHtml, {
319
+ pageSize: 'A4',
320
+ landscape: true,
321
+ outDir: outputDir,
322
+ fileName: `${dirName}.pdf`,
323
+ htmlPath: printHtmlPath,
324
+ sandbox: MCP_SANDBOX_MODE,
325
+ });
326
+ warnings.push(...(pdf.warnings || []));
327
+
328
+ if (wantsContent) {
329
+ const fileBuffer = await fs.readFile(pdf.pdfPath);
330
+ const fileSize = fileBuffer.byteLength;
331
+ const base64 = fileBuffer.toString('base64');
332
+ if (!maybeTooLargePayload(base64)) {
333
+ return toTextResult({
334
+ content_data: base64,
335
+ format: 'pdf',
336
+ size: fileSize,
337
+ warnings: uniqueWarnings(warnings),
338
+ image_count: slideshow.imageCount,
339
+ });
340
+ }
341
+
342
+ return toTextResult({
343
+ file_path: pdf.pdfPath,
344
+ format: 'pdf',
345
+ size: fileSize,
346
+ warnings: uniqueWarnings([...warnings, 'content_too_large']),
347
+ image_count: slideshow.imageCount,
348
+ });
349
+ }
350
+
351
+ return toTextResult({
352
+ file_path: pdf.pdfPath,
353
+ format: 'pdf',
354
+ size: pdf.size,
355
+ warnings: uniqueWarnings(warnings),
356
+ image_count: slideshow.imageCount,
357
+ });
358
+ } catch (error) {
359
+ return toErrorResult(error, 'slideshow_export_failed');
360
+ }
361
+ },
362
+ );
363
+
256
364
  server.registerTool(
257
365
  'open_file',
258
366
  MCP_TOOL_SCHEMAS.open_file,
@@ -29,6 +29,15 @@ export const MCP_TOOL_SCHEMAS = {
29
29
  return_content: z.boolean().optional().describe('Return inline HTML content directly'),
30
30
  },
31
31
  },
32
+ export_slideshow: {
33
+ description: 'Export slideshow from an image directory into HTML or PDF',
34
+ inputSchema: {
35
+ image_dir: z.string().describe('Absolute or relative image directory path'),
36
+ format: z.enum(['html', 'pdf']).optional().describe('Export format, default pdf'),
37
+ auto_play: z.number().optional().describe('Autoplay interval in seconds'),
38
+ return_content: z.boolean().optional().describe('Return file content directly'),
39
+ },
40
+ },
32
41
  open_file: {
33
42
  description: 'Open a local file/path using user preference or explicit mode: web/word/pdf/ppt',
34
43
  inputSchema: {