@astro-minimax/cli 0.5.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/README.md +69 -0
  2. package/dist/commands/ai.d.ts +2 -0
  3. package/dist/commands/ai.d.ts.map +1 -0
  4. package/dist/commands/ai.js +99 -0
  5. package/dist/commands/ai.js.map +1 -0
  6. package/dist/commands/data.d.ts +2 -0
  7. package/dist/commands/data.d.ts.map +1 -0
  8. package/dist/commands/data.js +111 -0
  9. package/dist/commands/data.js.map +1 -0
  10. package/dist/commands/hooks.d.ts +2 -0
  11. package/dist/commands/hooks.d.ts.map +1 -0
  12. package/dist/commands/hooks.js +378 -0
  13. package/dist/commands/hooks.js.map +1 -0
  14. package/dist/commands/init.d.ts +2 -0
  15. package/dist/commands/init.d.ts.map +1 -0
  16. package/dist/commands/init.js +50 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/podcast.d.ts +2 -0
  19. package/dist/commands/podcast.d.ts.map +1 -0
  20. package/dist/commands/podcast.js +89 -0
  21. package/dist/commands/podcast.js.map +1 -0
  22. package/dist/commands/post.d.ts +2 -0
  23. package/dist/commands/post.d.ts.map +1 -0
  24. package/dist/commands/post.js +190 -0
  25. package/dist/commands/post.js.map +1 -0
  26. package/dist/commands/profile.d.ts +2 -0
  27. package/dist/commands/profile.d.ts.map +1 -0
  28. package/dist/commands/profile.js +88 -0
  29. package/dist/commands/profile.js.map +1 -0
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +81 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/tools/ai-process.d.ts +20 -0
  35. package/dist/tools/ai-process.d.ts.map +1 -0
  36. package/dist/tools/ai-process.js +607 -0
  37. package/dist/tools/ai-process.js.map +1 -0
  38. package/dist/tools/build-author-context.d.ts +13 -0
  39. package/dist/tools/build-author-context.d.ts.map +1 -0
  40. package/dist/tools/build-author-context.js +313 -0
  41. package/dist/tools/build-author-context.js.map +1 -0
  42. package/dist/tools/build-voice-profile.d.ts +12 -0
  43. package/dist/tools/build-voice-profile.d.ts.map +1 -0
  44. package/dist/tools/build-voice-profile.js +270 -0
  45. package/dist/tools/build-voice-profile.js.map +1 -0
  46. package/dist/tools/eval-ai-chat.d.ts +17 -0
  47. package/dist/tools/eval-ai-chat.d.ts.map +1 -0
  48. package/dist/tools/eval-ai-chat.js +362 -0
  49. package/dist/tools/eval-ai-chat.js.map +1 -0
  50. package/dist/tools/generate-author-profile.d.ts +14 -0
  51. package/dist/tools/generate-author-profile.d.ts.map +1 -0
  52. package/dist/tools/generate-author-profile.js +289 -0
  53. package/dist/tools/generate-author-profile.js.map +1 -0
  54. package/dist/tools/generate-cover.d.ts +14 -0
  55. package/dist/tools/generate-cover.d.ts.map +1 -0
  56. package/dist/tools/generate-cover.js +95 -0
  57. package/dist/tools/generate-cover.js.map +1 -0
  58. package/dist/tools/generate-og.d.ts +3 -0
  59. package/dist/tools/generate-og.d.ts.map +1 -0
  60. package/dist/tools/generate-og.js +254 -0
  61. package/dist/tools/generate-og.js.map +1 -0
  62. package/dist/tools/generate-related.d.ts +11 -0
  63. package/dist/tools/generate-related.d.ts.map +1 -0
  64. package/dist/tools/generate-related.js +124 -0
  65. package/dist/tools/generate-related.js.map +1 -0
  66. package/dist/tools/generate-tags.d.ts +14 -0
  67. package/dist/tools/generate-tags.d.ts.map +1 -0
  68. package/dist/tools/generate-tags.js +182 -0
  69. package/dist/tools/generate-tags.js.map +1 -0
  70. package/dist/tools/lib/ai-provider.d.ts +43 -0
  71. package/dist/tools/lib/ai-provider.d.ts.map +1 -0
  72. package/dist/tools/lib/ai-provider.js +146 -0
  73. package/dist/tools/lib/ai-provider.js.map +1 -0
  74. package/dist/tools/lib/audio-processor.d.ts +46 -0
  75. package/dist/tools/lib/audio-processor.d.ts.map +1 -0
  76. package/dist/tools/lib/audio-processor.js +188 -0
  77. package/dist/tools/lib/audio-processor.js.map +1 -0
  78. package/dist/tools/lib/frontmatter.d.ts +11 -0
  79. package/dist/tools/lib/frontmatter.d.ts.map +1 -0
  80. package/dist/tools/lib/frontmatter.js +80 -0
  81. package/dist/tools/lib/frontmatter.js.map +1 -0
  82. package/dist/tools/lib/index.d.ts +7 -0
  83. package/dist/tools/lib/index.d.ts.map +1 -0
  84. package/{template/tools/lib/index.ts → dist/tools/lib/index.js} +1 -0
  85. package/dist/tools/lib/index.js.map +1 -0
  86. package/dist/tools/lib/markdown.d.ts +6 -0
  87. package/dist/tools/lib/markdown.d.ts.map +1 -0
  88. package/dist/tools/lib/markdown.js +34 -0
  89. package/dist/tools/lib/markdown.js.map +1 -0
  90. package/dist/tools/lib/posts.d.ts +25 -0
  91. package/dist/tools/lib/posts.d.ts.map +1 -0
  92. package/dist/tools/lib/posts.js +63 -0
  93. package/dist/tools/lib/posts.js.map +1 -0
  94. package/dist/tools/lib/script-generator.d.ts +61 -0
  95. package/dist/tools/lib/script-generator.d.ts.map +1 -0
  96. package/dist/tools/lib/script-generator.js +182 -0
  97. package/dist/tools/lib/script-generator.js.map +1 -0
  98. package/dist/tools/lib/tts-provider.d.ts +65 -0
  99. package/dist/tools/lib/tts-provider.d.ts.map +1 -0
  100. package/dist/tools/lib/tts-provider.js +116 -0
  101. package/dist/tools/lib/tts-provider.js.map +1 -0
  102. package/dist/tools/lib/types.d.ts +129 -0
  103. package/dist/tools/lib/types.d.ts.map +1 -0
  104. package/dist/tools/lib/types.js +64 -0
  105. package/dist/tools/lib/types.js.map +1 -0
  106. package/dist/tools/lib/utils.d.ts +18 -0
  107. package/dist/tools/lib/utils.d.ts.map +1 -0
  108. package/dist/tools/lib/utils.js +121 -0
  109. package/dist/tools/lib/utils.js.map +1 -0
  110. package/dist/tools/lib/vectors.d.ts +27 -0
  111. package/dist/tools/lib/vectors.d.ts.map +1 -0
  112. package/dist/tools/lib/vectors.js +64 -0
  113. package/dist/tools/lib/vectors.js.map +1 -0
  114. package/dist/tools/podcast-feed.d.ts +6 -0
  115. package/dist/tools/podcast-feed.d.ts.map +1 -0
  116. package/dist/tools/podcast-feed.js +121 -0
  117. package/dist/tools/podcast-feed.js.map +1 -0
  118. package/dist/tools/podcast-generate.d.ts +15 -0
  119. package/dist/tools/podcast-generate.d.ts.map +1 -0
  120. package/dist/tools/podcast-generate.js +318 -0
  121. package/dist/tools/podcast-generate.js.map +1 -0
  122. package/dist/tools/podcast-list.d.ts +6 -0
  123. package/dist/tools/podcast-list.d.ts.map +1 -0
  124. package/dist/tools/podcast-list.js +66 -0
  125. package/dist/tools/podcast-list.js.map +1 -0
  126. package/dist/tools/summarize.d.ts +16 -0
  127. package/dist/tools/summarize.d.ts.map +1 -0
  128. package/dist/tools/summarize.js +108 -0
  129. package/dist/tools/summarize.js.map +1 -0
  130. package/dist/tools/translate.d.ts +13 -0
  131. package/dist/tools/translate.d.ts.map +1 -0
  132. package/dist/tools/translate.js +46 -0
  133. package/dist/tools/translate.js.map +1 -0
  134. package/dist/tools/vectorize.d.ts +13 -0
  135. package/dist/tools/vectorize.d.ts.map +1 -0
  136. package/dist/tools/vectorize.js +87 -0
  137. package/dist/tools/vectorize.js.map +1 -0
  138. package/package.json +14 -9
  139. package/template/astro.config.ts +8 -28
  140. package/template/datas/ai-seo.json +8 -0
  141. package/template/datas/ai-skip-list.json +1 -0
  142. package/template/datas/author-profile-context.json +21 -0
  143. package/template/datas/author-profile-report.json +21 -0
  144. package/template/datas/eval/gold-set.json +72 -0
  145. package/template/functions/README.md +82 -0
  146. package/template/functions/api/ai-info.ts +2 -2
  147. package/template/functions/api/chat.ts +4 -1
  148. package/template/functions/api/notify/comment.ts +140 -68
  149. package/template/functions/api/notify/debug.ts +41 -0
  150. package/template/functions/api/notify/status.ts +97 -0
  151. package/template/functions/api/notify/test-ai-chat.ts +67 -0
  152. package/template/package.json +22 -25
  153. package/template/src/config.ts +11 -0
  154. package/template/src/content.config.ts +29 -16
  155. package/template/src/env.d.ts +0 -5
  156. package/index.js +0 -36
  157. package/template/tools/README.md +0 -169
  158. package/template/tools/ai-process.ts +0 -816
  159. package/template/tools/build-author-context.ts +0 -405
  160. package/template/tools/build-voice-profile.ts +0 -322
  161. package/template/tools/generate-author-profile.ts +0 -369
  162. package/template/tools/generate-cover.ts +0 -123
  163. package/template/tools/generate-og.ts +0 -280
  164. package/template/tools/generate-related.ts +0 -146
  165. package/template/tools/generate-tags.ts +0 -251
  166. package/template/tools/lib/ai-provider.ts +0 -240
  167. package/template/tools/lib/frontmatter.ts +0 -94
  168. package/template/tools/lib/markdown.ts +0 -40
  169. package/template/tools/lib/posts.ts +0 -89
  170. package/template/tools/lib/utils.ts +0 -138
  171. package/template/tools/lib/vectors.ts +0 -96
  172. package/template/tools/summarize.ts +0 -142
  173. package/template/tools/translate.ts +0 -60
  174. package/template/tools/vectorize.ts +0 -105
