@gangtiser/md2wechat 0.1.0 → 0.2.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 +29 -13
- package/dist/src/cli/index.js +26 -55
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/config.d.ts +17 -5
- package/dist/src/config/config.js +66 -5
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/mcp/tools.js +2 -51
- package/dist/src/mcp/tools.js.map +1 -1
- package/dist/src/renderer/render.d.ts +1 -3
- package/dist/src/renderer/render.js +152 -29
- package/dist/src/renderer/render.js.map +1 -1
- package/package.json +6 -5
- package/skills/md2wechat/SKILL.md +11 -11
- package/dist/src/images/generate.d.ts +0 -17
- package/dist/src/images/generate.js +0 -28
- package/dist/src/images/generate.js.map +0 -1
- package/dist/src/openai/client.d.ts +0 -34
- package/dist/src/openai/client.js +0 -101
- package/dist/src/openai/client.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# md2wechat
|
|
2
2
|
|
|
3
|
-
`md2wechat` is a TypeScript CLI, Codex Skill, and MCP server for turning Markdown into WeChat Official Account HTML. It runs locally, uses
|
|
3
|
+
`md2wechat` is a TypeScript CLI, Codex Skill, and MCP server for turning Markdown into WeChat Official Account HTML. It runs locally, uses deterministic conversion, and lets Codex or Claude handle any AI writing before calling the tool.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,27 +8,44 @@
|
|
|
8
8
|
npm install -g @gangtiser/md2wechat
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Node.js 20 or newer is required.
|
|
11
|
+
Node.js 20 or newer is required. Initialize customer configuration before using WeChat account features:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
|
|
14
|
+
md2wechat config init
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Open the generated config file and fill in:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"WECHAT_APP_ID": "your_app_id",
|
|
22
|
+
"WECHAT_APP_SECRET": "your_app_secret"
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Environment variables can override the file:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
export WECHAT_APP_ID="your_app_id"
|
|
30
|
+
export WECHAT_APP_SECRET="your_app_secret"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Defaults & Agent Usage
|
|
18
34
|
|
|
19
|
-
- Text rendering model: `gpt-5.5`
|
|
20
|
-
- Image generation model: `gpt-image-2`
|
|
21
35
|
- Runtime: Node.js 20+
|
|
36
|
+
- Markdown conversion: local deterministic renderer
|
|
37
|
+
- AI authoring/editing: use the host agent, such as Codex or Claude CLI
|
|
22
38
|
- No hosted md2wechat conversion service is used.
|
|
23
39
|
|
|
24
40
|
## CLI Usage
|
|
25
41
|
|
|
26
42
|
```bash
|
|
27
43
|
md2wechat inspect article.md --json
|
|
44
|
+
md2wechat config init --json
|
|
45
|
+
md2wechat config status --json
|
|
28
46
|
md2wechat themes list --json
|
|
29
47
|
md2wechat convert article.md --theme default --output article.html --json
|
|
30
48
|
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
49
|
```
|
|
33
50
|
|
|
34
51
|
Theme management:
|
|
@@ -55,7 +72,8 @@ Example MCP client configuration:
|
|
|
55
72
|
"command": "md2wechat",
|
|
56
73
|
"args": ["mcp"],
|
|
57
74
|
"env": {
|
|
58
|
-
"
|
|
75
|
+
"WECHAT_APP_ID": "your_app_id",
|
|
76
|
+
"WECHAT_APP_SECRET": "your_app_secret"
|
|
59
77
|
}
|
|
60
78
|
}
|
|
61
79
|
}
|
|
@@ -66,17 +84,15 @@ Available tools:
|
|
|
66
84
|
|
|
67
85
|
- `convert_article`
|
|
68
86
|
- `preview_article`
|
|
69
|
-
- `generate_image`
|
|
70
87
|
- `list_themes`
|
|
71
88
|
- `register_theme`
|
|
72
89
|
- `remove_theme`
|
|
73
90
|
|
|
74
91
|
## Configuration
|
|
75
92
|
|
|
76
|
-
- `
|
|
77
|
-
- `
|
|
78
|
-
- `
|
|
79
|
-
- `OPENAI_IMAGE_MODEL`: optional override, defaults to `gpt-image-2`
|
|
93
|
+
- `WECHAT_APP_ID`: optional environment override for the configured WeChat AppID
|
|
94
|
+
- `WECHAT_APP_SECRET`: optional environment override for the configured WeChat AppSecret
|
|
95
|
+
- `MD2WECHAT_CONFIG`: optional path to the JSON config file
|
|
80
96
|
- `MD2WECHAT_THEME_REGISTRY`: optional custom theme registry file
|
|
81
97
|
- `MD2WECHAT_THEMES_DIR`: optional custom theme directory
|
|
82
98
|
|
package/dist/src/cli/index.js
CHANGED
|
@@ -5,9 +5,7 @@ import { parseArticle } from "../core/article.js";
|
|
|
5
5
|
import { successEnvelope, failureEnvelope } from "../core/envelope.js";
|
|
6
6
|
import { AppError, toAppError } from "../core/errors.js";
|
|
7
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";
|
|
8
|
+
import { configStatus, loadConfig, resolveConfigPath, writeDefaultConfig } from "../config/config.js";
|
|
11
9
|
import { buildPreviewDocument } from "../preview/preview.js";
|
|
12
10
|
import { renderArticle } from "../renderer/render.js";
|
|
13
11
|
import { inspectArticle } from "../inspect/inspect.js";
|
|
@@ -47,7 +45,7 @@ function parseArgs(argv) {
|
|
|
47
45
|
}
|
|
48
46
|
continue;
|
|
49
47
|
}
|
|
50
|
-
if (command.length === 0 || (command[0] === "themes" && command.length === 1)) {
|
|
48
|
+
if (command.length === 0 || ((command[0] === "themes" || command[0] === "config") && command.length === 1)) {
|
|
51
49
|
command.push(item);
|
|
52
50
|
}
|
|
53
51
|
else {
|
|
@@ -75,10 +73,6 @@ async function route(args) {
|
|
|
75
73
|
await previewCommand(args);
|
|
76
74
|
return;
|
|
77
75
|
}
|
|
78
|
-
if (primary === "generate-image") {
|
|
79
|
-
await generateImageCommand(args);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
76
|
if (primary === "themes" && secondary === "list") {
|
|
83
77
|
await themesListCommand(args);
|
|
84
78
|
return;
|
|
@@ -91,6 +85,14 @@ async function route(args) {
|
|
|
91
85
|
await themesRemoveCommand(args);
|
|
92
86
|
return;
|
|
93
87
|
}
|
|
88
|
+
if (primary === "config" && secondary === "init") {
|
|
89
|
+
await configInitCommand(args);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (primary === "config" && secondary === "status") {
|
|
93
|
+
await configStatusCommand(args);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
94
96
|
if (primary === "mcp") {
|
|
95
97
|
const { startMcpServer } = await import("../mcp/server.js");
|
|
96
98
|
await startMcpServer();
|
|
@@ -109,21 +111,13 @@ async function convertCommand(args) {
|
|
|
109
111
|
const outputPath = stringFlag(args, "output") || stringFlag(args, "o");
|
|
110
112
|
const themeId = stringFlag(args, "theme") || "default";
|
|
111
113
|
const config = loadConfig();
|
|
112
|
-
const apiKey = requireOpenAIKey(config.openaiApiKey);
|
|
113
114
|
const input = await loadInput({ file });
|
|
114
115
|
const article = parseArticle(input.content);
|
|
115
116
|
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
117
|
const rendered = await renderArticle({
|
|
123
118
|
markdown: article.body,
|
|
124
119
|
metadata: article.metadata,
|
|
125
|
-
theme
|
|
126
|
-
client
|
|
120
|
+
theme
|
|
127
121
|
});
|
|
128
122
|
if (outputPath) {
|
|
129
123
|
await writeOutputFile(outputPath, rendered.html);
|
|
@@ -139,21 +133,13 @@ async function previewCommand(args) {
|
|
|
139
133
|
const outputPath = stringFlag(args, "output") || stringFlag(args, "o") || "preview.html";
|
|
140
134
|
const themeId = stringFlag(args, "theme") || "default";
|
|
141
135
|
const config = loadConfig();
|
|
142
|
-
const apiKey = requireOpenAIKey(config.openaiApiKey);
|
|
143
136
|
const input = await loadInput({ file });
|
|
144
137
|
const article = parseArticle(input.content);
|
|
145
138
|
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
139
|
const rendered = await renderArticle({
|
|
153
140
|
markdown: article.body,
|
|
154
141
|
metadata: article.metadata,
|
|
155
|
-
theme
|
|
156
|
-
client
|
|
142
|
+
theme
|
|
157
143
|
});
|
|
158
144
|
const preview = buildPreviewDocument(rendered.html, article.metadata);
|
|
159
145
|
await writeOutputFile(outputPath, preview);
|
|
@@ -163,29 +149,6 @@ async function previewCommand(args) {
|
|
|
163
149
|
theme_id: theme.id
|
|
164
150
|
}, `Preview written to ${outputPath}`);
|
|
165
151
|
}
|
|
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
152
|
async function themesListCommand(args) {
|
|
190
153
|
const themes = await listThemes(themeOptions(loadConfig()));
|
|
191
154
|
output(args, "THEMES_LISTED", "Themes listed", { themes }, themes.map((theme) => `${theme.id}\t${theme.name}`).join("\n"));
|
|
@@ -201,6 +164,20 @@ async function themesRemoveCommand(args) {
|
|
|
201
164
|
await removeTheme(name, themeOptions(loadConfig()));
|
|
202
165
|
output(args, "THEME_REMOVED", "Theme removed", { id: name }, `Removed theme ${name}`);
|
|
203
166
|
}
|
|
167
|
+
async function configInitCommand(args) {
|
|
168
|
+
const configPath = resolveConfigPath(process.env, stringFlag(args, "path"));
|
|
169
|
+
const status = await writeDefaultConfig(configPath, { force: args.flags.has("force") });
|
|
170
|
+
output(args, "CONFIG_INITIALIZED", "Config initialized", status, `Config written to ${status.config_path}`);
|
|
171
|
+
}
|
|
172
|
+
async function configStatusCommand(args) {
|
|
173
|
+
const config = loadConfig(process.env, { configPath: stringFlag(args, "path") });
|
|
174
|
+
const status = configStatus(config);
|
|
175
|
+
output(args, "CONFIG_STATUS", "Config status", status, [
|
|
176
|
+
`Config: ${status.config_path}`,
|
|
177
|
+
`WECHAT_APP_ID: ${status.wechat_app_id_configured ? "configured" : "missing"}`,
|
|
178
|
+
`WECHAT_APP_SECRET: ${status.wechat_app_secret_configured ? "configured" : "missing"}`
|
|
179
|
+
].join("\n"));
|
|
180
|
+
}
|
|
204
181
|
function output(args, code, message, data, text) {
|
|
205
182
|
if (args.flags.has("json")) {
|
|
206
183
|
printJson(successEnvelope(code, message, data));
|
|
@@ -223,12 +200,6 @@ function stringFlag(args, name) {
|
|
|
223
200
|
const value = args.flags.get(name);
|
|
224
201
|
return typeof value === "string" ? value : undefined;
|
|
225
202
|
}
|
|
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
203
|
function themeOptions(config) {
|
|
233
204
|
return {
|
|
234
205
|
registryPath: config.themeRegistryPath,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAQ7F,KAAK,UAAU,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,CAAC;QACH,cAAc,CAAC,IAAI,CAAC,CAAC;QACrB,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,SAAS,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACpG,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE/C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YAC7B,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACtB,KAAK,IAAI,CAAC,CAAC;YACb,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACxB,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;YAC3G,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,cAAc,CAAC,IAAgB;IACtC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,QAAQ,CAAC,oBAAoB,EAAE,8BAA8B,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,KAAK,UAAU,KAAK,CAAC,IAAgB;IACnC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;IAE1C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACjD,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QACrD,MAAM,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAClC,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QACnD,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACjD,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QACnD,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;QACtB,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAC5D,MAAM,cAAc,EAAE,CAAC;QACvB,OAAO;IACT,CAAC;IAED,MAAM,IAAI,QAAQ,CAAC,iBAAiB,EAAE,oBAAoB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC;AACnG,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAgB;IAC5C,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,2BAA2B,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACzE,MAAM,CAAC,IAAI,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAgB;IAC5C,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,2BAA2B,CAAC,CAAC;IACjE,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,SAAS,CAAC;IACvD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC;QACnC,QAAQ,EAAE,OAAO,CAAC,IAAI;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK;KACN,CAAC,CAAC;IAEH,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,CAAC,IAAI,EAAE,mBAAmB,EAAE,sBAAsB,EAAE;QACxD,GAAG,QAAQ;QACX,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;KAC/D,EAAE,UAAU,CAAC,CAAC,CAAC,mBAAmB,UAAU,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAgB;IAC5C,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,2BAA2B,CAAC,CAAC;IACjE,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,cAAc,CAAC;IACzF,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,SAAS,CAAC;IACvD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC;QACnC,QAAQ,EAAE,OAAO,CAAC,IAAI;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK;KACN,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,oBAAoB,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtE,MAAM,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,CAAC,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE;QAC7C,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QACrC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,QAAQ,EAAE,KAAK,CAAC,EAAE;KACnB,EAAE,sBAAsB,UAAU,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,IAAgB;IAC/C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,CAAC,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7H,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,IAAgB;IACnD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,wBAAwB,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,wBAAwB,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAC/E,MAAM,CAAC,IAAI,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,EAAE,KAAK,EAAE,EAAE,oBAAoB,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;AAClG,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,IAAgB;IACjD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,wBAAwB,CAAC,CAAC;IAC9D,MAAM,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,iBAAiB,IAAI,EAAE,CAAC,CAAC;AACxF,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,IAAgB;IAC/C,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACxF,MAAM,CAAC,IAAI,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,EAAE,qBAAqB,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;AAC9G,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,IAAgB;IACjD,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACjF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,CAAC,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,EAAE;QACrD,WAAW,MAAM,CAAC,WAAW,EAAE;QAC/B,kBAAkB,MAAM,CAAC,wBAAwB,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,EAAE;QAC9E,sBAAsB,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,EAAE;KACvF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,MAAM,CAAC,IAAgB,EAAE,IAAY,EAAE,OAAe,EAAE,IAAa,EAAE,IAAY;IAC1F,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,SAAS,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAClD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,aAAa,CAAC,IAAgB,EAAE,KAAa,EAAE,OAAe;IACrE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,UAAU,CAAC,IAAgB,EAAE,IAAY;IAChD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,SAAS,YAAY,CAAC,MAAqC;IACzD,OAAO;QACL,YAAY,EAAE,MAAM,CAAC,iBAAiB;QACtC,gBAAgB,EAAE,MAAM,CAAC,SAAS;KACnC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,UAAkB,EAAE,OAAe;IAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,aAAa,CAAC,MAAyC;IAC9D,OAAO;QACL,UAAU,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,WAAW,EAAE;QAChD,WAAW,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;QACjC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;KACjG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,KAAK,IAAI,EAAE,CAAC"}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
export interface RuntimeConfig {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
openaiImageModel: string;
|
|
2
|
+
configPath: string;
|
|
3
|
+
wechatAppId?: string;
|
|
4
|
+
wechatAppSecret?: string;
|
|
6
5
|
themeRegistryPath?: string;
|
|
7
6
|
themesDir?: string;
|
|
8
7
|
}
|
|
9
|
-
export
|
|
8
|
+
export interface LoadConfigOptions {
|
|
9
|
+
configPath?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ConfigStatus {
|
|
12
|
+
config_path: string;
|
|
13
|
+
wechat_app_id_configured: boolean;
|
|
14
|
+
wechat_app_secret_configured: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function loadConfig(env?: NodeJS.ProcessEnv, options?: LoadConfigOptions): RuntimeConfig;
|
|
17
|
+
export declare function resolveConfigPath(env?: NodeJS.ProcessEnv, explicitPath?: string): string;
|
|
18
|
+
export declare function writeDefaultConfig(configPath: string, options?: {
|
|
19
|
+
force?: boolean;
|
|
20
|
+
}): Promise<ConfigStatus>;
|
|
21
|
+
export declare function configStatus(config: RuntimeConfig): ConfigStatus;
|
|
@@ -1,11 +1,72 @@
|
|
|
1
|
-
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { AppError } from "../core/errors.js";
|
|
6
|
+
const DEFAULT_CONFIG = {
|
|
7
|
+
WECHAT_APP_ID: "your_app_id",
|
|
8
|
+
WECHAT_APP_SECRET: "your_app_secret"
|
|
9
|
+
};
|
|
10
|
+
export function loadConfig(env = process.env, options = {}) {
|
|
11
|
+
const configPath = resolveConfigPath(env, options.configPath);
|
|
12
|
+
const fileConfig = readConfigFile(configPath);
|
|
2
13
|
return {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
openaiImageModel: env.OPENAI_IMAGE_MODEL || "gpt-image-2",
|
|
14
|
+
configPath,
|
|
15
|
+
wechatAppId: stringValue(env.WECHAT_APP_ID) || fileConfig.WECHAT_APP_ID,
|
|
16
|
+
wechatAppSecret: stringValue(env.WECHAT_APP_SECRET) || fileConfig.WECHAT_APP_SECRET,
|
|
7
17
|
themeRegistryPath: env.MD2WECHAT_THEME_REGISTRY,
|
|
8
18
|
themesDir: env.MD2WECHAT_THEMES_DIR
|
|
9
19
|
};
|
|
10
20
|
}
|
|
21
|
+
export function resolveConfigPath(env = process.env, explicitPath) {
|
|
22
|
+
return resolve(explicitPath || env.MD2WECHAT_CONFIG || join(homedir(), ".md2wechat", "config.json"));
|
|
23
|
+
}
|
|
24
|
+
export async function writeDefaultConfig(configPath, options = {}) {
|
|
25
|
+
const resolvedPath = resolve(configPath);
|
|
26
|
+
if (existsSync(resolvedPath) && !options.force) {
|
|
27
|
+
throw new AppError("CONFIG_EXISTS", `Config already exists: ${resolvedPath}`);
|
|
28
|
+
}
|
|
29
|
+
await mkdir(dirname(resolvedPath), { recursive: true });
|
|
30
|
+
await writeFile(resolvedPath, `${JSON.stringify(DEFAULT_CONFIG, null, 2)}\n`);
|
|
31
|
+
return configStatus(loadConfig(process.env, { configPath: resolvedPath }));
|
|
32
|
+
}
|
|
33
|
+
export function configStatus(config) {
|
|
34
|
+
return {
|
|
35
|
+
config_path: config.configPath,
|
|
36
|
+
wechat_app_id_configured: isConfiguredCredential(config.wechatAppId),
|
|
37
|
+
wechat_app_secret_configured: isConfiguredCredential(config.wechatAppSecret)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function readConfigFile(configPath) {
|
|
41
|
+
if (!existsSync(configPath)) {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = JSON.parse(readFileSync(configPath, "utf8"));
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new AppError("CONFIG_INVALID", `Unable to read config file: ${configPath}`, false, error);
|
|
50
|
+
}
|
|
51
|
+
if (!isRecord(parsed)) {
|
|
52
|
+
throw new AppError("CONFIG_INVALID", "Config file must contain a JSON object");
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
WECHAT_APP_ID: stringValue(parsed.WECHAT_APP_ID),
|
|
56
|
+
WECHAT_APP_SECRET: stringValue(parsed.WECHAT_APP_SECRET)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function isConfiguredCredential(value) {
|
|
60
|
+
return Boolean(value && !value.startsWith("your_"));
|
|
61
|
+
}
|
|
62
|
+
function stringValue(value) {
|
|
63
|
+
if (typeof value !== "string") {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
const trimmed = value.trim();
|
|
67
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
68
|
+
}
|
|
69
|
+
function isRecord(value) {
|
|
70
|
+
return typeof value === "object" && value !== null;
|
|
71
|
+
}
|
|
11
72
|
//# sourceMappingURL=config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/config/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/config/config.ts"],"names":[],"mappings":"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"}
|
package/dist/src/mcp/tools.js
CHANGED
|
@@ -2,8 +2,6 @@ import { parseArticle } from "../core/article.js";
|
|
|
2
2
|
import { AppError, toAppError } from "../core/errors.js";
|
|
3
3
|
import { loadInput } from "../core/io.js";
|
|
4
4
|
import { loadConfig } from "../config/config.js";
|
|
5
|
-
import { generateImage } from "../images/generate.js";
|
|
6
|
-
import { createOpenAIClient } from "../openai/client.js";
|
|
7
5
|
import { buildPreviewDocument } from "../preview/preview.js";
|
|
8
6
|
import { renderArticle } from "../renderer/render.js";
|
|
9
7
|
import { listThemes, registerTheme, removeTheme, resolveTheme } from "../themes/registry.js";
|
|
@@ -19,20 +17,6 @@ export function listToolSchemas() {
|
|
|
19
17
|
description: "Render Markdown and write a local preview HTML document.",
|
|
20
18
|
inputSchema: articleInputSchema({ includeOutput: true })
|
|
21
19
|
},
|
|
22
|
-
{
|
|
23
|
-
name: "generate_image",
|
|
24
|
-
description: "Generate an image with OpenAI image generation and write it to disk.",
|
|
25
|
-
inputSchema: {
|
|
26
|
-
type: "object",
|
|
27
|
-
properties: {
|
|
28
|
-
prompt: { type: "string" },
|
|
29
|
-
article: { type: "string" },
|
|
30
|
-
output: { type: "string" },
|
|
31
|
-
size: { type: "string" },
|
|
32
|
-
quality: { type: "string" }
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
20
|
{
|
|
37
21
|
name: "list_themes",
|
|
38
22
|
description: "List built-in and custom themes.",
|
|
@@ -70,8 +54,6 @@ export async function handleToolCall(name, args = {}) {
|
|
|
70
54
|
return textResult(JSON.stringify(await convertArticle(args), null, 2));
|
|
71
55
|
case "preview_article":
|
|
72
56
|
return textResult(JSON.stringify(await previewArticle(args), null, 2));
|
|
73
|
-
case "generate_image":
|
|
74
|
-
return textResult(JSON.stringify(await generateImageTool(args), null, 2));
|
|
75
57
|
case "list_themes":
|
|
76
58
|
return textResult(JSON.stringify({ themes: await listThemes(themeOptions()) }, null, 2));
|
|
77
59
|
case "register_theme":
|
|
@@ -91,7 +73,6 @@ export async function handleToolCall(name, args = {}) {
|
|
|
91
73
|
}
|
|
92
74
|
}
|
|
93
75
|
async function convertArticle(args) {
|
|
94
|
-
const config = loadConfig();
|
|
95
76
|
const input = await loadInput({
|
|
96
77
|
content: optionalString(args, "content"),
|
|
97
78
|
file: optionalString(args, "file"),
|
|
@@ -99,21 +80,14 @@ async function convertArticle(args) {
|
|
|
99
80
|
});
|
|
100
81
|
const article = parseArticle(input.content);
|
|
101
82
|
const theme = await resolveTheme(optionalString(args, "theme_id") || "default", themeOptions());
|
|
102
|
-
const client = createOpenAIClient({
|
|
103
|
-
apiKey: requireOpenAIKey(config.openaiApiKey),
|
|
104
|
-
baseUrl: config.openaiBaseUrl,
|
|
105
|
-
textModel: config.openaiTextModel,
|
|
106
|
-
imageModel: config.openaiImageModel
|
|
107
|
-
});
|
|
108
83
|
const rendered = await renderArticle({
|
|
109
84
|
markdown: article.body,
|
|
110
85
|
metadata: article.metadata,
|
|
111
|
-
theme
|
|
112
|
-
client
|
|
86
|
+
theme
|
|
113
87
|
});
|
|
114
88
|
return {
|
|
115
89
|
html: rendered.html,
|
|
116
|
-
|
|
90
|
+
renderer: rendered.renderer,
|
|
117
91
|
theme_id: rendered.themeId,
|
|
118
92
|
metadata: article.metadata
|
|
119
93
|
};
|
|
@@ -128,23 +102,6 @@ async function previewArticle(args) {
|
|
|
128
102
|
preview_html: buildPreviewDocument(converted.html, isRecord(converted.metadata) ? converted.metadata : {})
|
|
129
103
|
};
|
|
130
104
|
}
|
|
131
|
-
async function generateImageTool(args) {
|
|
132
|
-
const config = loadConfig();
|
|
133
|
-
const client = createOpenAIClient({
|
|
134
|
-
apiKey: requireOpenAIKey(config.openaiApiKey),
|
|
135
|
-
baseUrl: config.openaiBaseUrl,
|
|
136
|
-
textModel: config.openaiTextModel,
|
|
137
|
-
imageModel: config.openaiImageModel
|
|
138
|
-
});
|
|
139
|
-
return generateImage({
|
|
140
|
-
prompt: optionalString(args, "prompt") || "WeChat article cover image",
|
|
141
|
-
articleContext: optionalString(args, "article"),
|
|
142
|
-
output: optionalString(args, "output") || "image.png",
|
|
143
|
-
size: optionalString(args, "size"),
|
|
144
|
-
quality: optionalString(args, "quality"),
|
|
145
|
-
client
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
105
|
function articleInputSchema(options) {
|
|
149
106
|
return {
|
|
150
107
|
type: "object",
|
|
@@ -171,12 +128,6 @@ function requiredString(args, key) {
|
|
|
171
128
|
}
|
|
172
129
|
return value;
|
|
173
130
|
}
|
|
174
|
-
function requireOpenAIKey(value) {
|
|
175
|
-
if (!value) {
|
|
176
|
-
throw new AppError("OPENAI_KEY_MISSING", "OPENAI_API_KEY is required");
|
|
177
|
-
}
|
|
178
|
-
return value;
|
|
179
|
-
}
|
|
180
131
|
function themeOptions() {
|
|
181
132
|
const config = loadConfig();
|
|
182
133
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../src/mcp/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ArticleMetadata } from "../core/article.js";
|
|
2
|
-
import type { OpenAIClient } from "../openai/client.js";
|
|
3
2
|
export interface RenderTheme {
|
|
4
3
|
id: string;
|
|
5
4
|
name: string;
|
|
@@ -9,11 +8,10 @@ export interface RenderArticleInput {
|
|
|
9
8
|
markdown: string;
|
|
10
9
|
metadata?: ArticleMetadata;
|
|
11
10
|
theme: RenderTheme;
|
|
12
|
-
client: OpenAIClient;
|
|
13
11
|
}
|
|
14
12
|
export interface RenderedArticle {
|
|
15
13
|
html: string;
|
|
16
|
-
|
|
14
|
+
renderer: "local";
|
|
17
15
|
themeId: string;
|
|
18
16
|
}
|
|
19
17
|
export declare function renderArticle(input: RenderArticleInput): Promise<RenderedArticle>;
|
|
@@ -1,36 +1,159 @@
|
|
|
1
|
-
import { sanitizeWechatHtml } from "../core/html.js";
|
|
1
|
+
import { escapeHtml, sanitizeWechatHtml } from "../core/html.js";
|
|
2
2
|
export async function renderArticle(input) {
|
|
3
|
-
const model = input.client.textModel;
|
|
4
|
-
const response = await input.client.responsesCreate({
|
|
5
|
-
model,
|
|
6
|
-
input: buildRenderPrompt(input)
|
|
7
|
-
});
|
|
8
3
|
return {
|
|
9
|
-
html: sanitizeWechatHtml(
|
|
10
|
-
|
|
4
|
+
html: sanitizeWechatHtml(renderMarkdown(input.markdown, stylesFor(input.theme.id))),
|
|
5
|
+
renderer: "local",
|
|
11
6
|
themeId: input.theme.id
|
|
12
7
|
};
|
|
13
8
|
}
|
|
14
|
-
function
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
9
|
+
function renderMarkdown(markdown, styles) {
|
|
10
|
+
const lines = markdown.replace(/\r\n/g, "\n").split("\n");
|
|
11
|
+
const html = [`<section style="${styles.container}">`];
|
|
12
|
+
const paragraph = [];
|
|
13
|
+
let listType;
|
|
14
|
+
let inCodeBlock = false;
|
|
15
|
+
let codeLines = [];
|
|
16
|
+
const flushParagraph = () => {
|
|
17
|
+
if (paragraph.length === 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
html.push(`<p style="${styles.paragraph}">${renderInline(paragraph.join(" "), styles)}</p>`);
|
|
21
|
+
paragraph.length = 0;
|
|
22
|
+
};
|
|
23
|
+
const closeList = () => {
|
|
24
|
+
if (!listType) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
html.push(`</${listType}>`);
|
|
28
|
+
listType = undefined;
|
|
29
|
+
};
|
|
30
|
+
const openList = (type) => {
|
|
31
|
+
if (listType === type) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
closeList();
|
|
35
|
+
html.push(`<${type} style="${styles.list}">`);
|
|
36
|
+
listType = type;
|
|
37
|
+
};
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
const trimmed = line.trim();
|
|
40
|
+
if (trimmed.startsWith("```")) {
|
|
41
|
+
flushParagraph();
|
|
42
|
+
closeList();
|
|
43
|
+
if (inCodeBlock) {
|
|
44
|
+
html.push(`<pre style="${styles.codeBlock}"><code>${escapeHtml(codeLines.join("\n"))}</code></pre>`);
|
|
45
|
+
codeLines = [];
|
|
46
|
+
inCodeBlock = false;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
inCodeBlock = true;
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (inCodeBlock) {
|
|
54
|
+
codeLines.push(line);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (!trimmed) {
|
|
58
|
+
flushParagraph();
|
|
59
|
+
closeList();
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const heading = /^(#{1,6})\s+(.+)$/.exec(trimmed);
|
|
63
|
+
if (heading) {
|
|
64
|
+
flushParagraph();
|
|
65
|
+
closeList();
|
|
66
|
+
const level = Math.min(heading[1].length, 3);
|
|
67
|
+
const style = level === 1 ? styles.heading1 : level === 2 ? styles.heading2 : styles.heading3;
|
|
68
|
+
html.push(`<h${level} style="${style}">${renderInline(heading[2], styles)}</h${level}>`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (/^([-*_])\s*(\1\s*){2,}$/.test(trimmed)) {
|
|
72
|
+
flushParagraph();
|
|
73
|
+
closeList();
|
|
74
|
+
html.push(`<hr style="border:0;border-top:1px solid #e5e7eb;margin:24px 0;" />`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const quote = /^>\s?(.+)$/.exec(line);
|
|
78
|
+
if (quote) {
|
|
79
|
+
flushParagraph();
|
|
80
|
+
closeList();
|
|
81
|
+
html.push(`<blockquote style="${styles.quote}">${renderInline(quote[1], styles)}</blockquote>`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const unordered = /^\s*[-*+]\s+(.+)$/.exec(line);
|
|
85
|
+
if (unordered) {
|
|
86
|
+
flushParagraph();
|
|
87
|
+
openList("ul");
|
|
88
|
+
html.push(`<li style="${styles.listItem}">${renderInline(unordered[1], styles)}</li>`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const ordered = /^\s*\d+\.\s+(.+)$/.exec(line);
|
|
92
|
+
if (ordered) {
|
|
93
|
+
flushParagraph();
|
|
94
|
+
openList("ol");
|
|
95
|
+
html.push(`<li style="${styles.listItem}">${renderInline(ordered[1], styles)}</li>`);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
paragraph.push(trimmed);
|
|
99
|
+
}
|
|
100
|
+
if (inCodeBlock) {
|
|
101
|
+
html.push(`<pre style="${styles.codeBlock}"><code>${escapeHtml(codeLines.join("\n"))}</code></pre>`);
|
|
102
|
+
}
|
|
103
|
+
flushParagraph();
|
|
104
|
+
closeList();
|
|
105
|
+
html.push("</section>");
|
|
106
|
+
return html.join("\n");
|
|
107
|
+
}
|
|
108
|
+
function renderInline(value, styles) {
|
|
109
|
+
let html = escapeHtml(value);
|
|
110
|
+
html = html.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^&]*")?\)/g, (_match, alt, src) => {
|
|
111
|
+
return `<img src="${src}" alt="${alt}" style="${styles.image}" />`;
|
|
112
|
+
});
|
|
113
|
+
html = html.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^&]*")?\)/g, (_match, label, href) => {
|
|
114
|
+
return `<a href="${href}" style="${styles.link}">${label}</a>`;
|
|
115
|
+
});
|
|
116
|
+
html = html.replace(/`([^`]+)`/g, `<code style="${styles.inlineCode}">$1</code>`);
|
|
117
|
+
html = html.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
118
|
+
html = html.replace(/__([^_]+)__/g, "<strong>$1</strong>");
|
|
119
|
+
html = html.replace(/\*([^*]+)\*/g, "<em>$1</em>");
|
|
120
|
+
html = html.replace(/_([^_]+)_/g, "<em>$1</em>");
|
|
121
|
+
return html;
|
|
122
|
+
}
|
|
123
|
+
function stylesFor(themeId) {
|
|
124
|
+
if (themeId === "minimal") {
|
|
125
|
+
return {
|
|
126
|
+
...baseStyles(),
|
|
127
|
+
container: "font-size:15px;line-height:1.75;color:#2f3437;",
|
|
128
|
+
heading1: "font-size:22px;line-height:1.35;font-weight:700;margin:0 0 18px;color:#111827;",
|
|
129
|
+
heading2: "font-size:18px;line-height:1.45;font-weight:700;margin:26px 0 12px;color:#111827;",
|
|
130
|
+
quote: "margin:18px 0;padding:10px 14px;border-left:3px solid #9ca3af;color:#4b5563;background:#f9fafb;"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (themeId === "editorial") {
|
|
134
|
+
return {
|
|
135
|
+
...baseStyles(),
|
|
136
|
+
heading1: "font-size:26px;line-height:1.3;font-weight:700;margin:0 0 20px;color:#111827;",
|
|
137
|
+
heading2: "font-size:20px;line-height:1.45;font-weight:700;margin:30px 0 14px;color:#7c2d12;",
|
|
138
|
+
quote: "margin:22px 0;padding:14px 18px;border-left:4px solid #c2410c;color:#431407;background:#fff7ed;font-weight:600;"
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return baseStyles();
|
|
142
|
+
}
|
|
143
|
+
function baseStyles() {
|
|
144
|
+
return {
|
|
145
|
+
container: "font-size:16px;line-height:1.8;color:#1f2937;",
|
|
146
|
+
heading1: "font-size:24px;line-height:1.35;font-weight:700;margin:0 0 20px;color:#111827;",
|
|
147
|
+
heading2: "font-size:20px;line-height:1.45;font-weight:700;margin:28px 0 14px;color:#111827;",
|
|
148
|
+
heading3: "font-size:17px;line-height:1.5;font-weight:700;margin:22px 0 10px;color:#111827;",
|
|
149
|
+
paragraph: "margin:0 0 16px;",
|
|
150
|
+
list: "margin:0 0 16px;padding-left:22px;",
|
|
151
|
+
listItem: "margin:0 0 8px;",
|
|
152
|
+
quote: "margin:20px 0;padding:12px 16px;border-left:4px solid #2563eb;color:#374151;background:#f8fafc;",
|
|
153
|
+
codeBlock: "margin:18px 0;padding:12px;overflow:auto;background:#f3f4f6;border-radius:4px;font-size:13px;line-height:1.6;",
|
|
154
|
+
inlineCode: "font-family:monospace;background:#f3f4f6;border-radius:3px;padding:1px 4px;",
|
|
155
|
+
image: "max-width:100%;height:auto;display:block;margin:16px auto;",
|
|
156
|
+
link: "color:#2563eb;text-decoration:none;"
|
|
157
|
+
};
|
|
35
158
|
}
|
|
36
159
|
//# sourceMappingURL=render.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/renderer/render.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/renderer/render.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gangtiser/md2wechat",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "TypeScript md2wechat CLI, Skill, and MCP server
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "TypeScript md2wechat CLI, Skill, and MCP server for local Markdown to WeChat HTML conversion.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wechat",
|
|
7
7
|
"markdown",
|
|
8
8
|
"mcp",
|
|
9
9
|
"codex",
|
|
10
|
-
"cli"
|
|
11
|
-
"openai"
|
|
10
|
+
"cli"
|
|
12
11
|
],
|
|
13
12
|
"author": "Gangtiser",
|
|
14
13
|
"license": "SEE LICENSE IN LICENSE",
|
|
15
14
|
"type": "module",
|
|
16
15
|
"bin": {
|
|
17
|
-
"md2wechat": "
|
|
16
|
+
"md2wechat": "dist/src/cli/index.js"
|
|
18
17
|
},
|
|
19
18
|
"repository": {
|
|
20
19
|
"type": "git",
|
|
@@ -31,6 +30,8 @@
|
|
|
31
30
|
"LICENSE"
|
|
32
31
|
],
|
|
33
32
|
"scripts": {
|
|
33
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
34
|
+
"prebuild": "npm run clean",
|
|
34
35
|
"build": "tsc",
|
|
35
36
|
"typecheck": "tsc --noEmit",
|
|
36
37
|
"test": "npm run build && node --test \"dist/test/**/*.test.js\"",
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: md2wechat
|
|
3
|
-
description: Convert Markdown to WeChat Official Account HTML,
|
|
3
|
+
description: Convert Markdown to WeChat Official Account HTML, manage themes, and use the md2wechat MCP server.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# md2wechat Skill
|
|
7
7
|
|
|
8
|
-
Use this skill when the user wants to turn Markdown into WeChat Official Account content, preview an article,
|
|
8
|
+
Use this skill when the user wants to turn Markdown into WeChat Official Account content, preview an article, manage themes, or expose the workflow through MCP.
|
|
9
9
|
|
|
10
|
-
## Defaults
|
|
10
|
+
## Defaults & Agent Usage
|
|
11
11
|
|
|
12
|
-
- Text rendering model: `gpt-5.5`
|
|
13
|
-
- Image generation model: `gpt-image-2`
|
|
14
12
|
- Conversion is local through the CLI or MCP server.
|
|
13
|
+
- Use Codex or Claude to write or revise Markdown before calling md2wechat.
|
|
14
|
+
- Customers configure WeChat credentials with `md2wechat config init` or environment variables.
|
|
15
15
|
|
|
16
16
|
## Workflow
|
|
17
17
|
|
|
18
18
|
1. Run `md2wechat inspect <file> --json`.
|
|
19
|
-
2. Run `md2wechat
|
|
20
|
-
3. Run `md2wechat
|
|
21
|
-
4. Run `md2wechat
|
|
22
|
-
5. Run `md2wechat
|
|
19
|
+
2. Run `md2wechat config status --json` if the workflow needs WeChat account credentials.
|
|
20
|
+
3. Run `md2wechat themes list --json` before choosing a theme.
|
|
21
|
+
4. Run `md2wechat convert <file> --theme <theme> --output <file>.html --json`.
|
|
22
|
+
5. Run `md2wechat preview <file> --theme <theme> --output preview.html --json` when the user wants a local confirmation page.
|
|
23
23
|
|
|
24
24
|
## Theme Management
|
|
25
25
|
|
|
@@ -39,7 +39,6 @@ Available tools:
|
|
|
39
39
|
|
|
40
40
|
- `convert_article`
|
|
41
41
|
- `preview_article`
|
|
42
|
-
- `generate_image`
|
|
43
42
|
- `list_themes`
|
|
44
43
|
- `register_theme`
|
|
45
44
|
- `remove_theme`
|
|
@@ -49,4 +48,5 @@ Available tools:
|
|
|
49
48
|
- Prefer file paths over large inline article content.
|
|
50
49
|
- Do not assume a theme exists; list themes first.
|
|
51
50
|
- Keep generated HTML and preview files on disk unless the user asks for stdout.
|
|
52
|
-
-
|
|
51
|
+
- Do not ask customers for model names or model API keys; the host agent handles AI authoring.
|
|
52
|
+
- Use `WECHAT_APP_ID` and `WECHAT_APP_SECRET` when WeChat account credentials are needed.
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { OpenAIClient } from "../openai/client.js";
|
|
2
|
-
export interface GenerateImageInput {
|
|
3
|
-
prompt: string;
|
|
4
|
-
articleContext?: string;
|
|
5
|
-
output: string;
|
|
6
|
-
size?: string;
|
|
7
|
-
quality?: string;
|
|
8
|
-
client: OpenAIClient;
|
|
9
|
-
}
|
|
10
|
-
export interface GeneratedImage {
|
|
11
|
-
outputPath: string;
|
|
12
|
-
model: string;
|
|
13
|
-
size?: string;
|
|
14
|
-
quality?: string;
|
|
15
|
-
revisedPrompt?: string;
|
|
16
|
-
}
|
|
17
|
-
export declare function generateImage(input: GenerateImageInput): Promise<GeneratedImage>;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
export async function generateImage(input) {
|
|
4
|
-
const model = input.client.imageModel;
|
|
5
|
-
const image = await input.client.imagesGenerate({
|
|
6
|
-
model,
|
|
7
|
-
prompt: finalPrompt(input.prompt, input.articleContext),
|
|
8
|
-
size: input.size,
|
|
9
|
-
quality: input.quality
|
|
10
|
-
});
|
|
11
|
-
const outputPath = path.resolve(input.output);
|
|
12
|
-
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
13
|
-
await writeFile(outputPath, Buffer.from(image.b64Json, "base64"));
|
|
14
|
-
return {
|
|
15
|
-
outputPath,
|
|
16
|
-
model: image.model,
|
|
17
|
-
size: input.size,
|
|
18
|
-
quality: input.quality,
|
|
19
|
-
revisedPrompt: image.revisedPrompt
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
function finalPrompt(prompt, articleContext) {
|
|
23
|
-
if (!articleContext?.trim()) {
|
|
24
|
-
return prompt;
|
|
25
|
-
}
|
|
26
|
-
return `${prompt}\n\nArticle context:\n${articleContext}`;
|
|
27
|
-
}
|
|
28
|
-
//# sourceMappingURL=generate.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/images/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,IAAI,MAAM,WAAW,CAAC;AAoB7B,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAyB;IAC3D,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;IACtC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC;QAC9C,KAAK;QACL,MAAM,EAAE,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC;QACvD,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAE9C,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,MAAM,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAElE,OAAO;QACL,UAAU;QACV,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,aAAa,EAAE,KAAK,CAAC,aAAa;KACnC,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,MAAc,EAAE,cAAkC;IACrE,IAAI,CAAC,cAAc,EAAE,IAAI,EAAE,EAAE,CAAC;QAC5B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,GAAG,MAAM,yBAAyB,cAAc,EAAE,CAAC;AAC5D,CAAC"}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export interface OpenAIClientOptions {
|
|
2
|
-
apiKey: string;
|
|
3
|
-
baseUrl?: string;
|
|
4
|
-
textModel?: string;
|
|
5
|
-
imageModel?: string;
|
|
6
|
-
}
|
|
7
|
-
export interface ResponsesCreateInput {
|
|
8
|
-
model?: string;
|
|
9
|
-
input: string;
|
|
10
|
-
}
|
|
11
|
-
export interface ResponsesCreateResult {
|
|
12
|
-
model: string;
|
|
13
|
-
outputText: string;
|
|
14
|
-
raw: unknown;
|
|
15
|
-
}
|
|
16
|
-
export interface ImagesGenerateInput {
|
|
17
|
-
model?: string;
|
|
18
|
-
prompt: string;
|
|
19
|
-
size?: string;
|
|
20
|
-
quality?: string;
|
|
21
|
-
}
|
|
22
|
-
export interface ImagesGenerateResult {
|
|
23
|
-
model: string;
|
|
24
|
-
b64Json: string;
|
|
25
|
-
revisedPrompt?: string;
|
|
26
|
-
raw: unknown;
|
|
27
|
-
}
|
|
28
|
-
export interface OpenAIClient {
|
|
29
|
-
textModel: string;
|
|
30
|
-
imageModel: string;
|
|
31
|
-
responsesCreate(input: ResponsesCreateInput): Promise<ResponsesCreateResult>;
|
|
32
|
-
imagesGenerate(input: ImagesGenerateInput): Promise<ImagesGenerateResult>;
|
|
33
|
-
}
|
|
34
|
-
export declare function createOpenAIClient(options: OpenAIClientOptions): OpenAIClient;
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
const defaultBaseUrl = "https://api.openai.com/v1";
|
|
2
|
-
const defaultTextModel = "gpt-5.5";
|
|
3
|
-
const defaultImageModel = "gpt-image-2";
|
|
4
|
-
export function createOpenAIClient(options) {
|
|
5
|
-
const baseUrl = (options.baseUrl || defaultBaseUrl).replace(/\/+$/, "");
|
|
6
|
-
const textModel = options.textModel || defaultTextModel;
|
|
7
|
-
const imageModel = options.imageModel || defaultImageModel;
|
|
8
|
-
return {
|
|
9
|
-
textModel,
|
|
10
|
-
imageModel,
|
|
11
|
-
async responsesCreate(input) {
|
|
12
|
-
const model = input.model || textModel;
|
|
13
|
-
const json = await postJson(`${baseUrl}/responses`, options.apiKey, {
|
|
14
|
-
model,
|
|
15
|
-
input: input.input
|
|
16
|
-
});
|
|
17
|
-
return {
|
|
18
|
-
model,
|
|
19
|
-
outputText: extractOutputText(json),
|
|
20
|
-
raw: json
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
async imagesGenerate(input) {
|
|
24
|
-
const model = input.model || imageModel;
|
|
25
|
-
const json = await postJson(`${baseUrl}/images/generations`, options.apiKey, {
|
|
26
|
-
model,
|
|
27
|
-
prompt: input.prompt,
|
|
28
|
-
...(input.size ? { size: input.size } : {}),
|
|
29
|
-
...(input.quality ? { quality: input.quality } : {})
|
|
30
|
-
});
|
|
31
|
-
const firstImage = extractFirstImage(json);
|
|
32
|
-
return {
|
|
33
|
-
model,
|
|
34
|
-
b64Json: firstImage.b64Json,
|
|
35
|
-
revisedPrompt: firstImage.revisedPrompt,
|
|
36
|
-
raw: json
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
async function postJson(url, apiKey, body) {
|
|
42
|
-
const response = await fetch(url, {
|
|
43
|
-
method: "POST",
|
|
44
|
-
headers: {
|
|
45
|
-
authorization: `Bearer ${apiKey}`,
|
|
46
|
-
"content-type": "application/json"
|
|
47
|
-
},
|
|
48
|
-
body: JSON.stringify(body),
|
|
49
|
-
signal: AbortSignal.timeout(120000)
|
|
50
|
-
});
|
|
51
|
-
const json = await response.json().catch(() => undefined);
|
|
52
|
-
if (!response.ok) {
|
|
53
|
-
throw new Error(openAIErrorMessage(json, response.status));
|
|
54
|
-
}
|
|
55
|
-
return json;
|
|
56
|
-
}
|
|
57
|
-
function extractOutputText(json) {
|
|
58
|
-
if (isRecord(json) && typeof json.output_text === "string") {
|
|
59
|
-
return json.output_text;
|
|
60
|
-
}
|
|
61
|
-
if (isRecord(json) && Array.isArray(json.output)) {
|
|
62
|
-
const parts = [];
|
|
63
|
-
for (const item of json.output) {
|
|
64
|
-
if (!isRecord(item) || !Array.isArray(item.content)) {
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
for (const content of item.content) {
|
|
68
|
-
if (isRecord(content) && typeof content.text === "string") {
|
|
69
|
-
parts.push(content.text);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (parts.length > 0) {
|
|
74
|
-
return parts.join("");
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
throw new Error("OpenAI response did not include output text");
|
|
78
|
-
}
|
|
79
|
-
function extractFirstImage(json) {
|
|
80
|
-
if (!isRecord(json) || !Array.isArray(json.data) || json.data.length === 0) {
|
|
81
|
-
throw new Error("OpenAI image response did not include image data");
|
|
82
|
-
}
|
|
83
|
-
const first = json.data[0];
|
|
84
|
-
if (!isRecord(first) || typeof first.b64_json !== "string") {
|
|
85
|
-
throw new Error("OpenAI image response did not include b64_json");
|
|
86
|
-
}
|
|
87
|
-
return {
|
|
88
|
-
b64Json: first.b64_json,
|
|
89
|
-
revisedPrompt: typeof first.revised_prompt === "string" ? first.revised_prompt : undefined
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
function openAIErrorMessage(json, status) {
|
|
93
|
-
if (isRecord(json) && isRecord(json.error) && typeof json.error.message === "string") {
|
|
94
|
-
return `OpenAI request failed (${status}): ${json.error.message}`;
|
|
95
|
-
}
|
|
96
|
-
return `OpenAI request failed (${status})`;
|
|
97
|
-
}
|
|
98
|
-
function isRecord(value) {
|
|
99
|
-
return typeof value === "object" && value !== null;
|
|
100
|
-
}
|
|
101
|
-
//# sourceMappingURL=client.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/openai/client.ts"],"names":[],"mappings":"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"}
|