zengen 0.1.36 → 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.
Files changed (153) hide show
  1. package/.github/workflows/pages.yml +1 -1
  2. package/.zen/meta.json +128 -30
  3. package/.zen/src/en-US/01d04f7c17b4a541ead9d759d877b30b403e15b849182a49eb1f62bd29ecd18c.md +120 -0
  4. package/.zen/src/en-US/1b798c44a4f353e47296ca83d5905e37e6aba3e90bbd9bc3b3d34fc12059a2ca.md +75 -0
  5. package/.zen/src/en-US/1e96be58d76c60056b708eb5bd8b8b81d7b5845d9cfe0b879d85068a5f11df3a.md +189 -0
  6. package/.zen/src/en-US/5ec990146b35e00de2630559126ee07f7cdcddeb23b0e8cab3d85b4181353e26.md +53 -0
  7. package/.zen/src/en-US/6124ea88edec5bde737b26b21f71ecfeffe4e73151784856edf813ee231a4baa.md +11 -0
  8. package/.zen/src/en-US/80ae9bed74fc6348a7c1fe9f33e86b65f5d919169721f77bcf0e1bc29fbdb4f9.md +61 -0
  9. package/.zen/src/en-US/f0c2799126931ccd113a0c45b1e623870b0d4f4f400becf6dd877da8f1011517.md +41 -0
  10. package/.zen/src/en-US/fdfca9b960d0eaa8b2b96fe988ead7481d2c0b16f66ebc94fb477139b4178cdc.md +65 -0
  11. package/.zen/src/zh-Hans/01d04f7c17b4a541ead9d759d877b30b403e15b849182a49eb1f62bd29ecd18c.md +120 -0
  12. package/.zen/src/zh-Hans/1b798c44a4f353e47296ca83d5905e37e6aba3e90bbd9bc3b3d34fc12059a2ca.md +77 -0
  13. package/.zen/src/zh-Hans/1e96be58d76c60056b708eb5bd8b8b81d7b5845d9cfe0b879d85068a5f11df3a.md +189 -0
  14. package/.zen/src/zh-Hans/5ec990146b35e00de2630559126ee07f7cdcddeb23b0e8cab3d85b4181353e26.md +55 -0
  15. package/.zen/src/zh-Hans/6124ea88edec5bde737b26b21f71ecfeffe4e73151784856edf813ee231a4baa.md +1 -0
  16. package/.zen/src/zh-Hans/6ad8db715a1b60613fe934fefb29fa981ecad9b63145593accff144d73b44bde.md +175 -0
  17. package/.zen/src/zh-Hans/80ae9bed74fc6348a7c1fe9f33e86b65f5d919169721f77bcf0e1bc29fbdb4f9.md +63 -0
  18. package/.zen/src/zh-Hans/a1580f71c6c6c1ff4a314be72d410a8507af2f087d56360c7f5048d349c21953.md +48 -0
  19. package/.zen/src/zh-Hans/d49012f98c4367b34034063400e2f7826bf0615952210c82396920172d468e2c.md +107 -0
  20. package/.zen/src/zh-Hans/f0c2799126931ccd113a0c45b1e623870b0d4f4f400becf6dd877da8f1011517.md +41 -0
  21. package/.zen/src/zh-Hans/fdfca9b960d0eaa8b2b96fe988ead7481d2c0b16f66ebc94fb477139b4178cdc.md +65 -0
  22. package/assets/templates/default/layout.html +274 -0
  23. package/dist/ai/extractMetadataFromMarkdown.d.ts +8 -0
  24. package/dist/ai/extractMetadataFromMarkdown.d.ts.map +1 -0
  25. package/dist/ai/extractMetadataFromMarkdown.js +88 -0
  26. package/dist/ai/extractMetadataFromMarkdown.js.map +1 -0
  27. package/dist/ai/translateMarkdown.d.ts +8 -0
  28. package/dist/ai/translateMarkdown.d.ts.map +1 -0
  29. package/dist/ai/translateMarkdown.js +29 -0
  30. package/dist/ai/translateMarkdown.js.map +1 -0
  31. package/dist/build/pipeline.d.ts +6 -0
  32. package/dist/build/pipeline.d.ts.map +1 -0
  33. package/dist/build/pipeline.js +218 -0
  34. package/dist/build/pipeline.js.map +1 -0
  35. package/dist/cli.js +10 -118
  36. package/dist/cli.js.map +1 -1
  37. package/dist/findEntries.d.ts +10 -0
  38. package/dist/findEntries.d.ts.map +1 -0
  39. package/dist/findEntries.js +38 -0
  40. package/dist/findEntries.js.map +1 -0
  41. package/dist/index.d.ts +1 -32
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1 -35
  44. package/dist/index.js.map +1 -1
  45. package/dist/metadata.d.ts +14 -0
  46. package/dist/metadata.d.ts.map +1 -0
  47. package/dist/metadata.js +78 -0
  48. package/dist/metadata.js.map +1 -0
  49. package/dist/paths.d.ts +6 -0
  50. package/dist/paths.d.ts.map +1 -0
  51. package/dist/paths.js +10 -0
  52. package/dist/paths.js.map +1 -0
  53. package/dist/process/extractMetadataByAI.d.ts +5 -0
  54. package/dist/process/extractMetadataByAI.d.ts.map +1 -0
  55. package/dist/process/extractMetadataByAI.js +31 -0
  56. package/dist/process/extractMetadataByAI.js.map +1 -0
  57. package/dist/process/template.d.ts +5 -0
  58. package/dist/process/template.d.ts.map +1 -0
  59. package/dist/process/template.js +188 -0
  60. package/dist/process/template.js.map +1 -0
  61. package/dist/scan/files.d.ts +7 -0
  62. package/dist/scan/files.d.ts.map +1 -0
  63. package/dist/scan/files.js +54 -0
  64. package/dist/scan/files.js.map +1 -0
  65. package/dist/services/openai.d.ts +41 -0
  66. package/dist/services/openai.d.ts.map +1 -0
  67. package/dist/services/openai.js +54 -0
  68. package/dist/services/openai.js.map +1 -0
  69. package/dist/types.d.ts +16 -67
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/utils/convertMarkdownToHtml.d.ts +7 -0
  72. package/dist/utils/convertMarkdownToHtml.d.ts.map +1 -0
  73. package/dist/utils/convertMarkdownToHtml.js +39 -0
  74. package/dist/utils/convertMarkdownToHtml.js.map +1 -0
  75. package/dist/utils/frontmatter.d.ts +6 -0
  76. package/dist/utils/frontmatter.d.ts.map +1 -0
  77. package/dist/utils/frontmatter.js +22 -0
  78. package/dist/utils/frontmatter.js.map +1 -0
  79. package/docs/deployment/github-pages.md +1 -2
  80. package/docs/guides/best-practices.md +4 -4
  81. package/docs/guides/config.md +0 -5
  82. package/package.json +4 -2
  83. package/src/ai/extractMetadataFromMarkdown.ts +95 -0
  84. package/src/ai/translateMarkdown.ts +29 -0
  85. package/src/build/pipeline.ts +211 -0
  86. package/src/cli.ts +10 -132
  87. package/src/findEntries.ts +37 -0
  88. package/src/index.ts +1 -40
  89. package/src/metadata.ts +44 -0
  90. package/src/paths.ts +7 -0
  91. package/src/process/extractMetadataByAI.ts +29 -0
  92. package/src/process/template.ts +201 -0
  93. package/src/scan/files.ts +17 -0
  94. package/src/services/openai.ts +92 -0
  95. package/src/types.ts +18 -72
  96. package/src/utils/convertMarkdownToHtml.ts +32 -0
  97. package/src/utils/frontmatter.ts +18 -0
  98. package/.zen/translations.json +0 -51
  99. package/dist/ai-client.d.ts +0 -34
  100. package/dist/ai-client.d.ts.map +0 -1
  101. package/dist/ai-client.js +0 -180
  102. package/dist/ai-client.js.map +0 -1
  103. package/dist/ai-processor.d.ts +0 -51
  104. package/dist/ai-processor.d.ts.map +0 -1
  105. package/dist/ai-processor.js +0 -215
  106. package/dist/ai-processor.js.map +0 -1
  107. package/dist/ai-service.d.ts +0 -79
  108. package/dist/ai-service.d.ts.map +0 -1
  109. package/dist/ai-service.js +0 -257
  110. package/dist/ai-service.js.map +0 -1
  111. package/dist/builder.d.ts +0 -70
  112. package/dist/builder.d.ts.map +0 -1
  113. package/dist/builder.js +0 -854
  114. package/dist/builder.js.map +0 -1
  115. package/dist/gitignore.d.ts +0 -41
  116. package/dist/gitignore.d.ts.map +0 -1
  117. package/dist/gitignore.js +0 -202
  118. package/dist/gitignore.js.map +0 -1
  119. package/dist/gitignore.test.d.ts +0 -2
  120. package/dist/gitignore.test.d.ts.map +0 -1
  121. package/dist/gitignore.test.js +0 -309
  122. package/dist/gitignore.test.js.map +0 -1
  123. package/dist/markdown.d.ts +0 -35
  124. package/dist/markdown.d.ts.map +0 -1
  125. package/dist/markdown.js +0 -221
  126. package/dist/markdown.js.map +0 -1
  127. package/dist/navigation.d.ts +0 -46
  128. package/dist/navigation.d.ts.map +0 -1
  129. package/dist/navigation.js +0 -196
  130. package/dist/navigation.js.map +0 -1
  131. package/dist/scanner.d.ts +0 -26
  132. package/dist/scanner.d.ts.map +0 -1
  133. package/dist/scanner.js +0 -190
  134. package/dist/scanner.js.map +0 -1
  135. package/dist/template.d.ts +0 -33
  136. package/dist/template.d.ts.map +0 -1
  137. package/dist/template.js +0 -434
  138. package/dist/template.js.map +0 -1
  139. package/dist/translation-service.d.ts +0 -72
  140. package/dist/translation-service.d.ts.map +0 -1
  141. package/dist/translation-service.js +0 -291
  142. package/dist/translation-service.js.map +0 -1
  143. package/src/ai-client.ts +0 -227
  144. package/src/ai-processor.ts +0 -243
  145. package/src/ai-service.ts +0 -281
  146. package/src/builder.ts +0 -991
  147. package/src/gitignore.test.ts +0 -318
  148. package/src/gitignore.ts +0 -193
  149. package/src/markdown.ts +0 -212
  150. package/src/navigation.ts +0 -237
  151. package/src/scanner.ts +0 -180
  152. package/src/template.ts +0 -425
  153. package/src/translation-service.ts +0 -350
