@gangtiser/md2wechat 0.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/LICENSE +5 -0
- package/README.md +105 -0
- package/dist/src/cli/index.d.ts +2 -0
- package/dist/src/cli/index.js +251 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/config/config.d.ts +9 -0
- package/dist/src/config/config.js +11 -0
- package/dist/src/config/config.js.map +1 -0
- package/dist/src/core/article.d.ts +20 -0
- package/dist/src/core/article.js +81 -0
- package/dist/src/core/article.js.map +1 -0
- package/dist/src/core/envelope.d.ts +14 -0
- package/dist/src/core/envelope.js +34 -0
- package/dist/src/core/envelope.js.map +1 -0
- package/dist/src/core/errors.d.ts +7 -0
- package/dist/src/core/errors.js +22 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/core/html.d.ts +2 -0
- package/dist/src/core/html.js +18 -0
- package/dist/src/core/html.js.map +1 -0
- package/dist/src/core/io.d.ts +11 -0
- package/dist/src/core/io.js +28 -0
- package/dist/src/core/io.js.map +1 -0
- package/dist/src/images/generate.d.ts +17 -0
- package/dist/src/images/generate.js +28 -0
- package/dist/src/images/generate.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/inspect/inspect.d.ts +18 -0
- package/dist/src/inspect/inspect.js +31 -0
- package/dist/src/inspect/inspect.js.map +1 -0
- package/dist/src/mcp/server.d.ts +1 -0
- package/dist/src/mcp/server.js +25 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tools.d.ts +19 -0
- package/dist/src/mcp/tools.js +190 -0
- package/dist/src/mcp/tools.js.map +1 -0
- package/dist/src/openai/client.d.ts +34 -0
- package/dist/src/openai/client.js +101 -0
- package/dist/src/openai/client.js.map +1 -0
- package/dist/src/preview/preview.d.ts +2 -0
- package/dist/src/preview/preview.js +21 -0
- package/dist/src/preview/preview.js.map +1 -0
- package/dist/src/renderer/render.d.ts +19 -0
- package/dist/src/renderer/render.js +36 -0
- package/dist/src/renderer/render.js.map +1 -0
- package/dist/src/themes/builtin.d.ts +2 -0
- package/dist/src/themes/builtin.js +21 -0
- package/dist/src/themes/builtin.js.map +1 -0
- package/dist/src/themes/registry.d.ts +18 -0
- package/dist/src/themes/registry.js +130 -0
- package/dist/src/themes/registry.js.map +1 -0
- package/package.json +55 -0
- package/skills/md2wechat/SKILL.md +52 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# md2wechat
|
|
2
|
+
|
|
3
|
+
`md2wechat` is a TypeScript CLI, Codex Skill, and MCP server for turning Markdown into WeChat Official Account HTML. It runs locally, uses OpenAI for rendering and image generation, and includes a local theme registry.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @gangtiser/md2wechat
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Node.js 20 or newer is required. Set your OpenAI key before conversion or image generation:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
export OPENAI_API_KEY="your_openai_key"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Defaults
|
|
18
|
+
|
|
19
|
+
- Text rendering model: `gpt-5.5`
|
|
20
|
+
- Image generation model: `gpt-image-2`
|
|
21
|
+
- Runtime: Node.js 20+
|
|
22
|
+
- No hosted md2wechat conversion service is used.
|
|
23
|
+
|
|
24
|
+
## CLI Usage
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
md2wechat inspect article.md --json
|
|
28
|
+
md2wechat themes list --json
|
|
29
|
+
md2wechat convert article.md --theme default --output article.html --json
|
|
30
|
+
md2wechat preview article.md --theme default --output preview.html --json
|
|
31
|
+
md2wechat generate-image "Editorial cover for an AI article" --output cover.png --json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Theme management:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
md2wechat themes register custom ./themes/custom.json --json
|
|
38
|
+
md2wechat themes remove custom --json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## MCP Usage
|
|
42
|
+
|
|
43
|
+
Start the stdio MCP server:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
md2wechat mcp
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Example MCP client configuration:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"md2wechat": {
|
|
55
|
+
"command": "md2wechat",
|
|
56
|
+
"args": ["mcp"],
|
|
57
|
+
"env": {
|
|
58
|
+
"OPENAI_API_KEY": "your_openai_key"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Available tools:
|
|
66
|
+
|
|
67
|
+
- `convert_article`
|
|
68
|
+
- `preview_article`
|
|
69
|
+
- `generate_image`
|
|
70
|
+
- `list_themes`
|
|
71
|
+
- `register_theme`
|
|
72
|
+
- `remove_theme`
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
- `OPENAI_API_KEY`: required for conversion and image generation
|
|
77
|
+
- `OPENAI_BASE_URL`: optional OpenAI-compatible base URL
|
|
78
|
+
- `OPENAI_TEXT_MODEL`: optional override, defaults to `gpt-5.5`
|
|
79
|
+
- `OPENAI_IMAGE_MODEL`: optional override, defaults to `gpt-image-2`
|
|
80
|
+
- `MD2WECHAT_THEME_REGISTRY`: optional custom theme registry file
|
|
81
|
+
- `MD2WECHAT_THEMES_DIR`: optional custom theme directory
|
|
82
|
+
|
|
83
|
+
## Theme File Format
|
|
84
|
+
|
|
85
|
+
Custom themes are JSON files:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"id": "custom",
|
|
90
|
+
"name": "Custom",
|
|
91
|
+
"description": "Optional description",
|
|
92
|
+
"prompt": "Style guidance for the renderer."
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Development
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm install
|
|
100
|
+
npm run typecheck
|
|
101
|
+
npm test
|
|
102
|
+
npm run verify
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Generated build output lives in `dist/` and is packaged for npm, but not committed to git.
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseArticle } from "../core/article.js";
|
|
5
|
+
import { successEnvelope, failureEnvelope } from "../core/envelope.js";
|
|
6
|
+
import { AppError, toAppError } from "../core/errors.js";
|
|
7
|
+
import { loadInput } from "../core/io.js";
|
|
8
|
+
import { loadConfig } from "../config/config.js";
|
|
9
|
+
import { generateImage } from "../images/generate.js";
|
|
10
|
+
import { createOpenAIClient } from "../openai/client.js";
|
|
11
|
+
import { buildPreviewDocument } from "../preview/preview.js";
|
|
12
|
+
import { renderArticle } from "../renderer/render.js";
|
|
13
|
+
import { inspectArticle } from "../inspect/inspect.js";
|
|
14
|
+
import { listThemes, registerTheme, removeTheme, resolveTheme } from "../themes/registry.js";
|
|
15
|
+
async function main(argv = process.argv.slice(2)) {
|
|
16
|
+
const args = parseArgs(argv);
|
|
17
|
+
try {
|
|
18
|
+
rejectModeFlag(args);
|
|
19
|
+
await route(args);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
const appError = toAppError(error);
|
|
23
|
+
if (args.flags.has("json")) {
|
|
24
|
+
printJson(failureEnvelope(appError.code, appError.message, appError.retryable, appError.message));
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.error(appError.message);
|
|
28
|
+
}
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function parseArgs(argv) {
|
|
33
|
+
const command = [];
|
|
34
|
+
const values = [];
|
|
35
|
+
const flags = new Map();
|
|
36
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
37
|
+
const item = argv[index];
|
|
38
|
+
if (item.startsWith("--")) {
|
|
39
|
+
const name = item.slice(2);
|
|
40
|
+
const next = argv[index + 1];
|
|
41
|
+
if (next && !next.startsWith("--")) {
|
|
42
|
+
flags.set(name, next);
|
|
43
|
+
index += 1;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
flags.set(name, true);
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (command.length === 0 || (command[0] === "themes" && command.length === 1)) {
|
|
51
|
+
command.push(item);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
values.push(item);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { command, values, flags };
|
|
58
|
+
}
|
|
59
|
+
function rejectModeFlag(args) {
|
|
60
|
+
if (args.flags.has("mode")) {
|
|
61
|
+
throw new AppError("MODE_NOT_SUPPORTED", "mode option is not supported");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function route(args) {
|
|
65
|
+
const [primary, secondary] = args.command;
|
|
66
|
+
if (primary === "inspect") {
|
|
67
|
+
await inspectCommand(args);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (primary === "convert") {
|
|
71
|
+
await convertCommand(args);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (primary === "preview") {
|
|
75
|
+
await previewCommand(args);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (primary === "generate-image") {
|
|
79
|
+
await generateImageCommand(args);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (primary === "themes" && secondary === "list") {
|
|
83
|
+
await themesListCommand(args);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (primary === "themes" && secondary === "register") {
|
|
87
|
+
await themesRegisterCommand(args);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (primary === "themes" && secondary === "remove") {
|
|
91
|
+
await themesRemoveCommand(args);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (primary === "mcp") {
|
|
95
|
+
const { startMcpServer } = await import("../mcp/server.js");
|
|
96
|
+
await startMcpServer();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
throw new AppError("COMMAND_INVALID", `unknown command: ${args.command.join(" ") || "(empty)"}`);
|
|
100
|
+
}
|
|
101
|
+
async function inspectCommand(args) {
|
|
102
|
+
const file = requiredValue(args, 0, "markdown file is required");
|
|
103
|
+
const input = await loadInput({ file });
|
|
104
|
+
const result = inspectArticle(input.content, { baseDir: input.baseDir });
|
|
105
|
+
output(args, "INSPECT_COMPLETED", "Inspection completed", result, formatInspect(result));
|
|
106
|
+
}
|
|
107
|
+
async function convertCommand(args) {
|
|
108
|
+
const file = requiredValue(args, 0, "markdown file is required");
|
|
109
|
+
const outputPath = stringFlag(args, "output") || stringFlag(args, "o");
|
|
110
|
+
const themeId = stringFlag(args, "theme") || "default";
|
|
111
|
+
const config = loadConfig();
|
|
112
|
+
const apiKey = requireOpenAIKey(config.openaiApiKey);
|
|
113
|
+
const input = await loadInput({ file });
|
|
114
|
+
const article = parseArticle(input.content);
|
|
115
|
+
const theme = await resolveTheme(themeId, themeOptions(config));
|
|
116
|
+
const client = createOpenAIClient({
|
|
117
|
+
apiKey,
|
|
118
|
+
baseUrl: config.openaiBaseUrl,
|
|
119
|
+
textModel: config.openaiTextModel,
|
|
120
|
+
imageModel: config.openaiImageModel
|
|
121
|
+
});
|
|
122
|
+
const rendered = await renderArticle({
|
|
123
|
+
markdown: article.body,
|
|
124
|
+
metadata: article.metadata,
|
|
125
|
+
theme,
|
|
126
|
+
client
|
|
127
|
+
});
|
|
128
|
+
if (outputPath) {
|
|
129
|
+
await writeOutputFile(outputPath, rendered.html);
|
|
130
|
+
}
|
|
131
|
+
output(args, "CONVERT_COMPLETED", "Conversion completed", {
|
|
132
|
+
...rendered,
|
|
133
|
+
metadata: article.metadata,
|
|
134
|
+
output_file: outputPath ? path.resolve(outputPath) : undefined
|
|
135
|
+
}, outputPath ? `HTML written to ${outputPath}` : rendered.html);
|
|
136
|
+
}
|
|
137
|
+
async function previewCommand(args) {
|
|
138
|
+
const file = requiredValue(args, 0, "markdown file is required");
|
|
139
|
+
const outputPath = stringFlag(args, "output") || stringFlag(args, "o") || "preview.html";
|
|
140
|
+
const themeId = stringFlag(args, "theme") || "default";
|
|
141
|
+
const config = loadConfig();
|
|
142
|
+
const apiKey = requireOpenAIKey(config.openaiApiKey);
|
|
143
|
+
const input = await loadInput({ file });
|
|
144
|
+
const article = parseArticle(input.content);
|
|
145
|
+
const theme = await resolveTheme(themeId, themeOptions(config));
|
|
146
|
+
const client = createOpenAIClient({
|
|
147
|
+
apiKey,
|
|
148
|
+
baseUrl: config.openaiBaseUrl,
|
|
149
|
+
textModel: config.openaiTextModel,
|
|
150
|
+
imageModel: config.openaiImageModel
|
|
151
|
+
});
|
|
152
|
+
const rendered = await renderArticle({
|
|
153
|
+
markdown: article.body,
|
|
154
|
+
metadata: article.metadata,
|
|
155
|
+
theme,
|
|
156
|
+
client
|
|
157
|
+
});
|
|
158
|
+
const preview = buildPreviewDocument(rendered.html, article.metadata);
|
|
159
|
+
await writeOutputFile(outputPath, preview);
|
|
160
|
+
output(args, "PREVIEW_READY", "Preview ready", {
|
|
161
|
+
output_file: path.resolve(outputPath),
|
|
162
|
+
metadata: article.metadata,
|
|
163
|
+
theme_id: theme.id
|
|
164
|
+
}, `Preview written to ${outputPath}`);
|
|
165
|
+
}
|
|
166
|
+
async function generateImageCommand(args) {
|
|
167
|
+
const config = loadConfig();
|
|
168
|
+
const apiKey = requireOpenAIKey(config.openaiApiKey);
|
|
169
|
+
const prompt = args.values[0] || "WeChat article cover image";
|
|
170
|
+
const articleFile = stringFlag(args, "article");
|
|
171
|
+
const articleContext = articleFile ? (await loadInput({ file: articleFile })).content : undefined;
|
|
172
|
+
const outputPath = stringFlag(args, "output") || stringFlag(args, "o") || "image.png";
|
|
173
|
+
const client = createOpenAIClient({
|
|
174
|
+
apiKey,
|
|
175
|
+
baseUrl: config.openaiBaseUrl,
|
|
176
|
+
textModel: config.openaiTextModel,
|
|
177
|
+
imageModel: config.openaiImageModel
|
|
178
|
+
});
|
|
179
|
+
const image = await generateImage({
|
|
180
|
+
prompt,
|
|
181
|
+
articleContext,
|
|
182
|
+
output: outputPath,
|
|
183
|
+
size: stringFlag(args, "size"),
|
|
184
|
+
quality: stringFlag(args, "quality"),
|
|
185
|
+
client
|
|
186
|
+
});
|
|
187
|
+
output(args, "IMAGE_GENERATED", "Image generated", image, `Image written to ${image.outputPath}`);
|
|
188
|
+
}
|
|
189
|
+
async function themesListCommand(args) {
|
|
190
|
+
const themes = await listThemes(themeOptions(loadConfig()));
|
|
191
|
+
output(args, "THEMES_LISTED", "Themes listed", { themes }, themes.map((theme) => `${theme.id}\t${theme.name}`).join("\n"));
|
|
192
|
+
}
|
|
193
|
+
async function themesRegisterCommand(args) {
|
|
194
|
+
const name = requiredValue(args, 0, "theme name is required");
|
|
195
|
+
const themePath = requiredValue(args, 1, "theme path is required");
|
|
196
|
+
const theme = await registerTheme(name, themePath, themeOptions(loadConfig()));
|
|
197
|
+
output(args, "THEME_REGISTERED", "Theme registered", { theme }, `Registered theme ${theme.id}`);
|
|
198
|
+
}
|
|
199
|
+
async function themesRemoveCommand(args) {
|
|
200
|
+
const name = requiredValue(args, 0, "theme name is required");
|
|
201
|
+
await removeTheme(name, themeOptions(loadConfig()));
|
|
202
|
+
output(args, "THEME_REMOVED", "Theme removed", { id: name }, `Removed theme ${name}`);
|
|
203
|
+
}
|
|
204
|
+
function output(args, code, message, data, text) {
|
|
205
|
+
if (args.flags.has("json")) {
|
|
206
|
+
printJson(successEnvelope(code, message, data));
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
console.log(text);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function printJson(value) {
|
|
213
|
+
console.log(JSON.stringify(value, null, 2));
|
|
214
|
+
}
|
|
215
|
+
function requiredValue(args, index, message) {
|
|
216
|
+
const value = args.values[index];
|
|
217
|
+
if (!value) {
|
|
218
|
+
throw new AppError("INPUT_INVALID", message);
|
|
219
|
+
}
|
|
220
|
+
return value;
|
|
221
|
+
}
|
|
222
|
+
function stringFlag(args, name) {
|
|
223
|
+
const value = args.flags.get(name);
|
|
224
|
+
return typeof value === "string" ? value : undefined;
|
|
225
|
+
}
|
|
226
|
+
function requireOpenAIKey(value) {
|
|
227
|
+
if (!value) {
|
|
228
|
+
throw new AppError("OPENAI_KEY_MISSING", "OPENAI_API_KEY is required");
|
|
229
|
+
}
|
|
230
|
+
return value;
|
|
231
|
+
}
|
|
232
|
+
function themeOptions(config) {
|
|
233
|
+
return {
|
|
234
|
+
registryPath: config.themeRegistryPath,
|
|
235
|
+
projectThemesDir: config.themesDir
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async function writeOutputFile(outputPath, content) {
|
|
239
|
+
const absolute = path.resolve(outputPath);
|
|
240
|
+
await mkdir(path.dirname(absolute), { recursive: true });
|
|
241
|
+
await writeFile(absolute, content);
|
|
242
|
+
}
|
|
243
|
+
function formatInspect(result) {
|
|
244
|
+
return [
|
|
245
|
+
`Title: ${result.metadata.title || "(missing)"}`,
|
|
246
|
+
`Images: ${result.images.length}`,
|
|
247
|
+
...result.checks.map((check) => `${check.status.toUpperCase()} ${check.code}: ${check.message}`)
|
|
248
|
+
].join("\n");
|
|
249
|
+
}
|
|
250
|
+
void main();
|
|
251
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface RuntimeConfig {
|
|
2
|
+
openaiApiKey?: string;
|
|
3
|
+
openaiBaseUrl: string;
|
|
4
|
+
openaiTextModel: string;
|
|
5
|
+
openaiImageModel: string;
|
|
6
|
+
themeRegistryPath?: string;
|
|
7
|
+
themesDir?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function loadConfig(env?: NodeJS.ProcessEnv): RuntimeConfig;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function loadConfig(env = process.env) {
|
|
2
|
+
return {
|
|
3
|
+
openaiApiKey: env.OPENAI_API_KEY,
|
|
4
|
+
openaiBaseUrl: env.OPENAI_BASE_URL || "https://api.openai.com/v1",
|
|
5
|
+
openaiTextModel: env.OPENAI_TEXT_MODEL || "gpt-5.5",
|
|
6
|
+
openaiImageModel: env.OPENAI_IMAGE_MODEL || "gpt-image-2",
|
|
7
|
+
themeRegistryPath: env.MD2WECHAT_THEME_REGISTRY,
|
|
8
|
+
themesDir: env.MD2WECHAT_THEMES_DIR
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/config/config.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,OAAO;QACL,YAAY,EAAE,GAAG,CAAC,cAAc;QAChC,aAAa,EAAE,GAAG,CAAC,eAAe,IAAI,2BAA2B;QACjE,eAAe,EAAE,GAAG,CAAC,iBAAiB,IAAI,SAAS;QACnD,gBAAgB,EAAE,GAAG,CAAC,kBAAkB,IAAI,aAAa;QACzD,iBAAiB,EAAE,GAAG,CAAC,wBAAwB;QAC/C,SAAS,EAAE,GAAG,CAAC,oBAAoB;KACpC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type ImageReferenceType = "local" | "remote" | "ai";
|
|
2
|
+
export interface ArticleMetadata {
|
|
3
|
+
title?: string;
|
|
4
|
+
author?: string;
|
|
5
|
+
digest?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ImageReference {
|
|
8
|
+
index: number;
|
|
9
|
+
alt: string;
|
|
10
|
+
src: string;
|
|
11
|
+
type: ImageReferenceType;
|
|
12
|
+
}
|
|
13
|
+
export interface ParsedArticle {
|
|
14
|
+
originalMarkdown: string;
|
|
15
|
+
body: string;
|
|
16
|
+
metadata: ArticleMetadata;
|
|
17
|
+
headingTitle?: string;
|
|
18
|
+
images: ImageReference[];
|
|
19
|
+
}
|
|
20
|
+
export declare function parseArticle(markdown: string): ParsedArticle;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export function parseArticle(markdown) {
|
|
2
|
+
const { metadata, body } = parseFrontmatter(markdown);
|
|
3
|
+
const headingTitle = firstHeading(body);
|
|
4
|
+
const resolvedMetadata = {
|
|
5
|
+
...metadata,
|
|
6
|
+
title: metadata.title || headingTitle
|
|
7
|
+
};
|
|
8
|
+
return {
|
|
9
|
+
originalMarkdown: markdown,
|
|
10
|
+
body,
|
|
11
|
+
metadata: resolvedMetadata,
|
|
12
|
+
headingTitle,
|
|
13
|
+
images: extractImages(body)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function parseFrontmatter(markdown) {
|
|
17
|
+
if (!markdown.startsWith("---\n")) {
|
|
18
|
+
return { metadata: {}, body: markdown };
|
|
19
|
+
}
|
|
20
|
+
const end = markdown.indexOf("\n---", 4);
|
|
21
|
+
if (end === -1) {
|
|
22
|
+
return { metadata: {}, body: markdown };
|
|
23
|
+
}
|
|
24
|
+
const frontmatter = markdown.slice(4, end);
|
|
25
|
+
const body = markdown.slice(end + 4).replace(/^\r?\n/, "");
|
|
26
|
+
const fields = {};
|
|
27
|
+
for (const line of frontmatter.split(/\r?\n/)) {
|
|
28
|
+
const match = /^([A-Za-z_][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line);
|
|
29
|
+
if (!match) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
fields[match[1].toLowerCase()] = stripQuotes(match[2].trim());
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
metadata: {
|
|
36
|
+
title: emptyToUndefined(fields.title),
|
|
37
|
+
author: emptyToUndefined(fields.author),
|
|
38
|
+
digest: emptyToUndefined(fields.digest || fields.summary || fields.description)
|
|
39
|
+
},
|
|
40
|
+
body
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function stripQuotes(value) {
|
|
44
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
45
|
+
return value.slice(1, -1);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function emptyToUndefined(value) {
|
|
50
|
+
const trimmed = value?.trim();
|
|
51
|
+
return trimmed ? trimmed : undefined;
|
|
52
|
+
}
|
|
53
|
+
function firstHeading(markdown) {
|
|
54
|
+
const match = /^#\s+(.+?)\s*$/m.exec(markdown);
|
|
55
|
+
return match?.[1]?.trim();
|
|
56
|
+
}
|
|
57
|
+
function extractImages(markdown) {
|
|
58
|
+
const images = [];
|
|
59
|
+
const pattern = /!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
60
|
+
let match;
|
|
61
|
+
while ((match = pattern.exec(markdown)) !== null) {
|
|
62
|
+
const src = match[2];
|
|
63
|
+
images.push({
|
|
64
|
+
index: images.length,
|
|
65
|
+
alt: match[1],
|
|
66
|
+
src,
|
|
67
|
+
type: imageType(src)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return images;
|
|
71
|
+
}
|
|
72
|
+
function imageType(src) {
|
|
73
|
+
if (src.startsWith("ai:")) {
|
|
74
|
+
return "ai";
|
|
75
|
+
}
|
|
76
|
+
if (/^https?:\/\//i.test(src)) {
|
|
77
|
+
return "remote";
|
|
78
|
+
}
|
|
79
|
+
return "local";
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=article.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"article.js","sourceRoot":"","sources":["../../../src/core/article.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type EnvelopeStatus = "completed" | "action_required" | "failed";
|
|
2
|
+
export interface JsonEnvelope<T = unknown> {
|
|
3
|
+
success: boolean;
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
schema_version: "1.0";
|
|
7
|
+
status: EnvelopeStatus;
|
|
8
|
+
retryable: boolean;
|
|
9
|
+
data?: T;
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function successEnvelope<T>(code: string, message: string, data: T): JsonEnvelope<T>;
|
|
13
|
+
export declare function actionEnvelope<T>(code: string, message: string, data: T): JsonEnvelope<T>;
|
|
14
|
+
export declare function failureEnvelope(code: string, message: string, retryable: boolean, error?: string): JsonEnvelope;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function successEnvelope(code, message, data) {
|
|
2
|
+
return {
|
|
3
|
+
success: true,
|
|
4
|
+
code,
|
|
5
|
+
message,
|
|
6
|
+
schema_version: "1.0",
|
|
7
|
+
status: "completed",
|
|
8
|
+
retryable: false,
|
|
9
|
+
data
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function actionEnvelope(code, message, data) {
|
|
13
|
+
return {
|
|
14
|
+
success: true,
|
|
15
|
+
code,
|
|
16
|
+
message,
|
|
17
|
+
schema_version: "1.0",
|
|
18
|
+
status: "action_required",
|
|
19
|
+
retryable: false,
|
|
20
|
+
data
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function failureEnvelope(code, message, retryable, error) {
|
|
24
|
+
return {
|
|
25
|
+
success: false,
|
|
26
|
+
code,
|
|
27
|
+
message,
|
|
28
|
+
schema_version: "1.0",
|
|
29
|
+
status: "failed",
|
|
30
|
+
retryable,
|
|
31
|
+
error
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../../src/core/envelope.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,eAAe,CAAI,IAAY,EAAE,OAAe,EAAE,IAAO;IACvE,OAAO;QACL,OAAO,EAAE,IAAI;QACb,IAAI;QACJ,OAAO;QACP,cAAc,EAAE,KAAK;QACrB,MAAM,EAAE,WAAW;QACnB,SAAS,EAAE,KAAK;QAChB,IAAI;KACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAI,IAAY,EAAE,OAAe,EAAE,IAAO;IACtE,OAAO;QACL,OAAO,EAAE,IAAI;QACb,IAAI;QACJ,OAAO;QACP,cAAc,EAAE,KAAK;QACrB,MAAM,EAAE,iBAAiB;QACzB,SAAS,EAAE,KAAK;QAChB,IAAI;KACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe,EAAE,SAAkB,EAAE,KAAc;IAC/F,OAAO;QACL,OAAO,EAAE,KAAK;QACd,IAAI;QACJ,OAAO;QACP,cAAc,EAAE,KAAK;QACrB,MAAM,EAAE,QAAQ;QAChB,SAAS;QACT,KAAK;KACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare class AppError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
readonly retryable: boolean;
|
|
4
|
+
readonly original?: unknown | undefined;
|
|
5
|
+
constructor(code: string, message: string, retryable?: boolean, original?: unknown | undefined);
|
|
6
|
+
}
|
|
7
|
+
export declare function toAppError(error: unknown, fallbackCode?: string): AppError;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export class AppError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
retryable;
|
|
4
|
+
original;
|
|
5
|
+
constructor(code, message, retryable = false, original) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.retryable = retryable;
|
|
9
|
+
this.original = original;
|
|
10
|
+
this.name = "AppError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function toAppError(error, fallbackCode = "ERROR") {
|
|
14
|
+
if (error instanceof AppError) {
|
|
15
|
+
return error;
|
|
16
|
+
}
|
|
17
|
+
if (error instanceof Error) {
|
|
18
|
+
return new AppError(fallbackCode, error.message, false, error);
|
|
19
|
+
}
|
|
20
|
+
return new AppError(fallbackCode, String(error), false, error);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEf;IAEA;IACA;IAJlB,YACkB,IAAY,EAC5B,OAAe,EACC,YAAY,KAAK,EACjB,QAAkB;QAElC,KAAK,CAAC,OAAO,CAAC,CAAC;QALC,SAAI,GAAJ,IAAI,CAAQ;QAEZ,cAAS,GAAT,SAAS,CAAQ;QACjB,aAAQ,GAAR,QAAQ,CAAU;QAGlC,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,KAAc,EAAE,YAAY,GAAG,OAAO;IAC/D,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,IAAI,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function sanitizeWechatHtml(html) {
|
|
2
|
+
return html
|
|
3
|
+
.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "")
|
|
4
|
+
.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "")
|
|
5
|
+
.replace(/\s+on[a-z]+\s*=\s*"[^"]*"/gi, "")
|
|
6
|
+
.replace(/\s+on[a-z]+\s*=\s*'[^']*'/gi, "")
|
|
7
|
+
.replace(/\s+on[a-z]+\s*=\s*[^\s>]+/gi, "")
|
|
8
|
+
.replace(/javascript:/gi, "");
|
|
9
|
+
}
|
|
10
|
+
export function escapeHtml(value) {
|
|
11
|
+
return value
|
|
12
|
+
.replace(/&/g, "&")
|
|
13
|
+
.replace(/</g, "<")
|
|
14
|
+
.replace(/>/g, ">")
|
|
15
|
+
.replace(/"/g, """)
|
|
16
|
+
.replace(/'/g, "'");
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.js","sourceRoot":"","sources":["../../../src/core/html.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI;SACR,OAAO,CAAC,qCAAqC,EAAE,EAAE,CAAC;SAClD,OAAO,CAAC,mCAAmC,EAAE,EAAE,CAAC;SAChD,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC;SAC1C,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC;SAC1C,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC;SAC1C,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface LoadInputOptions {
|
|
2
|
+
content?: string;
|
|
3
|
+
file?: string;
|
|
4
|
+
contentUrl?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface LoadedInput {
|
|
7
|
+
content: string;
|
|
8
|
+
baseDir?: string;
|
|
9
|
+
source: "content" | "file" | "url";
|
|
10
|
+
}
|
|
11
|
+
export declare function loadInput(options: LoadInputOptions): Promise<LoadedInput>;
|