@@ -0,0 +1,289 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * 生成作者画像报告
4
+ *
5
+ * 基于作者上下文数据生成用于 About 页面的结构化简介。
6
+ * 支持 AI 生成和规则模板两种模式。
7
+ *
8
+ * 用法:
9
+ * pnpm profile:generate AI 生成画像报告
10
+ * pnpm profile:generate --no-ai 使用规则模板
11
+ * pnpm profile:generate --force 强制重新生成(不回退)
12
+ */
13
+ import { readdir, readFile } from "node:fs/promises";
14
+ import { join } from "node:path";
15
+ import { loadEnv, readJson, writeJson, truncate, parseCliArgs, DATA_DIR, BLOG_DIR, } from "./lib/utils.js";
16
+ import { stripMarkdown } from "./lib/markdown.js";
17
+ import { extractFrontmatter } from "./lib/frontmatter.js";
18
+ import { chatCompletion, hasAPIKey, getConfig } from "./lib/ai-provider.js";
19
+ // ─── 常量 ─────────────────────────────────────────────────────
20
+ const OUTPUT_REPORT = join(DATA_DIR, "author-profile-report.json");
21
+ const OUTPUT_CONTEXT = join(DATA_DIR, "author-profile-context.json");
22
+ const DEFAULT_SITE_URL = "https://example.com";
23
+ function parseArgs() {
24
+ return parseCliArgs({ force: false, noAI: false });
25
+ }
26
+ async function collectPosts(_siteUrl) {
27
+ const entries = await readdir(BLOG_DIR, { withFileTypes: true });
28
+ const aiSummaries = await readJson(join(DATA_DIR, "ai-summaries.json"), {
29
+ articles: {},
30
+ });
31
+ const posts = [];
32
+ for (const entry of entries) {
33
+ if (!entry.isDirectory() || entry.name.startsWith("_"))
34
+ continue;
35
+ const subDir = join(BLOG_DIR, entry.name);
36
+ const subEntries = await readdir(subDir, { withFileTypes: true });
37
+ for (const subEntry of subEntries) {
38
+ if (!subEntry.isFile() || !subEntry.name.endsWith(".md"))
39
+ continue;
40
+ const filePath = join(subDir, subEntry.name);
41
+ const raw = await readFile(filePath, "utf-8");
42
+ const fm = extractFrontmatter(raw);
43
+ if (!fm.data.title || fm.data.draft)
44
+ continue;
45
+ const relativePath = filePath.replace(BLOG_DIR + "/", "");
46
+ const id = relativePath.replace(/\.md$/, "");
47
+ const lang = relativePath.startsWith("en/") ? "en" : "zh";
48
+ const slug = id.split("/").slice(1).join("/");
49
+ const summaryEntry = aiSummaries.articles?.[id]?.data;
50
+ posts.push({
51
+ title: String(fm.data.title),
52
+ date: String(fm.data.pubDatetime),
53
+ lang,
54
+ category: String(fm.data.category || ""),
55
+ tags: Array.isArray(fm.data.tags) ? fm.data.tags : [],
56
+ description: String(fm.data.description || ""),
57
+ summary: summaryEntry?.summary || truncate(stripMarkdown(fm.body), 100),
58
+ keyPoints: summaryEntry?.keyPoints || [],
59
+ url: `/${lang}/posts/${slug}/`,
60
+ });
61
+ }
62
+ }
63
+ posts.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
64
+ return posts;
65
+ }
66
+ // ─── 上下文构建 ───────────────────────────────────────────────
67
+ function buildContext(posts, siteUrl) {
68
+ const selectedPosts = posts.slice(0, 12);
69
+ const zhPosts = posts.filter((p) => p.lang === "zh");
70
+ const enPosts = posts.filter((p) => p.lang === "en");
71
+ // 标签聚合
72
+ const tagCounts = new Map();
73
+ for (const post of posts) {
74
+ for (const tag of post.tags) {
75
+ tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1);
76
+ }
77
+ }
78
+ const topTags = [...tagCounts.entries()]
79
+ .sort((a, b) => b[1] - a[1])
80
+ .slice(0, 15)
81
+ .map(([tag]) => tag);
82
+ // 分类聚合
83
+ const categoryCounts = new Map();
84
+ for (const post of posts) {
85
+ if (post.category) {
86
+ categoryCounts.set(post.category, (categoryCounts.get(post.category) || 0) + 1);
87
+ }
88
+ }
89
+ const topCategories = [...categoryCounts.entries()]
90
+ .sort((a, b) => b[1] - a[1])
91
+ .slice(0, 5)
92
+ .map(([cat]) => cat);
93
+ return {
94
+ generatedAt: new Date().toISOString(),
95
+ siteUrl,
96
+ sourceInfo: {
97
+ totalPosts: posts.length,
98
+ zhPosts: zhPosts.length,
99
+ enPosts: enPosts.length,
100
+ selectedPosts: selectedPosts.length,
101
+ },
102
+ profile: {
103
+ name: process.env.SITE_AUTHOR || "博主",
104
+ siteUrl,
105
+ },
106
+ posts: selectedPosts.map((post) => ({
107
+ title: post.title,
108
+ date: post.date,
109
+ categories: [post.category].filter(Boolean),
110
+ tags: post.tags.slice(0, 5),
111
+ summary: post.summary,
112
+ keyPoints: post.keyPoints,
113
+ url: post.url,
114
+ })),
115
+ topTags,
116
+ topCategories,
117
+ contentStats: {
118
+ totalPosts: posts.length,
119
+ avgPostPerMonth: Math.round(posts.length / Math.max(1, calculateMonthsSpan(posts))),
120
+ },
121
+ };
122
+ }
123
+ function calculateMonthsSpan(posts) {
124
+ if (posts.length < 2)
125
+ return 1;
126
+ const latest = new Date(posts[0].date);
127
+ const earliest = new Date(posts[posts.length - 1].date);
128
+ return ((latest.getFullYear() - earliest.getFullYear()) * 12 +
129
+ (latest.getMonth() - earliest.getMonth()) +
130
+ 1);
131
+ }
132
+ // ─── AI 生成 ───────────────────────────────────────────────────
133
+ async function generateReportWithAI(context) {
134
+ if (!hasAPIKey()) {
135
+ throw new Error("未配置 AI API Key");
136
+ }
137
+ const config = getConfig();
138
+ const systemPrompt = `你是一位中文科技写作编辑。请基于给定上下文,以第三方视角生成作者画像 JSON。
139
+ 要求:
140
+ 1. 严格输出 JSON,不要输出 Markdown 或多余文本。
141
+ 2. 语气客观、克制、具体,不要夸张和空泛。
142
+ 3. 结论必须可由上下文支撑,避免编造。
143
+ 4. 文案使用中文,第三人称,不使用"我"。
144
+
145
+ 输出 schema:
146
+ {
147
+ "report": {
148
+ "hero": {"title":"AI 视角下的作者","summary":"...","intro":"..."},
149
+ "identities":[{"name":"...","description":"...","evidence":"..."}],
150
+ "strengths":[{"title":"...","points":["..."]}],
151
+ "styles":[{"trait":"...","description":"..."}],
152
+ "proofs":{
153
+ "posts":[{"title":"...","url":"...","reason":"...","date":"YYYY-MM-DD"}]
154
+ },
155
+ "disclaimer":"..."
156
+ }
157
+ }`;
158
+ const contextText = JSON.stringify(context, null, 2).slice(0, 25000);
159
+ const userPrompt = `上下文数据如下,请根据这些信息生成报告:\n${contextText}`;
160
+ const content = await chatCompletion([
161
+ { role: "system", content: systemPrompt },
162
+ { role: "user", content: userPrompt },
163
+ ], { maxTokens: 3000, responseFormat: "json" });
164
+ const parsed = JSON.parse(content);
165
+ return {
166
+ meta: {
167
+ lastUpdated: new Date().toISOString(),
168
+ model: config.model,
169
+ generatedBy: "ai",
170
+ },
171
+ report: parsed.report || parsed,
172
+ };
173
+ }
174
+ // ─── 规则模板生成 ─────────────────────────────────────────────
175
+ function buildRuleBasedReport(context) {
176
+ const posts = context.posts.slice(0, 5).map((post) => ({
177
+ title: post.title,
178
+ url: post.url,
179
+ reason: post.summary?.slice(0, 60) || "该文章体现了作者的写作风格。",
180
+ date: post.date,
181
+ }));
182
+ const topCategories = context.topCategories.slice(0, 3).join("、");
183
+ const authorName = context.profile.name;
184
+ return {
185
+ meta: {
186
+ lastUpdated: new Date().toISOString(),
187
+ model: "rule-based-template",
188
+ generatedBy: "rule-based",
189
+ },
190
+ report: {
191
+ hero: {
192
+ title: `AI 视角下的 ${authorName}`,
193
+ summary: `一位专注于${topCategories}领域的博主,持续输出高质量内容。`,
194
+ intro: `博客已发布 ${context.sourceInfo.totalPosts} 篇文章,涵盖 ${context.topTags.length} 个主题标签。`,
195
+ },
196
+ identities: [
197
+ {
198
+ name: "技术博主",
199
+ description: "持续分享技术经验与实践心得。",
200
+ evidence: `已发布 ${context.sourceInfo.totalPosts} 篇文章。`,
201
+ },
202
+ {
203
+ name: "内容创作者",
204
+ description: "注重内容质量与读者体验。",
205
+ evidence: posts[0]
206
+ ? `近期文章《${posts[0].title}》体现了专业的写作风格。`
207
+ : "",
208
+ },
209
+ ],
210
+ strengths: [
211
+ {
212
+ title: "内容深度",
213
+ points: [
214
+ "文章结构清晰,逻辑性强",
215
+ "注重实践,配合代码示例",
216
+ "持续更新,覆盖多个技术领域",
217
+ ],
218
+ },
219
+ {
220
+ title: "表达风格",
221
+ points: ["语言简洁明了", "注重可读性", "善于总结提炼"],
222
+ },
223
+ ],
224
+ styles: [
225
+ {
226
+ trait: "技术导向",
227
+ description: "专注于技术内容的深度讲解与实践分享。",
228
+ },
229
+ {
230
+ trait: "结构化表达",
231
+ description: "文章结构清晰,便于读者理解和学习。",
232
+ },
233
+ ],
234
+ proofs: { posts },
235
+ disclaimer: "该页面由 AI 归纳与规则模板联合生成,旨在帮助访客快速建立认知,可能存在概括偏差,请以原始文章信息为准。",
236
+ },
237
+ };
238
+ }
239
+ // ─── 主流程 ───────────────────────────────────────────────────
240
+ async function main() {
241
+ const args = parseArgs();
242
+ await loadEnv();
243
+ console.log("👤 生成作者画像报告");
244
+ console.log("━".repeat(50));
245
+ const siteUrl = process.env.SITE_URL || DEFAULT_SITE_URL;
246
+ console.log(` 站点 URL: ${siteUrl}`);
247
+ console.log(` 输出目录: ${DATA_DIR}`);
248
+ console.log("");
249
+ // 收集数据
250
+ console.log("📂 收集文章数据...");
251
+ const posts = await collectPosts(siteUrl);
252
+ console.log(` 找到 ${posts.length} 篇文章`);
253
+ const context = buildContext(posts, siteUrl);
254
+ await writeJson(OUTPUT_CONTEXT, context);
255
+ console.log(` 上下文已保存: ${OUTPUT_CONTEXT}`);
256
+ // 生成报告
257
+ let report;
258
+ if (args.noAI) {
259
+ console.log("\n📝 使用规则模板生成...");
260
+ report = buildRuleBasedReport(context);
261
+ }
262
+ else {
263
+ console.log("\n🤖 使用 AI 生成...");
264
+ try {
265
+ report = await generateReportWithAI(context);
266
+ console.log(" ✅ AI 生成成功");
267
+ }
268
+ catch (error) {
269
+ const err = error;
270
+ if (!args.force) {
271
+ console.warn(` ⚠️ AI 生成失败: ${err.message}`);
272
+ console.log(" 📝 回退使用规则模板...");
273
+ report = buildRuleBasedReport(context);
274
+ }
275
+ else {
276
+ throw error;
277
+ }
278
+ }
279
+ }
280
+ await writeJson(OUTPUT_REPORT, report);
281
+ console.log("\n✅ 画像报告生成完成");
282
+ console.log(`📄 报告文件: ${OUTPUT_REPORT}`);
283
+ console.log(`🧩 上下文文件: ${OUTPUT_CONTEXT}`);
284
+ }
285
+ main().catch((error) => {
286
+ console.error("❌ 生成失败:", error.message);
287
+ process.exit(1);
288
+ });
289
+ //# sourceMappingURL=generate-author-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-author-profile.js","sourceRoot":"","sources":["../../src/tools/generate-author-profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * AI 封面图生成工具
4
+ *
5
+ * 用法:
6
+ * pnpm run tools:cover <文章路径> # 生成封面图
7
+ * pnpm run tools:cover <文章路径> --write # 生成并写入 frontmatter
8
+ * pnpm run tools:cover <文章路径> --style <风格> # 指定风格 (abstract|minimal|tech|illustration)
9
+ *
10
+ * 环境变量:
11
+ * AI_API_KEY / OPENAI_API_KEY — API key
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=generate-cover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-cover.d.ts","sourceRoot":"","sources":["../../src/tools/generate-cover.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG"}
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * AI 封面图生成工具
4
+ *
5
+ * 用法:
6
+ * pnpm run tools:cover <文章路径> # 生成封面图
7
+ * pnpm run tools:cover <文章路径> --write # 生成并写入 frontmatter
8
+ * pnpm run tools:cover <文章路径> --style <风格> # 指定风格 (abstract|minimal|tech|illustration)
9
+ *
10
+ * 环境变量:
11
+ * AI_API_KEY / OPENAI_API_KEY — API key
12
+ */
13
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
14
+ import { join, basename } from "node:path";
15
+ import { extractFrontmatter } from "./lib/frontmatter.js";
16
+ import { generateImage } from "./lib/ai-provider.js";
17
+ const COVERS_DIR = join(process.cwd(), "src/assets/covers");
18
+ function parseArgs() {
19
+ const args = process.argv.slice(2);
20
+ const filePath = args.find(a => !a.startsWith("--"));
21
+ const style = args.includes("--style")
22
+ ? args[args.indexOf("--style") + 1] || "abstract"
23
+ : "abstract";
24
+ const write = args.includes("--write");
25
+ if (!filePath) {
26
+ console.error("用法: pnpm run tools:cover <文章路径> [--write] [--style abstract|minimal|tech|illustration]");
27
+ process.exit(1);
28
+ }
29
+ return { filePath, style, write };
30
+ }
31
+ const STYLE_PROMPTS = {
32
+ abstract: "Create an abstract, modern cover image with geometric shapes, gradients and soft lighting. The composition should feel clean, professional and tech-oriented.",
33
+ minimal: "Create a minimalist cover image with plenty of negative space, subtle textures and a refined color palette. Simple, elegant composition.",
34
+ tech: "Create a technology-themed cover image with digital elements like circuit patterns, code fragments, data visualizations. Modern and futuristic.",
35
+ illustration: "Create a flat illustration style cover image with bold colors and simplified shapes. Friendly and approachable design.",
36
+ };
37
+ function generateFileName(title) {
38
+ const slug = title
39
+ .toLowerCase()
40
+ .replace(/[\u4e00-\u9fff]/g, m => m)
41
+ .replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-")
42
+ .replace(/^-|-$/g, "")
43
+ .slice(0, 50);
44
+ return `cover-${slug}-${Date.now().toString(36)}.png`;
45
+ }
46
+ async function main() {
47
+ const { filePath, style, write } = parseArgs();
48
+ const fullPath = join(process.cwd(), filePath);
49
+ const content = await readFile(fullPath, "utf-8");
50
+ const { data } = extractFrontmatter(content);
51
+ const title = data.title || basename(filePath, ".md");
52
+ const description = data.description || "";
53
+ if (data.ogImage && !write) {
54
+ console.log(`⚠️ 文章已有 ogImage: ${data.ogImage}`);
55
+ console.log(` 使用 --write 强制覆盖\n`);
56
+ }
57
+ const stylePrompt = STYLE_PROMPTS[style] || STYLE_PROMPTS.abstract;
58
+ const prompt = `${stylePrompt}\n\nThis is for a blog post titled: "${title}"\nDescription: "${description}"\n\nDo NOT include any text, letters, numbers or words in the image. Pure visual design only. Aspect ratio: 16:9, high quality.`;
59
+ console.log(`🎨 生成封面图...`);
60
+ console.log(` 风格: ${style}`);
61
+ console.log(` 标题: ${title}\n`);
62
+ const imageBuffer = await generateImage(prompt);
63
+ await mkdir(COVERS_DIR, { recursive: true });
64
+ const fileName = generateFileName(title);
65
+ const outputPath = join(COVERS_DIR, fileName);
66
+ await writeFile(outputPath, imageBuffer);
67
+ const relativePath = `../../../assets/covers/${fileName}`;
68
+ console.log(`\n✅ 封面图已生成: ${outputPath}`);
69
+ console.log(` 相对路径 (用于 frontmatter): ${relativePath}`);
70
+ if (write) {
71
+ const ogImageLine = `ogImage: ${relativePath}`;
72
+ let newContent;
73
+ if (data.ogImage) {
74
+ newContent = content.replace(/^ogImage:.*$/m, ogImageLine);
75
+ }
76
+ else {
77
+ const lines = content.split("\n");
78
+ const closingIdx = lines.indexOf("---", 1);
79
+ if (closingIdx > 0) {
80
+ lines.splice(closingIdx, 0, ogImageLine);
81
+ }
82
+ newContent = lines.join("\n");
83
+ }
84
+ await writeFile(fullPath, newContent, "utf-8");
85
+ console.log(` ✍️ 已更新 frontmatter ogImage 字段`);
86
+ }
87
+ else {
88
+ console.log(`\n💡 添加到 frontmatter:\n ogImage: ${relativePath}`);
89
+ }
90
+ }
91
+ main().catch(err => {
92
+ console.error("❌ 错误:", err.message || err);
93
+ process.exit(1);
94
+ });
95
+ //# sourceMappingURL=generate-cover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-cover.js","sourceRoot":"","sources":["../../src/tools/generate-cover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env npx tsx
2
+ export {};
3
+ //# sourceMappingURL=generate-og.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-og.d.ts","sourceRoot":"","sources":["../../src/tools/generate-og.ts"],"names":[],"mappings":""}