@@ -1,72 +0,0 @@
1
- import { FileInfo } from './types';
2
- /**
3
- * 翻译缓存项
4
- */
5
- export interface TranslationCache {
6
- sourceHash: string;
7
- sourceLang: string;
8
- targetLang: string;
9
- translatedContent: string;
10
- lastUpdated: string;
11
- }
12
- /**
13
- * 翻译服务配置
14
- */
15
- export interface TranslationConfig {
16
- enabled: boolean;
17
- apiKey: string;
18
- baseUrl: string;
19
- model: string;
20
- temperature: number;
21
- maxTokens: number;
22
- }
23
- /**
24
- * 翻译服务类
25
- */
26
- export declare class TranslationService {
27
- private config;
28
- private aiService;
29
- private translationCachePath;
30
- constructor(config?: Partial<TranslationConfig>);
31
- /**
32
- * 检查是否启用翻译功能
33
- */
34
- isEnabled(): boolean;
35
- /**
36
- * 加载翻译缓存
37
- */
38
- loadTranslationCache(): Promise<TranslationCache[]>;
39
- /**
40
- * 保存翻译缓存
41
- */
42
- saveTranslationCache(cache: TranslationCache[]): Promise<void>;
43
- /**
44
- * 获取缓存的翻译
45
- */
46
- getCachedTranslation(sourceHash: string, sourceLang: string, targetLang: string): Promise<string | null>;
47
- /**
48
- * 缓存翻译结果
49
- */
50
- cacheTranslation(sourceHash: string, sourceLang: string, targetLang: string, translatedContent: string): Promise<void>;
51
- /**
52
- * 使用AI翻译内容
53
- */
54
- translateWithAI(content: string, sourceLang: string, targetLang: string): Promise<string>;
55
- /**
56
- * 翻译文件
57
- */
58
- translateFile(fileInfo: FileInfo, sourceLang: string, targetLang: string): Promise<string>;
59
- /**
60
- * 生成翻译后的文件路径
61
- */
62
- getTranslatedFilePath(originalPath: string, targetLang: string, nativeHash: string): string;
63
- /**
64
- * 确保翻译文件存在
65
- */
66
- ensureTranslatedFile(fileInfo: FileInfo, sourceLang: string, targetLang: string, nativeHash: string): Promise<string>;
67
- /**
68
- * 清理过期的翻译缓存
69
- */
70
- cleanupCache(maxAgeDays?: number): Promise<void>;
71
- }
72
- //# sourceMappingURL=translation-service.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"translation-service.d.ts","sourceRoot":"","sources":["../src/translation-service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG/C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,oBAAoB,CAAS;gBAEzB,MAAM,GAAE,OAAO,CAAC,iBAAiB,CAAM;IAqBnD;;OAEG;IACH,SAAS,IAAI,OAAO;IAUpB;;OAEG;IACG,oBAAoB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAWzD;;OAEG;IACG,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IASpE;;OAEG;IACG,oBAAoB,CACxB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAyBzB;;OAEG;IACG,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC;IA2ChB;;OAEG;IACG,eAAe,CACnB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IAsDlB;;OAEG;IACG,aAAa,CACjB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IA0BlB;;OAEG;IACH,qBAAqB,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,MAAM;IAOT;;OAEG;IACG,oBAAoB,CACxB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IA6BlB;;OAEG;IACG,YAAY,CAAC,UAAU,GAAE,MAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAqB3D"}
@@ -1,291 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.TranslationService = void 0;
37
- const fs = __importStar(require("fs/promises"));
38
- const path = __importStar(require("path"));
39
- const ai_service_1 = require("./ai-service");
40
- /**
41
- * 翻译服务类
42
- */
43
- class TranslationService {
44
- constructor(config = {}) {
45
- // 从环境变量读取配置
46
- const apiKey = process.env.OPENAI_API_KEY || '';
47
- const baseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
48
- // 配置优先级:构造函数参数 > 环境变量 > 默认值
49
- const model = config.model || process.env.OPENAI_MODEL || 'gpt-3.5-turbo';
50
- this.config = {
51
- enabled: config.enabled ?? apiKey !== '',
52
- apiKey,
53
- baseUrl,
54
- model,
55
- temperature: 0, // 总是设置为 0,翻译不需要随机性
56
- maxTokens: config.maxTokens || 2000,
57
- };
58
- this.aiService = new ai_service_1.AIService();
59
- this.translationCachePath = path.join(process.cwd(), '.zen', 'translations.json');
60
- }
61
- /**
62
- * 检查是否启用翻译功能
63
- */
64
- isEnabled() {
65
- const enabled = this.config.enabled && this.config.apiKey !== '';
66
- if (!enabled && this.config.enabled) {
67
- console.warn('⚠️ Translation is enabled but API key is missing. Please set OPENAI_API_KEY environment variable.');
68
- }
69
- return enabled;
70
- }
71
- /**
72
- * 加载翻译缓存
73
- */
74
- async loadTranslationCache() {
75
- try {
76
- await fs.access(this.translationCachePath);
77
- const content = await fs.readFile(this.translationCachePath, 'utf-8');
78
- return JSON.parse(content);
79
- }
80
- catch (error) {
81
- // 如果文件不存在,返回空数组
82
- return [];
83
- }
84
- }
85
- /**
86
- * 保存翻译缓存
87
- */
88
- async saveTranslationCache(cache) {
89
- // 确保 .zen 目录存在
90
- const zenDir = path.dirname(this.translationCachePath);
91
- await fs.mkdir(zenDir, { recursive: true });
92
- // 保存文件
93
- await fs.writeFile(this.translationCachePath, JSON.stringify(cache, null, 2), 'utf-8');
94
- }
95
- /**
96
- * 获取缓存的翻译
97
- */
98
- async getCachedTranslation(sourceHash, sourceLang, targetLang) {
99
- if (!this.isEnabled()) {
100
- return null;
101
- }
102
- try {
103
- const cache = await this.loadTranslationCache();
104
- const cachedTranslation = cache.find(item => item.sourceHash === sourceHash &&
105
- item.sourceLang === sourceLang &&
106
- item.targetLang === targetLang);
107
- if (cachedTranslation) {
108
- console.log(`📚 Using cached translation for ${sourceHash} (${sourceLang} → ${targetLang})`);
109
- return cachedTranslation.translatedContent;
110
- }
111
- }
112
- catch (error) {
113
- console.warn(`⚠️ Failed to load translation cache:`, error);
114
- }
115
- return null;
116
- }
117
- /**
118
- * 缓存翻译结果
119
- */
120
- async cacheTranslation(sourceHash, sourceLang, targetLang, translatedContent) {
121
- if (!this.isEnabled()) {
122
- return;
123
- }
124
- try {
125
- const cache = await this.loadTranslationCache();
126
- // 查找是否已存在相同翻译
127
- const existingIndex = cache.findIndex(item => item.sourceHash === sourceHash &&
128
- item.sourceLang === sourceLang &&
129
- item.targetLang === targetLang);
130
- if (existingIndex >= 0) {
131
- // 更新现有缓存
132
- cache[existingIndex] = {
133
- sourceHash,
134
- sourceLang,
135
- targetLang,
136
- translatedContent,
137
- lastUpdated: new Date().toISOString(),
138
- };
139
- }
140
- else {
141
- // 添加新缓存
142
- cache.push({
143
- sourceHash,
144
- sourceLang,
145
- targetLang,
146
- translatedContent,
147
- lastUpdated: new Date().toISOString(),
148
- });
149
- }
150
- await this.saveTranslationCache(cache);
151
- console.log(`💾 Cached translation for ${sourceHash} (${sourceLang} → ${targetLang})`);
152
- }
153
- catch (error) {
154
- console.warn(`⚠️ Failed to cache translation:`, error);
155
- }
156
- }
157
- /**
158
- * 使用AI翻译内容
159
- */
160
- async translateWithAI(content, sourceLang, targetLang) {
161
- if (!this.isEnabled()) {
162
- throw new Error('Translation service is not enabled');
163
- }
164
- const prompt = `请将以下${sourceLang}文本翻译成${targetLang}。保持Markdown格式不变,只翻译文本内容:
165
-
166
- ${content}
167
-
168
- 翻译结果(保持原格式):`;
169
- try {
170
- const response = await fetch(`${this.config.baseUrl}/chat/completions`, {
171
- method: 'POST',
172
- headers: {
173
- 'Content-Type': 'application/json',
174
- 'Authorization': `Bearer ${this.config.apiKey}`,
175
- },
176
- body: JSON.stringify({
177
- model: this.config.model,
178
- messages: [
179
- {
180
- role: 'system',
181
- content: '你是一个专业的翻译助手,擅长将文档翻译成不同语言,同时保持原有的格式和结构。'
182
- },
183
- {
184
- role: 'user',
185
- content: prompt
186
- }
187
- ],
188
- temperature: this.config.temperature,
189
- max_tokens: this.config.maxTokens,
190
- }),
191
- });
192
- if (!response.ok) {
193
- const errorText = await response.text();
194
- throw new Error(`Translation API error: ${response.status} ${errorText}`);
195
- }
196
- const data = await response.json();
197
- const translatedContent = data.choices[0]?.message?.content?.trim() || '';
198
- if (!translatedContent) {
199
- throw new Error('Empty translation response');
200
- }
201
- return translatedContent;
202
- }
203
- catch (error) {
204
- console.error(`❌ Translation failed:`, error);
205
- throw error;
206
- }
207
- }
208
- /**
209
- * 翻译文件
210
- */
211
- async translateFile(fileInfo, sourceLang, targetLang) {
212
- const sourceHash = fileInfo.hash || this.aiService.calculateFileHash(fileInfo.content);
213
- // 检查缓存
214
- const cachedTranslation = await this.getCachedTranslation(sourceHash, sourceLang, targetLang);
215
- if (cachedTranslation) {
216
- return cachedTranslation;
217
- }
218
- // 如果目标语言与源语言相同,直接返回原内容
219
- if (sourceLang === targetLang) {
220
- console.log(`📝 Skipping translation (same language): ${sourceLang} → ${targetLang}`);
221
- await this.cacheTranslation(sourceHash, sourceLang, targetLang, fileInfo.content);
222
- return fileInfo.content;
223
- }
224
- // 使用AI翻译
225
- console.log(`🌐 Translating from ${sourceLang} to ${targetLang}...`);
226
- const translatedContent = await this.translateWithAI(fileInfo.content, sourceLang, targetLang);
227
- // 缓存结果
228
- await this.cacheTranslation(sourceHash, sourceLang, targetLang, translatedContent);
229
- return translatedContent;
230
- }
231
- /**
232
- * 生成翻译后的文件路径
233
- */
234
- getTranslatedFilePath(originalPath, targetLang, nativeHash) {
235
- const zenSrcDir = path.join(process.cwd(), '.zen', 'src');
236
- const langDir = path.join(zenSrcDir, targetLang);
237
- const fileName = `${nativeHash}.md`;
238
- return path.join(langDir, fileName);
239
- }
240
- /**
241
- * 确保翻译文件存在
242
- */
243
- async ensureTranslatedFile(fileInfo, sourceLang, targetLang, nativeHash) {
244
- const translatedFilePath = this.getTranslatedFilePath(fileInfo.path, targetLang, nativeHash);
245
- try {
246
- // 检查文件是否已存在
247
- await fs.access(translatedFilePath);
248
- console.log(`📄 Translation file already exists: ${translatedFilePath}`);
249
- // 读取现有内容
250
- const existingContent = await fs.readFile(translatedFilePath, 'utf-8');
251
- return existingContent;
252
- }
253
- catch (error) {
254
- // 文件不存在,需要翻译
255
- console.log(`🔄 Creating translation file: ${translatedFilePath}`);
256
- // 翻译内容
257
- const translatedContent = await this.translateFile(fileInfo, sourceLang, targetLang);
258
- // 确保目录存在
259
- const dirPath = path.dirname(translatedFilePath);
260
- await fs.mkdir(dirPath, { recursive: true });
261
- // 保存翻译文件
262
- await fs.writeFile(translatedFilePath, translatedContent, 'utf-8');
263
- return translatedContent;
264
- }
265
- }
266
- /**
267
- * 清理过期的翻译缓存
268
- */
269
- async cleanupCache(maxAgeDays = 30) {
270
- try {
271
- const cache = await this.loadTranslationCache();
272
- const cutoffTime = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
273
- const originalCount = cache.length;
274
- // 过滤掉过期的缓存
275
- const filteredCache = cache.filter(item => {
276
- const itemTime = new Date(item.lastUpdated).getTime();
277
- return itemTime >= cutoffTime;
278
- });
279
- const cleanedCount = originalCount - filteredCache.length;
280
- if (cleanedCount > 0) {
281
- await this.saveTranslationCache(filteredCache);
282
- console.log(`🧹 Cleaned ${cleanedCount} expired translation cache entries`);
283
- }
284
- }
285
- catch (error) {
286
- console.warn(`⚠️ Failed to cleanup translation cache:`, error);
287
- }
288
- }
289
- }
290
- exports.TranslationService = TranslationService;
291
- //# sourceMappingURL=translation-service.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"translation-service.js","sourceRoot":"","sources":["../src/translation-service.ts"],"names":[],"mappings":"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"}
package/src/ai-client.ts DELETED
@@ -1,227 +0,0 @@
1
- import { AIMetadata } from './types';
2
- import { AIService, AIConfig } from './ai-service';
3
-
4
- /**
5
- * OpenAI 兼容 API 响应接口
6
- */
7
- interface OpenAIResponse {
8
- id: string;
9
- object: string;
10
- created: number;
11
- model: string;
12
- choices: Array<{
13
- index: number;
14
- message: {
15
- role: string;
16
- content: string;
17
- };
18
- finish_reason: string;
19
- }>;
20
- usage: {
21
- prompt_tokens: number;
22
- completion_tokens: number;
23
- total_tokens: number;
24
- };
25
- }
26
-
27
- /**
28
- * AI 客户端类 - 使用 fetch 调用 OpenAI 兼容 API
29
- */
30
- export class AIClient {
31
- private aiService: AIService;
32
-
33
- constructor(aiService: AIService) {
34
- this.aiService = aiService;
35
- }
36
-
37
- /**
38
- * 调用 AI 模型提取文档 metadata
39
- */
40
- async extractMetadata(content: string, filePath: string): Promise<AIMetadata | null> {
41
- const config = this.aiService.getConfig();
42
-
43
- if (!config.enabled || !config.apiKey) {
44
- console.log(`⚠️ AI feature is disabled or API key not configured for: ${filePath}`);
45
- return null;
46
- }
47
-
48
- try {
49
- console.log(`🤖 Extracting AI metadata for: ${filePath}`);
50
-
51
- const prompt = this.buildMetadataPrompt(content);
52
- const response = await this.callOpenAIAPI(prompt, config);
53
-
54
- if (!response) {
55
- console.warn(`⚠️ Failed to extract metadata for: ${filePath}`);
56
- return null;
57
- }
58
-
59
- const metadata = this.parseMetadataResponse(response.choices[0].message.content);
60
-
61
- // 添加 tokens 使用情况
62
- metadata.tokens_used = {
63
- prompt: response.usage.prompt_tokens,
64
- completion: response.usage.completion_tokens,
65
- total: response.usage.total_tokens,
66
- };
67
-
68
- // 打印 tokens 使用情况
69
- this.aiService.logTokenUsage(filePath, metadata.tokens_used);
70
-
71
- return metadata;
72
- } catch (error) {
73
- console.error(`❌ Failed to extract AI metadata for ${filePath}:`, error);
74
- return null;
75
- }
76
- }
77
-
78
- /**
79
- * 构建提取 metadata 的 prompt
80
- */
81
- private buildMetadataPrompt(content: string): string {
82
- // 限制内容长度以避免 token 超限
83
- const maxContentLength = 8000;
84
- const truncatedContent =
85
- content.length > maxContentLength
86
- ? content.substring(0, maxContentLength) + '... [内容已截断]'
87
- : content;
88
-
89
- return `请分析以下文档内容,提取以下信息并返回 JSON 格式:
90
-
91
- 文档内容:
92
- """
93
- ${truncatedContent}
94
- """
95
-
96
- 请提取:
97
- 1. title: 文档的标题(简洁明了,不超过 20 个字)
98
- 2. summary: 文档摘要(控制在 100 字以内,概括主要内容)
99
- 3. tags: 关键词列表(3-8 个关键词,使用中文或英文)
100
- 4. inferred_date: 文档中隐含的创建日期(如果有的话,格式:YYYY-MM-DD,没有就留空字符串)
101
- 5. inferred_lang: 文档使用的语言代码(例如:zh-Hans 表示简体中文,en-US 表示美式英语)
102
-
103
- 请严格按照以下 JSON 格式返回,不要包含任何其他文本:
104
- {
105
- "title": "文档标题",
106
- "summary": "文档摘要...",
107
- "tags": ["关键词1", "关键词2", "关键词3"],
108
- "inferred_date": "2023-01-01",
109
- "inferred_lang": "zh-Hans"
110
- }`;
111
- }
112
-
113
- /**
114
- * 调用 OpenAI 兼容 API
115
- */
116
- private async callOpenAIAPI(prompt: string, config: AIConfig): Promise<OpenAIResponse | null> {
117
- try {
118
- const response = await fetch(`${config.baseUrl}/chat/completions`, {
119
- method: 'POST',
120
- headers: {
121
- 'Content-Type': 'application/json',
122
- Authorization: `Bearer ${config.apiKey}`,
123
- },
124
- body: JSON.stringify({
125
- model: config.model,
126
- messages: [
127
- {
128
- role: 'system',
129
- content:
130
- '你是一个专业的文档分析助手,擅长从文档中提取结构化信息。请严格按照要求的 JSON 格式返回结果。',
131
- },
132
- {
133
- role: 'user',
134
- content: prompt,
135
- },
136
- ],
137
- temperature: config.temperature,
138
- max_tokens: config.maxTokens,
139
- response_format: { type: 'json_object' },
140
- }),
141
- });
142
-
143
- if (!response.ok) {
144
- const errorText = await response.text();
145
- console.error(`❌ OpenAI API error (${response.status}):`, errorText);
146
- return null;
147
- }
148
-
149
- const data: OpenAIResponse = await response.json();
150
- return data;
151
- } catch (error) {
152
- console.error('❌ Failed to call OpenAI API:', error);
153
- return null;
154
- }
155
- }
156
-
157
- /**
158
- * 解析 AI 返回的 metadata
159
- */
160
- private parseMetadataResponse(responseContent: string): AIMetadata {
161
- try {
162
- const metadata = JSON.parse(responseContent);
163
-
164
- // 验证和清理数据
165
- return {
166
- title: metadata.title?.trim() || '未命名文档',
167
- summary: metadata.summary?.trim() || '',
168
- tags: Array.isArray(metadata.tags)
169
- ? metadata.tags.map((tag: string) => tag.trim()).filter(Boolean)
170
- : [],
171
- inferred_date: metadata.inferred_date?.trim() || undefined,
172
- inferred_lang: metadata.inferred_lang?.trim() || 'zh-Hans',
173
- };
174
- } catch (error) {
175
- console.error('❌ Failed to parse AI response:', error, 'Response:', responseContent);
176
-
177
- // 返回默认值
178
- return {
179
- title: '解析失败',
180
- summary: 'AI 响应解析失败',
181
- tags: ['error'],
182
- inferred_lang: 'zh-Hans',
183
- };
184
- }
185
- }
186
-
187
- /**
188
- * 批量处理文件
189
- */
190
- async processFiles(
191
- files: Array<{ content: string; path: string; hash: string }>
192
- ): Promise<Map<string, AIMetadata>> {
193
- const results = new Map<string, AIMetadata>();
194
- const config = this.aiService.getConfig();
195
-
196
- if (!config.enabled || !config.apiKey) {
197
- console.log('⚠️ AI feature is disabled or API key not configured');
198
- return results;
199
- }
200
-
201
- console.log(`🤖 Processing ${files.length} files with AI...`);
202
-
203
- for (const file of files) {
204
- try {
205
- // 检查缓存
206
- const cachedMetadata = await this.aiService.getCachedMetadata(file.hash, file.path);
207
- if (cachedMetadata) {
208
- results.set(file.path, cachedMetadata);
209
- continue;
210
- }
211
-
212
- // 调用 AI 提取 metadata
213
- const metadata = await this.extractMetadata(file.content, file.path);
214
- if (metadata) {
215
- results.set(file.path, metadata);
216
-
217
- // 缓存结果
218
- await this.aiService.cacheMetadata(file.hash, file.path, metadata);
219
- }
220
- } catch (error) {
221
- console.error(`❌ Failed to process file ${file.path}:`, error);
222
- }
223
- }
224
-
225
- return results;
226
- }
227
- }