agent-reader 1.0.0 → 1.1.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 +64 -1
- package/SKILL.md +40 -0
- package/bin/agent-reader.js +1 -1
- package/openclaw-skill/SKILL.md +40 -0
- package/openclaw-skill/schema.json +197 -0
- package/package.json +1 -1
- package/src/cli/commands.js +22 -2
- package/src/core/assets.js +8 -0
- package/src/core/exporter.js +279 -17
- package/src/core/opener.js +4 -1
- package/src/core/renderer.js +7 -6
- package/src/core/themes/dark.css +322 -182
- package/src/mcp/server.js +12 -52
- package/src/mcp/toolSchemas.js +53 -0
- package/src/utils/pathGuard.js +62 -0
- package/src/utils/server.js +10 -4
- package/src/core/themes/print.css +0 -54
package/README.md
CHANGED
|
@@ -29,6 +29,24 @@ Agent Reader 解决这三个问题:**一条命令,输出即交付**。
|
|
|
29
29
|
| 图片 → 幻灯片 | 全屏播放、键盘翻页、自动轮播、缩略图导航 |
|
|
30
30
|
| 幻灯片 → PDF | 每张图片一页,适合存档分享 |
|
|
31
31
|
|
|
32
|
+
## 幻灯片快速上手
|
|
33
|
+
|
|
34
|
+
如果你有一组图片要现场展示,直接用这一条命令:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
agent-reader slides ./my-images/
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
常用选项:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
agent-reader slides ./my-images/ --auto 5
|
|
44
|
+
agent-reader slides ./my-images/ --format pdf
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
支持格式:`png`、`jpg`、`jpeg`、`gif`、`svg`、`webp`。
|
|
48
|
+
图片按自然顺序排序(例如 `1`、`2`、`10`,不是 `1`、`10`、`2`)。
|
|
49
|
+
|
|
32
50
|
## 两种使用方式
|
|
33
51
|
|
|
34
52
|
**CLI** — 开发者和 Agent 通过命令行调用
|
|
@@ -93,7 +111,6 @@ agent-reader render 你的文件.md
|
|
|
93
111
|
```bash
|
|
94
112
|
agent-reader render 你的文件.md --theme light # 亮色(默认)
|
|
95
113
|
agent-reader render 你的文件.md --theme dark # 暗色
|
|
96
|
-
agent-reader render 你的文件.md --theme print # 打印友好
|
|
97
114
|
```
|
|
98
115
|
|
|
99
116
|
### 导出 Word
|
|
@@ -211,3 +228,49 @@ Claude Desktop 配置(`claude_desktop_config.json`):
|
|
|
211
228
|
| Node.js 18+ | 是 | 运行环境 |
|
|
212
229
|
| Puppeteer | 是 | PDF 导出(安装时自动下载 Chromium) |
|
|
213
230
|
| Pandoc | 否 | Word 导出更好看(没有会自动降级为纯 JS 方案) |
|
|
231
|
+
|
|
232
|
+
## 云环境部署
|
|
233
|
+
|
|
234
|
+
在 Docker/CI 中,Agent Reader 会自动检测环境并在需要时为 Puppeteer 关闭沙盒参数(`--no-sandbox` 等)。
|
|
235
|
+
|
|
236
|
+
手动覆盖方式:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# auto | on | off
|
|
240
|
+
AGENT_READER_SANDBOX=off agent-reader export report.md --format pdf
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Docker 环境建议预装 Chromium 依赖(示例):
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
apt-get update && apt-get install -y chromium-browser
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## FAQ:没有 Pandoc 怎么办?
|
|
250
|
+
|
|
251
|
+
- 不装 Pandoc 也能导出 DOCX,只是排版会使用基础模式
|
|
252
|
+
- 装 Pandoc 后,代码块和复杂表格的效果更稳定
|
|
253
|
+
|
|
254
|
+
常见安装命令:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# macOS
|
|
258
|
+
brew install pandoc
|
|
259
|
+
|
|
260
|
+
# Linux
|
|
261
|
+
apt-get install pandoc
|
|
262
|
+
|
|
263
|
+
# Windows
|
|
264
|
+
winget install pandoc
|
|
265
|
+
# or: choco install pandoc
|
|
266
|
+
# or: scoop install pandoc
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Puppeteer 进阶安装
|
|
270
|
+
|
|
271
|
+
如果你已经有可用浏览器,想减少安装体积:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true npm install -g agent-reader
|
|
275
|
+
PUPPETEER_EXECUTABLE_PATH=/path/to/chrome agent-reader export report.md --format pdf
|
|
276
|
+
```
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent_reader
|
|
3
|
+
description: 把 Markdown 渲染成漂亮网页、导出 Word/PDF、图片做幻灯片。专为 AI Agent 输出设计。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Agent Reader Skill
|
|
7
|
+
|
|
8
|
+
## 能力
|
|
9
|
+
|
|
10
|
+
- Markdown 渲染为可阅读网页(目录、代码高亮、表格样式)
|
|
11
|
+
- 导出 PDF / DOCX 文档
|
|
12
|
+
- 图片目录生成幻灯片并支持导出 PDF
|
|
13
|
+
|
|
14
|
+
## 触发条件
|
|
15
|
+
|
|
16
|
+
- 用户要求把 Markdown 输出整理成可交付文档
|
|
17
|
+
- 用户需要导出 Word 或 PDF
|
|
18
|
+
- 用户希望把图片集合用于展示或汇报
|
|
19
|
+
|
|
20
|
+
## CLI 调用
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
agent-reader render report.md
|
|
24
|
+
agent-reader export report.md --format pdf
|
|
25
|
+
agent-reader export report.md --format docx
|
|
26
|
+
agent-reader slides ./images --auto 5
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## MCP 调用
|
|
30
|
+
|
|
31
|
+
- `render_markdown`
|
|
32
|
+
- `export_document`
|
|
33
|
+
- `create_slideshow`
|
|
34
|
+
- `open_file`
|
|
35
|
+
|
|
36
|
+
## 安装
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install -g agent-reader
|
|
40
|
+
```
|
package/bin/agent-reader.js
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent_reader
|
|
3
|
+
description: 把 Markdown 渲染成漂亮网页、导出 Word/PDF、图片做幻灯片。专为 AI Agent 输出设计。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Agent Reader Skill
|
|
7
|
+
|
|
8
|
+
## 能力
|
|
9
|
+
|
|
10
|
+
- Markdown 渲染为可阅读网页(目录、代码高亮、表格样式)
|
|
11
|
+
- 导出 PDF / DOCX 文档
|
|
12
|
+
- 图片目录生成幻灯片并支持导出 PDF
|
|
13
|
+
|
|
14
|
+
## 触发条件
|
|
15
|
+
|
|
16
|
+
- 用户要求把 Markdown 输出整理成可交付文档
|
|
17
|
+
- 用户需要导出 Word 或 PDF
|
|
18
|
+
- 用户希望把图片集合用于展示或汇报
|
|
19
|
+
|
|
20
|
+
## CLI 调用
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
agent-reader render report.md
|
|
24
|
+
agent-reader export report.md --format pdf
|
|
25
|
+
agent-reader export report.md --format docx
|
|
26
|
+
agent-reader slides ./images --auto 5
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## MCP 调用
|
|
30
|
+
|
|
31
|
+
- `render_markdown`
|
|
32
|
+
- `export_document`
|
|
33
|
+
- `create_slideshow`
|
|
34
|
+
- `open_file`
|
|
35
|
+
|
|
36
|
+
## 安装
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install -g agent-reader
|
|
40
|
+
```
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "agent-reader-openclaw-skill-schema",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"tools": {
|
|
6
|
+
"render_markdown": {
|
|
7
|
+
"description": "Render markdown text into styled HTML",
|
|
8
|
+
"input": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"properties": {
|
|
11
|
+
"content": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "Markdown source content"
|
|
14
|
+
},
|
|
15
|
+
"source_path": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Source markdown path for relative images; relative resources must stay under source directory."
|
|
18
|
+
},
|
|
19
|
+
"theme": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Theme name"
|
|
22
|
+
},
|
|
23
|
+
"auto_open": {
|
|
24
|
+
"type": "boolean",
|
|
25
|
+
"description": "Open output automatically (ignored in MCP)"
|
|
26
|
+
},
|
|
27
|
+
"return_content": {
|
|
28
|
+
"type": "boolean",
|
|
29
|
+
"description": "Return inline HTML content directly"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"required": [
|
|
33
|
+
"content"
|
|
34
|
+
],
|
|
35
|
+
"additionalProperties": false
|
|
36
|
+
},
|
|
37
|
+
"output": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"properties": {
|
|
40
|
+
"html_path": {
|
|
41
|
+
"type": "string"
|
|
42
|
+
},
|
|
43
|
+
"content_data": {
|
|
44
|
+
"type": "string"
|
|
45
|
+
},
|
|
46
|
+
"format": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"enum": [
|
|
49
|
+
"html"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"size": {
|
|
53
|
+
"type": "number"
|
|
54
|
+
},
|
|
55
|
+
"warnings": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"items": {
|
|
58
|
+
"type": "string"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"required": [
|
|
63
|
+
"format",
|
|
64
|
+
"size",
|
|
65
|
+
"warnings"
|
|
66
|
+
],
|
|
67
|
+
"additionalProperties": true
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"export_document": {
|
|
71
|
+
"description": "Export markdown text into PDF or DOCX",
|
|
72
|
+
"input": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"properties": {
|
|
75
|
+
"content": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"description": "Markdown source content"
|
|
78
|
+
},
|
|
79
|
+
"source_path": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"description": "Source markdown path for relative images; relative resources must stay under source directory."
|
|
82
|
+
},
|
|
83
|
+
"format": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"enum": [
|
|
86
|
+
"pdf",
|
|
87
|
+
"docx"
|
|
88
|
+
],
|
|
89
|
+
"description": "Export format"
|
|
90
|
+
},
|
|
91
|
+
"return_content": {
|
|
92
|
+
"type": "boolean",
|
|
93
|
+
"description": "Return file bytes as base64"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"required": [
|
|
97
|
+
"content",
|
|
98
|
+
"format"
|
|
99
|
+
],
|
|
100
|
+
"additionalProperties": false
|
|
101
|
+
},
|
|
102
|
+
"output": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"properties": {
|
|
105
|
+
"file_path": {
|
|
106
|
+
"type": "string"
|
|
107
|
+
},
|
|
108
|
+
"content_data": {
|
|
109
|
+
"type": "string"
|
|
110
|
+
},
|
|
111
|
+
"format": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"enum": [
|
|
114
|
+
"pdf",
|
|
115
|
+
"docx"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
"size": {
|
|
119
|
+
"type": "number"
|
|
120
|
+
},
|
|
121
|
+
"warnings": {
|
|
122
|
+
"type": "array",
|
|
123
|
+
"items": {
|
|
124
|
+
"type": "string"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"required": [
|
|
129
|
+
"format",
|
|
130
|
+
"size",
|
|
131
|
+
"warnings"
|
|
132
|
+
],
|
|
133
|
+
"additionalProperties": true
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"create_slideshow": {
|
|
137
|
+
"description": "Create slideshow HTML from an image directory",
|
|
138
|
+
"input": {
|
|
139
|
+
"type": "object",
|
|
140
|
+
"properties": {
|
|
141
|
+
"image_dir": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"description": "Absolute or relative image directory path"
|
|
144
|
+
},
|
|
145
|
+
"auto_play": {
|
|
146
|
+
"type": "number",
|
|
147
|
+
"description": "Autoplay interval in seconds"
|
|
148
|
+
},
|
|
149
|
+
"auto_open": {
|
|
150
|
+
"type": "boolean",
|
|
151
|
+
"description": "Open output automatically (ignored in MCP)"
|
|
152
|
+
},
|
|
153
|
+
"return_content": {
|
|
154
|
+
"type": "boolean",
|
|
155
|
+
"description": "Return inline HTML content directly"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"required": [
|
|
159
|
+
"image_dir"
|
|
160
|
+
],
|
|
161
|
+
"additionalProperties": false
|
|
162
|
+
},
|
|
163
|
+
"output": {
|
|
164
|
+
"type": "object",
|
|
165
|
+
"properties": {
|
|
166
|
+
"html_path": {
|
|
167
|
+
"type": "string"
|
|
168
|
+
},
|
|
169
|
+
"content_data": {
|
|
170
|
+
"type": "string"
|
|
171
|
+
},
|
|
172
|
+
"format": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"enum": [
|
|
175
|
+
"html"
|
|
176
|
+
]
|
|
177
|
+
},
|
|
178
|
+
"size": {
|
|
179
|
+
"type": "number"
|
|
180
|
+
},
|
|
181
|
+
"warnings": {
|
|
182
|
+
"type": "array",
|
|
183
|
+
"items": {
|
|
184
|
+
"type": "string"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
"required": [
|
|
189
|
+
"format",
|
|
190
|
+
"size",
|
|
191
|
+
"warnings"
|
|
192
|
+
],
|
|
193
|
+
"additionalProperties": true
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
package/package.json
CHANGED
package/src/cli/commands.js
CHANGED
|
@@ -4,7 +4,14 @@ import open from 'open';
|
|
|
4
4
|
import { execa } from 'execa';
|
|
5
5
|
import { renderMarkdown } from '../core/renderer.js';
|
|
6
6
|
import { createSlideshow } from '../core/slideshow.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
checkPandoc,
|
|
9
|
+
checkPuppeteer,
|
|
10
|
+
exportDOCX,
|
|
11
|
+
exportDOCXFromHTML,
|
|
12
|
+
exportPDF,
|
|
13
|
+
resolveSandboxMode,
|
|
14
|
+
} from '../core/exporter.js';
|
|
8
15
|
import { openTarget } from '../core/opener.js';
|
|
9
16
|
import { cleanOldOutputs, createOutputDir } from '../utils/output.js';
|
|
10
17
|
import { createLogger } from '../utils/logger.js';
|
|
@@ -86,6 +93,10 @@ function dedupeWarnings(warnings) {
|
|
|
86
93
|
return [...new Set((warnings || []).filter(Boolean))];
|
|
87
94
|
}
|
|
88
95
|
|
|
96
|
+
function resolveSandboxOption(options = {}) {
|
|
97
|
+
return resolveSandboxMode(options.sandbox, process.env);
|
|
98
|
+
}
|
|
99
|
+
|
|
89
100
|
async function buildPrintHtml(imageDir) {
|
|
90
101
|
const absDir = path.resolve(imageDir);
|
|
91
102
|
const entries = await fs.readdir(absDir, { withFileTypes: true });
|
|
@@ -135,9 +146,11 @@ async function maybeServeOutput(options, outputDir, targetPath, logger, jsonOnly
|
|
|
135
146
|
return null;
|
|
136
147
|
}
|
|
137
148
|
|
|
149
|
+
const sandbox = resolveSandboxOption(options);
|
|
138
150
|
const serverHandle = await startStaticServer(outputDir, {
|
|
139
151
|
host: '127.0.0.1',
|
|
140
152
|
port: Number(options.port || 3000),
|
|
153
|
+
sandbox,
|
|
141
154
|
});
|
|
142
155
|
|
|
143
156
|
const rel = path.relative(outputDir, targetPath).split(path.sep).join('/');
|
|
@@ -222,6 +235,7 @@ export async function exportCommand(file, options) {
|
|
|
222
235
|
|
|
223
236
|
try {
|
|
224
237
|
const format = normalizeFormat(options.format);
|
|
238
|
+
const sandbox = resolveSandboxOption(options);
|
|
225
239
|
const input = await resolveMarkdownInput(file, options, mode.logger);
|
|
226
240
|
const outputDir = await createOutputDir(input.name, options.outDir);
|
|
227
241
|
const warnings = [];
|
|
@@ -249,6 +263,7 @@ export async function exportCommand(file, options) {
|
|
|
249
263
|
outDir: outputDir,
|
|
250
264
|
fileName: `${input.name}.pdf`,
|
|
251
265
|
htmlPath,
|
|
266
|
+
sandbox,
|
|
252
267
|
});
|
|
253
268
|
warnings.push(...pdf.warnings);
|
|
254
269
|
targetPath = pdf.pdfPath;
|
|
@@ -322,6 +337,7 @@ export async function slidesCommand(dir, options) {
|
|
|
322
337
|
await fs.writeFile(printHtmlPath, result.printHtml, 'utf8');
|
|
323
338
|
|
|
324
339
|
const format = String(options.format || '').toLowerCase();
|
|
340
|
+
const sandbox = resolveSandboxOption(options);
|
|
325
341
|
const dirName = path.basename(inputDir);
|
|
326
342
|
|
|
327
343
|
if (format === 'pdf') {
|
|
@@ -331,6 +347,7 @@ export async function slidesCommand(dir, options) {
|
|
|
331
347
|
outDir: outputDir,
|
|
332
348
|
fileName: `${dirName}.pdf`,
|
|
333
349
|
htmlPath: printHtmlPath,
|
|
350
|
+
sandbox,
|
|
334
351
|
});
|
|
335
352
|
|
|
336
353
|
const pdfWarnings = dedupeWarnings([...result.warnings, ...(pdf.warnings || [])]);
|
|
@@ -398,6 +415,7 @@ export async function openCommand(target, options) {
|
|
|
398
415
|
throw new Error('missing target path');
|
|
399
416
|
}
|
|
400
417
|
|
|
418
|
+
const sandbox = resolveSandboxOption(options);
|
|
401
419
|
const preferences = await loadPreferences();
|
|
402
420
|
const requestedMode = normalizeOpenMode(options.as || options.mode || 'auto', 'auto');
|
|
403
421
|
|
|
@@ -410,6 +428,7 @@ export async function openCommand(target, options) {
|
|
|
410
428
|
pageSize: options.pageSize || 'A4',
|
|
411
429
|
fetchRemote: options.fetchRemote !== false,
|
|
412
430
|
returnContent: false,
|
|
431
|
+
sandbox,
|
|
413
432
|
});
|
|
414
433
|
|
|
415
434
|
const canServe = result.format === 'html' && result.path;
|
|
@@ -598,5 +617,6 @@ export function setupCommonCommandOptions(command) {
|
|
|
598
617
|
.option('--inline-all', 'inline all assets to base64')
|
|
599
618
|
.option('--no-fetch-remote', 'disable remote image fetching')
|
|
600
619
|
.option('--serve', 'serve generated files via local HTTP server')
|
|
601
|
-
.option('--port <port>', 'port for --serve mode', '3000')
|
|
620
|
+
.option('--port <port>', 'port for --serve mode', '3000')
|
|
621
|
+
.option('--sandbox <mode>', 'Puppeteer sandbox mode: auto|on|off');
|
|
602
622
|
}
|
package/src/core/assets.js
CHANGED
|
@@ -3,6 +3,7 @@ import dns from 'node:dns/promises';
|
|
|
3
3
|
import { promises as fs } from 'node:fs';
|
|
4
4
|
import net from 'node:net';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
+
import { assertWithinBase } from '../utils/pathGuard.js';
|
|
6
7
|
|
|
7
8
|
const LOCAL_IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp']);
|
|
8
9
|
const DEFAULT_MAX_INLINE_BYTES = 200 * 1024;
|
|
@@ -241,6 +242,13 @@ async function processLocalImage(src, {
|
|
|
241
242
|
}
|
|
242
243
|
|
|
243
244
|
const resolvedPath = isAbsoluteFile ? cleanSrc : path.resolve(baseDir, cleanSrc);
|
|
245
|
+
if (!isAbsoluteFile) {
|
|
246
|
+
const withinBase = await assertWithinBase(resolvedPath, baseDir);
|
|
247
|
+
if (!withinBase) {
|
|
248
|
+
warnings.push(`path traversal blocked: ${cleanSrc}`);
|
|
249
|
+
return cleanSrc;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
244
252
|
|
|
245
253
|
let fileBuffer;
|
|
246
254
|
try {
|