@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.
- package/README.md +69 -0
- package/dist/commands/ai.d.ts +2 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +99 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/data.d.ts +2 -0
- package/dist/commands/data.d.ts.map +1 -0
- package/dist/commands/data.js +111 -0
- package/dist/commands/data.js.map +1 -0
- package/dist/commands/hooks.d.ts +2 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +378 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +50 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/podcast.d.ts +2 -0
- package/dist/commands/podcast.d.ts.map +1 -0
- package/dist/commands/podcast.js +89 -0
- package/dist/commands/podcast.js.map +1 -0
- package/dist/commands/post.d.ts +2 -0
- package/dist/commands/post.d.ts.map +1 -0
- package/dist/commands/post.js +190 -0
- package/dist/commands/post.js.map +1 -0
- package/dist/commands/profile.d.ts +2 -0
- package/dist/commands/profile.d.ts.map +1 -0
- package/dist/commands/profile.js +88 -0
- package/dist/commands/profile.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/ai-process.d.ts +20 -0
- package/dist/tools/ai-process.d.ts.map +1 -0
- package/dist/tools/ai-process.js +607 -0
- package/dist/tools/ai-process.js.map +1 -0
- package/dist/tools/build-author-context.d.ts +13 -0
- package/dist/tools/build-author-context.d.ts.map +1 -0
- package/dist/tools/build-author-context.js +313 -0
- package/dist/tools/build-author-context.js.map +1 -0
- package/dist/tools/build-voice-profile.d.ts +12 -0
- package/dist/tools/build-voice-profile.d.ts.map +1 -0
- package/dist/tools/build-voice-profile.js +270 -0
- package/dist/tools/build-voice-profile.js.map +1 -0
- package/dist/tools/eval-ai-chat.d.ts +17 -0
- package/dist/tools/eval-ai-chat.d.ts.map +1 -0
- package/dist/tools/eval-ai-chat.js +362 -0
- package/dist/tools/eval-ai-chat.js.map +1 -0
- package/dist/tools/generate-author-profile.d.ts +14 -0
- package/dist/tools/generate-author-profile.d.ts.map +1 -0
- package/dist/tools/generate-author-profile.js +289 -0
- package/dist/tools/generate-author-profile.js.map +1 -0
- package/dist/tools/generate-cover.d.ts +14 -0
- package/dist/tools/generate-cover.d.ts.map +1 -0
- package/dist/tools/generate-cover.js +95 -0
- package/dist/tools/generate-cover.js.map +1 -0
- package/dist/tools/generate-og.d.ts +3 -0
- package/dist/tools/generate-og.d.ts.map +1 -0
- package/dist/tools/generate-og.js +254 -0
- package/dist/tools/generate-og.js.map +1 -0
- package/dist/tools/generate-related.d.ts +11 -0
- package/dist/tools/generate-related.d.ts.map +1 -0
- package/dist/tools/generate-related.js +124 -0
- package/dist/tools/generate-related.js.map +1 -0
- package/dist/tools/generate-tags.d.ts +14 -0
- package/dist/tools/generate-tags.d.ts.map +1 -0
- package/dist/tools/generate-tags.js +182 -0
- package/dist/tools/generate-tags.js.map +1 -0
- package/dist/tools/lib/ai-provider.d.ts +43 -0
- package/dist/tools/lib/ai-provider.d.ts.map +1 -0
- package/dist/tools/lib/ai-provider.js +146 -0
- package/dist/tools/lib/ai-provider.js.map +1 -0
- package/dist/tools/lib/audio-processor.d.ts +46 -0
- package/dist/tools/lib/audio-processor.d.ts.map +1 -0
- package/dist/tools/lib/audio-processor.js +188 -0
- package/dist/tools/lib/audio-processor.js.map +1 -0
- package/dist/tools/lib/frontmatter.d.ts +11 -0
- package/dist/tools/lib/frontmatter.d.ts.map +1 -0
- package/dist/tools/lib/frontmatter.js +80 -0
- package/dist/tools/lib/frontmatter.js.map +1 -0
- package/dist/tools/lib/index.d.ts +7 -0
- package/dist/tools/lib/index.d.ts.map +1 -0
- package/{template/tools/lib/index.ts → dist/tools/lib/index.js} +1 -0
- package/dist/tools/lib/index.js.map +1 -0
- package/dist/tools/lib/markdown.d.ts +6 -0
- package/dist/tools/lib/markdown.d.ts.map +1 -0
- package/dist/tools/lib/markdown.js +34 -0
- package/dist/tools/lib/markdown.js.map +1 -0
- package/dist/tools/lib/posts.d.ts +25 -0
- package/dist/tools/lib/posts.d.ts.map +1 -0
- package/dist/tools/lib/posts.js +63 -0
- package/dist/tools/lib/posts.js.map +1 -0
- package/dist/tools/lib/script-generator.d.ts +61 -0
- package/dist/tools/lib/script-generator.d.ts.map +1 -0
- package/dist/tools/lib/script-generator.js +182 -0
- package/dist/tools/lib/script-generator.js.map +1 -0
- package/dist/tools/lib/tts-provider.d.ts +65 -0
- package/dist/tools/lib/tts-provider.d.ts.map +1 -0
- package/dist/tools/lib/tts-provider.js +116 -0
- package/dist/tools/lib/tts-provider.js.map +1 -0
- package/dist/tools/lib/types.d.ts +129 -0
- package/dist/tools/lib/types.d.ts.map +1 -0
- package/dist/tools/lib/types.js +64 -0
- package/dist/tools/lib/types.js.map +1 -0
- package/dist/tools/lib/utils.d.ts +18 -0
- package/dist/tools/lib/utils.d.ts.map +1 -0
- package/dist/tools/lib/utils.js +121 -0
- package/dist/tools/lib/utils.js.map +1 -0
- package/dist/tools/lib/vectors.d.ts +27 -0
- package/dist/tools/lib/vectors.d.ts.map +1 -0
- package/dist/tools/lib/vectors.js +64 -0
- package/dist/tools/lib/vectors.js.map +1 -0
- package/dist/tools/podcast-feed.d.ts +6 -0
- package/dist/tools/podcast-feed.d.ts.map +1 -0
- package/dist/tools/podcast-feed.js +121 -0
- package/dist/tools/podcast-feed.js.map +1 -0
- package/dist/tools/podcast-generate.d.ts +15 -0
- package/dist/tools/podcast-generate.d.ts.map +1 -0
- package/dist/tools/podcast-generate.js +318 -0
- package/dist/tools/podcast-generate.js.map +1 -0
- package/dist/tools/podcast-list.d.ts +6 -0
- package/dist/tools/podcast-list.d.ts.map +1 -0
- package/dist/tools/podcast-list.js +66 -0
- package/dist/tools/podcast-list.js.map +1 -0
- package/dist/tools/summarize.d.ts +16 -0
- package/dist/tools/summarize.d.ts.map +1 -0
- package/dist/tools/summarize.js +108 -0
- package/dist/tools/summarize.js.map +1 -0
- package/dist/tools/translate.d.ts +13 -0
- package/dist/tools/translate.d.ts.map +1 -0
- package/dist/tools/translate.js +46 -0
- package/dist/tools/translate.js.map +1 -0
- package/dist/tools/vectorize.d.ts +13 -0
- package/dist/tools/vectorize.d.ts.map +1 -0
- package/dist/tools/vectorize.js +87 -0
- package/dist/tools/vectorize.js.map +1 -0
- package/package.json +14 -9
- package/template/astro.config.ts +8 -28
- package/template/datas/ai-seo.json +8 -0
- package/template/datas/ai-skip-list.json +1 -0
- package/template/datas/author-profile-context.json +21 -0
- package/template/datas/author-profile-report.json +21 -0
- package/template/datas/eval/gold-set.json +72 -0
- package/template/functions/README.md +82 -0
- package/template/functions/api/ai-info.ts +2 -2
- package/template/functions/api/chat.ts +4 -1
- package/template/functions/api/notify/comment.ts +140 -68
- package/template/functions/api/notify/debug.ts +41 -0
- package/template/functions/api/notify/status.ts +97 -0
- package/template/functions/api/notify/test-ai-chat.ts +67 -0
- package/template/package.json +22 -25
- package/template/src/config.ts +11 -0
- package/template/src/content.config.ts +29 -16
- package/template/src/env.d.ts +0 -5
- package/index.js +0 -36
- package/template/tools/README.md +0 -169
- package/template/tools/ai-process.ts +0 -816
- package/template/tools/build-author-context.ts +0 -405
- package/template/tools/build-voice-profile.ts +0 -322
- package/template/tools/generate-author-profile.ts +0 -369
- package/template/tools/generate-cover.ts +0 -123
- package/template/tools/generate-og.ts +0 -280
- package/template/tools/generate-related.ts +0 -146
- package/template/tools/generate-tags.ts +0 -251
- package/template/tools/lib/ai-provider.ts +0 -240
- package/template/tools/lib/frontmatter.ts +0 -94
- package/template/tools/lib/markdown.ts +0 -40
- package/template/tools/lib/posts.ts +0 -89
- package/template/tools/lib/utils.ts +0 -138
- package/template/tools/lib/vectors.ts +0 -96
- package/template/tools/summarize.ts +0 -142
- package/template/tools/translate.ts +0 -60
- package/template/tools/vectorize.ts +0 -105
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types and interfaces for the podcast generation system.
|
|
3
|
+
*
|
|
4
|
+
* @module podcast/types
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Default speaker configurations for each language.
|
|
8
|
+
* Host uses "alloy" (neutral voice), Guest uses "onyx" (deeper voice).
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_SPEAKERS = {
|
|
11
|
+
zh: [
|
|
12
|
+
{ id: "host", name: "主持人", role: "host", voiceId: "alloy" },
|
|
13
|
+
{ id: "guest", name: "嘉宾", role: "guest", voiceId: "onyx" },
|
|
14
|
+
],
|
|
15
|
+
en: [
|
|
16
|
+
{ id: "host", name: "Host", role: "host", voiceId: "alloy" },
|
|
17
|
+
{ id: "guest", name: "Guest", role: "guest", voiceId: "echo" },
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Available OpenAI TTS voices.
|
|
22
|
+
* @see https://platform.openai.com/docs/guides/text-to-speech/voice-options
|
|
23
|
+
*/
|
|
24
|
+
export const OPENAI_VOICES = [
|
|
25
|
+
"alloy",
|
|
26
|
+
"echo",
|
|
27
|
+
"fable",
|
|
28
|
+
"onyx",
|
|
29
|
+
"nova",
|
|
30
|
+
"shimmer",
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Type guard to check if a string is a valid OpenAI voice.
|
|
34
|
+
* @param voice - Voice string to check
|
|
35
|
+
* @returns True if the voice is valid
|
|
36
|
+
*/
|
|
37
|
+
export function isValidOpenAIVoice(voice) {
|
|
38
|
+
return OPENAI_VOICES.includes(voice);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* TTS models available from OpenAI.
|
|
42
|
+
*/
|
|
43
|
+
export const OPENAI_TTS_MODELS = ["tts-1", "tts-1-hd"];
|
|
44
|
+
/**
|
|
45
|
+
* Parse podcast frontmatter from a record (parsed YAML frontmatter).
|
|
46
|
+
* @param frontmatter - Parsed frontmatter object
|
|
47
|
+
* @returns PodcastFrontmatter or undefined
|
|
48
|
+
*/
|
|
49
|
+
export function parsePodcastFrontmatter(frontmatter) {
|
|
50
|
+
if (!frontmatter.podcast)
|
|
51
|
+
return undefined;
|
|
52
|
+
const podcast = frontmatter.podcast;
|
|
53
|
+
const voices = podcast.voices;
|
|
54
|
+
return {
|
|
55
|
+
enabled: typeof podcast.enabled === "boolean" ? podcast.enabled : undefined,
|
|
56
|
+
voices: voices
|
|
57
|
+
? {
|
|
58
|
+
host: typeof voices.host === "string" ? voices.host : undefined,
|
|
59
|
+
guest: typeof voices.guest === "string" ? voices.guest : undefined,
|
|
60
|
+
}
|
|
61
|
+
: undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/tools/lib/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAsGH;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA0C;IACrE,EAAE,EAAE;QACF,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE;QAC3D,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE;KAC5D;IACD,EAAE,EAAE;QACF,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE;QAC5D,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE;KAC/D;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,OAAO;IACP,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,SAAS;CACD,CAAC;AAKX;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,aAAa,CAAC,QAAQ,CAAC,KAAoB,CAAC,CAAC;AACtD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,UAAU,CAAU,CAAC;AAKhE;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,WAAoC;IAEpC,IAAI,CAAC,WAAW,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAE3C,MAAM,OAAO,GAAG,WAAW,CAAC,OAAkC,CAAC;IAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,MAA6C,CAAC;IAErE,OAAO;QACL,OAAO,EACL,OAAO,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QACpE,MAAM,EAAE,MAAM;YACZ,CAAC,CAAC;gBACE,IAAI,EAAE,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;gBAC/D,KAAK,EAAE,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;aACnE;YACH,CAAC,CAAC,SAAS;KACd,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 共享工具函数
|
|
3
|
+
*/
|
|
4
|
+
export declare const ROOT_DIR: string;
|
|
5
|
+
export declare const DATA_DIR: string;
|
|
6
|
+
export declare const BLOG_DIR: string;
|
|
7
|
+
export declare function loadEnv(): Promise<void>;
|
|
8
|
+
export declare function readJson<T>(filePath: string, fallback: T): Promise<T>;
|
|
9
|
+
export declare function writeJson(filePath: string, data: unknown): Promise<void>;
|
|
10
|
+
export declare function truncate(text: string, max?: number): string;
|
|
11
|
+
export declare function normalizeSpace(text: string): string;
|
|
12
|
+
export declare function parseCliArgs<T extends Record<string, unknown>>(defaults: T, parsers?: Partial<{
|
|
13
|
+
[K in keyof T]: (value: string) => T[K];
|
|
14
|
+
}>): T;
|
|
15
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
16
|
+
export declare function toPlainDate(value: unknown): string;
|
|
17
|
+
export declare function contentHash(text: string): string;
|
|
18
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/tools/lib/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,eAAO,MAAM,QAAQ,QAAgB,CAAC;AACtC,eAAO,MAAM,QAAQ,QAA0B,CAAC;AAChD,eAAO,MAAM,QAAQ,QAAkC,CAAC;AAIxD,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAwB7C;AAID,wBAAsB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAO3E;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9E;AAID,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,MAAM,CAIxD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAID,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5D,QAAQ,EAAE,CAAC,EACX,OAAO,CAAC,EAAE,OAAO,CAAC;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC,GAC7D,CAAC,CA2CH;AAID,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKlD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 共享工具函数
|
|
3
|
+
*/
|
|
4
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
export const ROOT_DIR = process.cwd();
|
|
8
|
+
export const DATA_DIR = join(ROOT_DIR, "datas");
|
|
9
|
+
export const BLOG_DIR = join(ROOT_DIR, "src/data/blog");
|
|
10
|
+
// ─── 环境变量加载 ────────────────────────────────────────
|
|
11
|
+
export async function loadEnv() {
|
|
12
|
+
const envPath = join(ROOT_DIR, ".env");
|
|
13
|
+
try {
|
|
14
|
+
const content = await readFile(envPath, "utf-8");
|
|
15
|
+
for (const line of content.split("\n")) {
|
|
16
|
+
const trimmed = line.trim();
|
|
17
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
18
|
+
continue;
|
|
19
|
+
const eqIndex = trimmed.indexOf("=");
|
|
20
|
+
if (eqIndex === -1)
|
|
21
|
+
continue;
|
|
22
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
23
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
24
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
25
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
26
|
+
value = value.slice(1, -1);
|
|
27
|
+
}
|
|
28
|
+
if (!process.env[key]) {
|
|
29
|
+
process.env[key] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// .env 不存在时继续
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// ─── JSON 文件操作 ────────────────────────────────────────
|
|
38
|
+
export async function readJson(filePath, fallback) {
|
|
39
|
+
try {
|
|
40
|
+
const raw = await readFile(filePath, "utf-8");
|
|
41
|
+
return JSON.parse(raw);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return fallback;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export async function writeJson(filePath, data) {
|
|
48
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
49
|
+
await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
50
|
+
}
|
|
51
|
+
// ─── 文本处理 ────────────────────────────────────────────────
|
|
52
|
+
export function truncate(text, max = 120) {
|
|
53
|
+
if (!text)
|
|
54
|
+
return "";
|
|
55
|
+
if (text.length <= max)
|
|
56
|
+
return text;
|
|
57
|
+
return `${text.slice(0, max - 1)}…`;
|
|
58
|
+
}
|
|
59
|
+
export function normalizeSpace(text) {
|
|
60
|
+
return String(text ?? "").replace(/\s+/g, " ").trim();
|
|
61
|
+
}
|
|
62
|
+
// ─── CLI 参数解析 ─────────────────────────────────────────────
|
|
63
|
+
export function parseCliArgs(defaults, parsers) {
|
|
64
|
+
const args = process.argv.slice(2);
|
|
65
|
+
const result = { ...defaults };
|
|
66
|
+
// Build a map of all possible key variants
|
|
67
|
+
const keyMap = new Map();
|
|
68
|
+
for (const key of Object.keys(defaults)) {
|
|
69
|
+
const keyStr = String(key);
|
|
70
|
+
keyMap.set(keyStr, key);
|
|
71
|
+
keyMap.set(keyStr.toLowerCase(), key);
|
|
72
|
+
// kebab-case from camelCase (noAI -> no-a-i for each capital)
|
|
73
|
+
const kebabKey = keyStr.replace(/[A-Z]/g, (c) => `-${c}`).toLowerCase();
|
|
74
|
+
keyMap.set(kebabKey, key);
|
|
75
|
+
// Also handle natural kebab-case (noAI -> no-ai)
|
|
76
|
+
// This handles cases like noAI -> no-ai (user types --no-ai)
|
|
77
|
+
const naturalKebab = keyStr.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
78
|
+
keyMap.set(naturalKebab, key);
|
|
79
|
+
}
|
|
80
|
+
for (const arg of args) {
|
|
81
|
+
if (arg.startsWith("--")) {
|
|
82
|
+
const eqIndex = arg.indexOf("=");
|
|
83
|
+
if (eqIndex === -1) {
|
|
84
|
+
// Boolean flag
|
|
85
|
+
const key = arg.slice(2);
|
|
86
|
+
const mappedKey = keyMap.get(key) || keyMap.get(key.toLowerCase());
|
|
87
|
+
if (mappedKey && typeof result[mappedKey] === "boolean") {
|
|
88
|
+
result[String(mappedKey)] = true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
const key = arg.slice(2, eqIndex);
|
|
93
|
+
const value = arg.slice(eqIndex + 1);
|
|
94
|
+
const mappedKey = keyMap.get(key) || keyMap.get(key.toLowerCase());
|
|
95
|
+
if (mappedKey) {
|
|
96
|
+
const parser = parsers?.[mappedKey];
|
|
97
|
+
result[String(mappedKey)] = parser
|
|
98
|
+
? parser(value)
|
|
99
|
+
: value;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
// ─── 其他工具 ─────────────────────────────────────────────────
|
|
107
|
+
export function sleep(ms) {
|
|
108
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
109
|
+
}
|
|
110
|
+
export function toPlainDate(value) {
|
|
111
|
+
if (!value)
|
|
112
|
+
return "";
|
|
113
|
+
const parsed = new Date(value);
|
|
114
|
+
if (Number.isNaN(parsed.getTime()))
|
|
115
|
+
return String(value).slice(0, 10);
|
|
116
|
+
return parsed.toISOString().slice(0, 10);
|
|
117
|
+
}
|
|
118
|
+
export function contentHash(text) {
|
|
119
|
+
return createHash("sha256").update(text).digest("hex").slice(0, 12);
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/tools/lib/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 向量计算与 TF-IDF 工具
|
|
3
|
+
*/
|
|
4
|
+
export declare function cosineSimilarity(a: number[], b: number[]): number;
|
|
5
|
+
export declare function tokenize(text: string): string[];
|
|
6
|
+
export declare function buildVocabulary(documents: string[][]): string[];
|
|
7
|
+
export declare function computeTfIdf(tokens: string[], vocabulary: string[], idf: Map<string, number>): number[];
|
|
8
|
+
export interface ContentChunk {
|
|
9
|
+
postId: string;
|
|
10
|
+
title: string;
|
|
11
|
+
lang: string;
|
|
12
|
+
chunkIndex: number;
|
|
13
|
+
text: string;
|
|
14
|
+
vector?: number[];
|
|
15
|
+
}
|
|
16
|
+
export interface VectorIndex {
|
|
17
|
+
version: number;
|
|
18
|
+
method: "tfidf" | "openai";
|
|
19
|
+
createdAt: string;
|
|
20
|
+
vocabulary?: string[];
|
|
21
|
+
chunks: ContentChunk[];
|
|
22
|
+
}
|
|
23
|
+
export declare function generateTfIdfVectors(chunks: ContentChunk[]): {
|
|
24
|
+
vocabulary: string[];
|
|
25
|
+
vectors: number[][];
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=vectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vectors.d.ts","sourceRoot":"","sources":["../../../src/tools/lib/vectors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAYjE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAS/C;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,CAc/D;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EAAE,EAChB,UAAU,EAAE,MAAM,EAAE,EACpB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACvB,MAAM,EAAE,CAUV;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,YAAY,EAAE,GACrB;IAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAA;CAAE,CAe/C"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 向量计算与 TF-IDF 工具
|
|
3
|
+
*/
|
|
4
|
+
export function cosineSimilarity(a, b) {
|
|
5
|
+
if (a.length !== b.length)
|
|
6
|
+
return 0;
|
|
7
|
+
let dot = 0, magA = 0, magB = 0;
|
|
8
|
+
for (let i = 0; i < a.length; i++) {
|
|
9
|
+
dot += a[i] * b[i];
|
|
10
|
+
magA += a[i] * a[i];
|
|
11
|
+
magB += b[i] * b[i];
|
|
12
|
+
}
|
|
13
|
+
const mag = Math.sqrt(magA) * Math.sqrt(magB);
|
|
14
|
+
return mag === 0 ? 0 : dot / mag;
|
|
15
|
+
}
|
|
16
|
+
export function tokenize(text) {
|
|
17
|
+
const CJK = /[\u4e00-\u9fff\u3400-\u4dbf]/g;
|
|
18
|
+
const cjkChars = text.match(CJK) || [];
|
|
19
|
+
const latin = text
|
|
20
|
+
.replace(CJK, " ")
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.split(/\W+/)
|
|
23
|
+
.filter(w => w.length > 2);
|
|
24
|
+
return [...cjkChars, ...latin];
|
|
25
|
+
}
|
|
26
|
+
export function buildVocabulary(documents) {
|
|
27
|
+
const df = new Map();
|
|
28
|
+
for (const doc of documents) {
|
|
29
|
+
const unique = new Set(doc);
|
|
30
|
+
for (const term of unique) {
|
|
31
|
+
df.set(term, (df.get(term) || 0) + 1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const maxDf = documents.length * 0.8;
|
|
35
|
+
return Array.from(df.entries())
|
|
36
|
+
.filter(([, count]) => count >= 2 && count <= maxDf)
|
|
37
|
+
.sort((a, b) => b[1] - a[1])
|
|
38
|
+
.slice(0, 2000)
|
|
39
|
+
.map(([term]) => term);
|
|
40
|
+
}
|
|
41
|
+
export function computeTfIdf(tokens, vocabulary, idf) {
|
|
42
|
+
const tf = new Map();
|
|
43
|
+
for (const t of tokens)
|
|
44
|
+
tf.set(t, (tf.get(t) || 0) + 1);
|
|
45
|
+
const maxTf = Math.max(...tf.values(), 1);
|
|
46
|
+
return vocabulary.map(term => {
|
|
47
|
+
const termTf = (tf.get(term) || 0) / maxTf;
|
|
48
|
+
const termIdf = idf.get(term) || 0;
|
|
49
|
+
return +(termTf * termIdf).toFixed(6);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export function generateTfIdfVectors(chunks) {
|
|
53
|
+
const tokenizedDocs = chunks.map(c => tokenize(c.text));
|
|
54
|
+
const vocabulary = buildVocabulary(tokenizedDocs);
|
|
55
|
+
const N = tokenizedDocs.length;
|
|
56
|
+
const idf = new Map();
|
|
57
|
+
for (const term of vocabulary) {
|
|
58
|
+
const docCount = tokenizedDocs.filter(doc => doc.includes(term)).length;
|
|
59
|
+
idf.set(term, Math.log(N / (docCount + 1)) + 1);
|
|
60
|
+
}
|
|
61
|
+
const vectors = tokenizedDocs.map(tokens => computeTfIdf(tokens, vocabulary, idf));
|
|
62
|
+
return { vocabulary, vectors };
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=vectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vectors.js","sourceRoot":"","sources":["../../../src/tools/lib/vectors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"podcast-feed.d.ts","sourceRoot":"","sources":["../../src/tools/podcast-feed.ts"],"names":[],"mappings":";AACA;;GAEG"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Generate Podcast RSS Feed
|
|
4
|
+
*/
|
|
5
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
const DATA_DIR = join(process.cwd(), "datas");
|
|
8
|
+
const PUBLIC_DIR = join(process.cwd(), "public");
|
|
9
|
+
const AUDIO_FILE = join(DATA_DIR, "podcast-audio.json");
|
|
10
|
+
const SCRIPTS_FILE = join(DATA_DIR, "podcast-scripts.json");
|
|
11
|
+
async function loadSiteConfig() {
|
|
12
|
+
try {
|
|
13
|
+
const configPath = join(process.cwd(), "src", "config.ts");
|
|
14
|
+
const raw = await readFile(configPath, "utf-8");
|
|
15
|
+
const websiteMatch = raw.match(/website:\s*["']([^"']+)["']/);
|
|
16
|
+
const titleMatch = raw.match(/title:\s*["']([^"']+)["']/);
|
|
17
|
+
const descMatch = raw.match(/desc:\s*["']([^"']+)["']/);
|
|
18
|
+
const authorMatch = raw.match(/author:\s*["']([^"']+)["']/);
|
|
19
|
+
return {
|
|
20
|
+
website: websiteMatch?.[1] || process.env.SITE_URL || "https://example.com",
|
|
21
|
+
title: titleMatch?.[1] || "Blog Podcast",
|
|
22
|
+
desc: descMatch?.[1] || "AI-generated podcast from blog posts",
|
|
23
|
+
author: authorMatch?.[1] || "Author",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return {
|
|
28
|
+
website: process.env.SITE_URL || "https://example.com",
|
|
29
|
+
title: "Blog Podcast",
|
|
30
|
+
desc: "AI-generated podcast",
|
|
31
|
+
author: "Author",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function escapeXml(str) {
|
|
36
|
+
return str
|
|
37
|
+
.replace(/&/g, "&")
|
|
38
|
+
.replace(/</g, "<")
|
|
39
|
+
.replace(/>/g, ">")
|
|
40
|
+
.replace(/"/g, """)
|
|
41
|
+
.replace(/'/g, "'");
|
|
42
|
+
}
|
|
43
|
+
function generateRss(site, episodes) {
|
|
44
|
+
const items = episodes
|
|
45
|
+
.map((ep) => {
|
|
46
|
+
const audioUrl = `${site.website}/podcasts/${ep.lang}/${ep.slug.split("/")[1]}.mp3`;
|
|
47
|
+
return `
|
|
48
|
+
<item>
|
|
49
|
+
<title>${escapeXml(ep.title)}</title>
|
|
50
|
+
<description>AI-generated podcast episode from blog post: ${escapeXml(ep.title)}</description>
|
|
51
|
+
<enclosure url="${audioUrl}" length="${ep.fileSize}" type="audio/mpeg"/>
|
|
52
|
+
<guid isPermaLink="true">${audioUrl}</guid>
|
|
53
|
+
<pubDate>${new Date(ep.generatedAt).toUTCString()}</pubDate>
|
|
54
|
+
<itunes:duration>${Math.ceil(ep.duration)}</itunes:duration>
|
|
55
|
+
</item>`;
|
|
56
|
+
})
|
|
57
|
+
.join("");
|
|
58
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
59
|
+
<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
60
|
+
<channel>
|
|
61
|
+
<title>${escapeXml(site.title)} Podcast</title>
|
|
62
|
+
<description>${escapeXml(site.desc)}</description>
|
|
63
|
+
<link>${site.website}</link>
|
|
64
|
+
<atom:link href="${site.website}/podcast.xml" rel="self" type="application/rss+xml"/>
|
|
65
|
+
<language>zh-cn</language>
|
|
66
|
+
<itunes:author>${escapeXml(site.author)}</itunes:author>
|
|
67
|
+
<itunes:summary>${escapeXml(site.desc)}</itunes:summary>
|
|
68
|
+
<itunes:category text="Technology"/>
|
|
69
|
+
<itunes:explicit>false</itunes:explicit>
|
|
70
|
+
${items}
|
|
71
|
+
</channel>
|
|
72
|
+
</rss>`;
|
|
73
|
+
}
|
|
74
|
+
async function main() {
|
|
75
|
+
const site = await loadSiteConfig();
|
|
76
|
+
let audioData = {
|
|
77
|
+
articles: {},
|
|
78
|
+
};
|
|
79
|
+
try {
|
|
80
|
+
const raw = await readFile(AUDIO_FILE, "utf-8");
|
|
81
|
+
audioData = JSON.parse(raw);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
console.log("No audio data found. Run 'astro-minimax podcast generate' first.");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
let scriptsData = {
|
|
88
|
+
articles: {},
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
const raw = await readFile(SCRIPTS_FILE, "utf-8");
|
|
92
|
+
scriptsData = JSON.parse(raw);
|
|
93
|
+
}
|
|
94
|
+
catch { }
|
|
95
|
+
const episodes = [];
|
|
96
|
+
for (const [slug, meta] of Object.entries(audioData.articles)) {
|
|
97
|
+
const script = scriptsData.articles[slug];
|
|
98
|
+
episodes.push({
|
|
99
|
+
slug,
|
|
100
|
+
title: script?.title || slug.split("/")[1] || slug,
|
|
101
|
+
lang: meta.lang,
|
|
102
|
+
duration: meta.duration,
|
|
103
|
+
fileSize: meta.fileSize,
|
|
104
|
+
generatedAt: meta.generatedAt,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
episodes.sort((a, b) => new Date(b.generatedAt).getTime() - new Date(a.generatedAt).getTime());
|
|
108
|
+
if (episodes.length === 0) {
|
|
109
|
+
console.log("No episodes found.");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const rss = generateRss(site, episodes);
|
|
113
|
+
const outputPath = join(PUBLIC_DIR, "podcast.xml");
|
|
114
|
+
await writeFile(outputPath, rss, "utf-8");
|
|
115
|
+
console.log("Generated podcast RSS feed");
|
|
116
|
+
console.log(` Path: ${outputPath}`);
|
|
117
|
+
console.log(` Episodes: ${episodes.length}`);
|
|
118
|
+
console.log(` URL: ${site.website}/podcast.xml`);
|
|
119
|
+
}
|
|
120
|
+
main().catch(console.error);
|
|
121
|
+
//# sourceMappingURL=podcast-feed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"podcast-feed.js","sourceRoot":"","sources":["../../src/tools/podcast-feed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Podcast Generation Script
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* pnpm podcast:generate Generate all podcasts
|
|
7
|
+
* pnpm podcast:generate --slug=zh/xxx Single article
|
|
8
|
+
* pnpm podcast:generate --task=script Script only
|
|
9
|
+
* pnpm podcast:generate --task=audio Audio only
|
|
10
|
+
* pnpm podcast:generate --force Ignore cache
|
|
11
|
+
* pnpm podcast:generate --dry-run Preview only
|
|
12
|
+
* pnpm podcast:generate --lang=zh Specific language
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=podcast-generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"podcast-generate.d.ts","sourceRoot":"","sources":["../../src/tools/podcast-generate.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG"}
|