cerevox 2.7.0 → 2.8.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.
@@ -170,35 +170,41 @@ async function sendProgress(context, progress, total, message) {
170
170
  params: { progressToken: token, progress, total, message },
171
171
  });
172
172
  }
173
- async function updateMediaDurations(session, fileName, durationMs) {
173
+ async function updateMediaLogs(session, fileName, result, mediaType = 'video') {
174
174
  try {
175
- const mediaDurationsPath = node_path_1.default.resolve(projectLocalDir, 'media_durations.json');
176
- let mediaDurations = [];
175
+ const mediaLogsPath = node_path_1.default.resolve(projectLocalDir, 'media_logs.json');
176
+ let mediaLogs = [];
177
177
  // 尝试读取现有文件
178
178
  try {
179
- const existingContent = await (0, promises_1.readFile)(mediaDurationsPath, 'utf-8');
180
- mediaDurations = JSON.parse(existingContent);
179
+ const existingContent = await (0, promises_1.readFile)(mediaLogsPath, 'utf-8');
180
+ mediaLogs = JSON.parse(existingContent);
181
181
  }
182
182
  catch (error) {
183
183
  // 文件不存在或格式错误,使用空数组
184
- mediaDurations = [];
184
+ mediaLogs = [];
185
185
  }
186
186
  // 检查是否已存在相同文件名的记录
187
- const existingIndex = mediaDurations.findIndex(item => item.fileName === fileName);
187
+ const existingIndex = mediaLogs.findIndex(item => item.fileName === fileName);
188
+ const logEntry = {
189
+ fileName,
190
+ mediaType,
191
+ timestamp: new Date().toISOString(),
192
+ result,
193
+ };
188
194
  if (existingIndex >= 0) {
189
195
  // 更新现有记录
190
- mediaDurations[existingIndex].durationMs = durationMs;
196
+ mediaLogs[existingIndex] = logEntry;
191
197
  }
192
198
  else {
193
199
  // 添加新记录
194
- mediaDurations.push({ fileName, durationMs });
200
+ mediaLogs.push(logEntry);
195
201
  }
196
202
  // 写入文件
197
- await (0, promises_1.writeFile)(mediaDurationsPath, JSON.stringify(mediaDurations, null, 2));
198
- console.log(`Updated media_durations.json: ${fileName} -> ${durationMs}ms`);
203
+ await (0, promises_1.writeFile)(mediaLogsPath, JSON.stringify(mediaLogs, null, 2));
204
+ console.log(`Updated media_logs.json: ${fileName} -> ${JSON.stringify(result)} (${mediaType})`);
199
205
  }
200
206
  catch (error) {
201
- console.warn(`Failed to update media_durations.json for ${fileName}:`, error);
207
+ console.warn(`Failed to update media_logs.json for ${fileName}:`, error);
202
208
  }
203
209
  }
204
210
  async function listFiles(dir) {
@@ -216,6 +222,7 @@ const cerevox = new index_1.default({
216
222
  });
217
223
  let session = null;
218
224
  let projectLocalDir = '.';
225
+ let syncSubtitles = false;
219
226
  server.registerTool('zerocut-version', {
220
227
  title: `ZeroCut Version`,
221
228
  description: `ZeroCut: ${constants_1.VERSION}`,
@@ -265,6 +272,8 @@ server.registerTool('retrieve-rules-context', {
265
272
  'music-video',
266
273
  'stage-play',
267
274
  'anime-series',
275
+ 'story-telling',
276
+ 'creative-ad',
268
277
  'custom',
269
278
  ])
270
279
  .default('general-video')
@@ -284,7 +293,9 @@ server.registerTool('retrieve-rules-context', {
284
293
  else if (purpose !== 'general-video' &&
285
294
  purpose !== 'music-video' &&
286
295
  purpose !== 'stage-play' &&
287
- purpose !== 'anime-series') {
296
+ purpose !== 'anime-series' &&
297
+ purpose !== 'story-telling' &&
298
+ purpose !== 'creative-ad') {
288
299
  return createErrorResponse(`Project rules file not found: ${projectRulesFile}`, 'retrieve-rules-context');
289
300
  }
290
301
  try {
@@ -347,6 +358,8 @@ server.registerTool('zerocut-project-open', {
347
358
  }
348
359
  console.log('Initializing project...');
349
360
  const workDir = await initProject(session);
361
+ // Initialize syncSubtitles flag
362
+ syncSubtitles = false;
350
363
  projectLocalDir = (0, node_path_1.resolve)(process.env.ZEROCUT_PROJECT_CWD || process.cwd(), localDir || '.');
351
364
  const syncDir = (0, node_path_1.resolve)(projectLocalDir, 'materials');
352
365
  try {
@@ -551,8 +564,8 @@ server.registerTool('search-image', {
551
564
  }
552
565
  });
553
566
  server.registerTool('generate-character-image', {
554
- title: 'Generate Turnaround Image',
555
- description: 'Generate a turnaround image for any character.',
567
+ title: 'Generate Character Image',
568
+ description: 'Generate a turnaround image or portrait for any character.',
556
569
  inputSchema: {
557
570
  name: zod_1.z.string().describe('The name of the character.'),
558
571
  gender: zod_1.z
@@ -573,14 +586,23 @@ server.registerTool('generate-character-image', {
573
586
  .string()
574
587
  .default('形象参考[图1]的人物形象\n')
575
588
  .describe('形象参考图的提示文本.'),
589
+ isTurnaround: zod_1.z
590
+ .boolean()
591
+ .default(true)
592
+ .describe('是否生成三视图。true: 生成1280x720的三视图,false: 生成720x1280的竖版人物正视图'),
576
593
  saveToFileName: zod_1.z.string().describe('The filename to save.'),
577
594
  },
578
- }, async ({ name, gender, age, appearance, clothing, personality, style, saveToFileName, referenceImage, referenceImagePrompt, }) => {
595
+ }, async ({ name, gender, age, appearance, clothing, personality, style, saveToFileName, referenceImage, referenceImagePrompt, isTurnaround, }) => {
579
596
  try {
580
597
  // 验证session状态
581
598
  const currentSession = await validateSession('generate-character-image');
582
599
  const validatedFileName = validateFileName(saveToFileName);
583
- const prompt = `
600
+ // 根据 isTurnaround 参数生成不同的提示词和尺寸
601
+ let prompt;
602
+ let size;
603
+ if (isTurnaround) {
604
+ // 生成三视图
605
+ prompt = `
584
606
  你是一个专业的角色设计师,请根据设定生成角色全身三视图,图片为白底,图中不带任何文字。设定为:
585
607
 
586
608
  角色名称:${name}
@@ -592,7 +614,26 @@ server.registerTool('generate-character-image', {
592
614
  构图风格:${style}
593
615
  ${referenceImagePrompt}
594
616
  三视图分别是指侧视图、正视图和背视图,从左到右按照这个顺序生成,三者都必须是全身图。
595
- `;
617
+ `;
618
+ size = '1280x720';
619
+ }
620
+ else {
621
+ // 生成竖版人物正视图
622
+ prompt = `
623
+ 你是一个专业的角色设计师,请根据设定生成角色全身正视图,图片为白底,图中不带任何文字。设定为:
624
+
625
+ 角色名称:${name}
626
+ 角色性别:${gender}
627
+ 角色年龄:${age}
628
+ 角色外观:${appearance}
629
+ 角色服装:${clothing}
630
+ 角色性格:${personality}
631
+ 构图风格:${style}
632
+ ${referenceImagePrompt}
633
+ 请生成一张完整的全身正视图,角色面向观众,展现完整的身体比例和服装细节。
634
+ `;
635
+ size = '720x1280';
636
+ }
596
637
  let imageBase64Array;
597
638
  if (referenceImage) {
598
639
  try {
@@ -620,7 +661,6 @@ ${referenceImagePrompt}
620
661
  }
621
662
  }
622
663
  const ai = currentSession.ai;
623
- const size = '1280x720';
624
664
  const res = await ai.generateImage({
625
665
  prompt,
626
666
  size,
@@ -637,6 +677,7 @@ ${referenceImagePrompt}
637
677
  // source: res.url,
638
678
  uri,
639
679
  prompt,
680
+ isTurnaround,
640
681
  size,
641
682
  timestamp: new Date().toISOString(),
642
683
  };
@@ -760,7 +801,7 @@ server.registerTool('generate-image', {
760
801
  .describe('Whether this is a turnaround image.如果是三视图,这个参数务必传true'),
761
802
  }))
762
803
  .optional()
763
- .describe(`Array of reference images with character or object names.如果stage_atmosphere中有角色apply_turnaround_image,那么必须要传这个参数生成角色图片
804
+ .describe(`Array of reference images with character or object names.如果stage_atmosphere中有角色apply_reference_image,那么必须要传这个参数生成角色图片
764
805
 
765
806
  传参示例
766
807
  \`\`\`
@@ -864,7 +905,7 @@ server.registerTool('generate-image', {
864
905
  }
865
906
  }
866
907
  const turnaroundMessage = hasTurnaround
867
- ? '⚠️ 注意**三视图**是指**同一个**人或物体的不同视角合成图,三部分都表示同一个人或物体,只能参考其信息画出**一个人或一个物体**,不要画成多个人或多个物体!\n\n'
908
+ ? '⚠️ 注意**三视图**是指**同一个**人或物体的不同视角合成图,三部分都表示同一个人或物体,只能参考其信息画出**一个人或一个物体**,不要画成多个人或多个物体!\n'
868
909
  : '';
869
910
  if (objectPrefix.length > 0) {
870
911
  processedPrompt = `${objectPrefix.join('\n')}
@@ -924,6 +965,231 @@ ${processedPrompt}`.trim();
924
965
  return createErrorResponse(error, 'generate-image');
925
966
  }
926
967
  });
968
+ server.registerTool('generate-image-series', {
969
+ title: 'Generate Image Series',
970
+ description: 'Generate a series of images based on prompts.',
971
+ inputSchema: {
972
+ prompts: zod_1.z
973
+ .union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())])
974
+ .describe('The prompts to generate images. Can be a single string or array of strings.'),
975
+ size: zod_1.z
976
+ .enum([
977
+ '1024x1024',
978
+ '864x1152',
979
+ '1152x864',
980
+ '1280x720',
981
+ '720x1280',
982
+ '832x1248',
983
+ '1248x832',
984
+ '1512x648',
985
+ ])
986
+ .optional()
987
+ .describe('The size of the images.'),
988
+ watermark: zod_1.z
989
+ .boolean()
990
+ .optional()
991
+ .default(false)
992
+ .describe('Whether to add watermark to the images.'),
993
+ max_count: zod_1.z
994
+ .number()
995
+ .min(1)
996
+ .max(15)
997
+ .optional()
998
+ .default(15)
999
+ .describe('Maximum number of images to generate (1-15).'),
1000
+ referenceImages: zod_1.z
1001
+ .array(zod_1.z.object({
1002
+ image: zod_1.z.string().describe('Local image file path'),
1003
+ type: zod_1.z
1004
+ .enum(['character', 'object', 'background'])
1005
+ .describe('Type of the reference image. 必须传,如果是参考角色三视图,传character,如果是参考背景图,传background,否则传object'),
1006
+ name: zod_1.z.string().describe('Name for this reference image'),
1007
+ isTurnaround: zod_1.z
1008
+ .boolean()
1009
+ .describe('Whether this is a turnaround image.如果是三视图,这个参数务必传true'),
1010
+ }))
1011
+ .optional()
1012
+ .describe(`Array of reference images with character or object names.如果stage_atmosphere中有角色apply_reference_image,那么必须要传这个参数生成角色图片
1013
+
1014
+ 传参示例
1015
+ \`\`\`
1016
+ {
1017
+ "image": "latiao.jpeg",
1018
+ "type": "object",
1019
+ "name": "卫龙辣条",
1020
+ }
1021
+ \`\`\`
1022
+ `),
1023
+ saveToFileNames: zod_1.z
1024
+ .array(zod_1.z.string())
1025
+ .describe('Array of filenames to save the generated images.'),
1026
+ },
1027
+ }, async ({ prompts, size, watermark, max_count, referenceImages, saveToFileNames }, context) => {
1028
+ try {
1029
+ const currentSession = await validateSession('generate-image-series');
1030
+ // Validate file names
1031
+ const validatedFileNames = saveToFileNames.map(fileName => validateFileName(fileName));
1032
+ if (typeof prompts === 'string' && prompts.startsWith('[')) {
1033
+ try {
1034
+ prompts = JSON.parse(prompts);
1035
+ }
1036
+ catch (ex) {
1037
+ // 解析失败,保持原始字符串
1038
+ }
1039
+ }
1040
+ console.log(`Generating image series with ${Array.isArray(prompts) ? prompts.length : 1} prompts...`);
1041
+ // 处理参考图片
1042
+ let imageBase64Array;
1043
+ let refPrefix = '';
1044
+ if (referenceImages && referenceImages.length > 0) {
1045
+ imageBase64Array = [];
1046
+ const objectPrefix = [];
1047
+ let index = 0;
1048
+ let hasTurnaround = false;
1049
+ for (const refImage of referenceImages) {
1050
+ const imagePath = (0, node_path_1.dirname)(refImage.image) !== '.'
1051
+ ? refImage.image
1052
+ : `./materials/${refImage.image}`;
1053
+ // 需要得到当前项目的绝对路径
1054
+ const imageFilePath = (0, node_path_1.resolve)(process.env.ZEROCUT_PROJECT_CWD || process.cwd(), projectLocalDir, imagePath);
1055
+ try {
1056
+ // 直接读取本地文件
1057
+ if (!(0, node_fs_1.existsSync)(imageFilePath)) {
1058
+ return createErrorResponse(`Reference image not found: ${imageFilePath}`, 'generate-image-series');
1059
+ }
1060
+ const imageBuffer = await (0, promises_1.readFile)(imageFilePath);
1061
+ const fileName = (0, node_path_1.basename)(imagePath);
1062
+ const mimeType = fileName.toLowerCase().endsWith('.png')
1063
+ ? 'image/png'
1064
+ : fileName.toLowerCase().endsWith('.jpg') ||
1065
+ fileName.toLowerCase().endsWith('.jpeg')
1066
+ ? 'image/jpeg'
1067
+ : 'image/png';
1068
+ const base64String = `data:${mimeType};base64,${imageBuffer.toString('base64')}`;
1069
+ imageBase64Array.push(base64String);
1070
+ console.log(`Loaded reference image: ${imagePath} for character: ${refImage.name}`);
1071
+ if (refImage.type === 'character') {
1072
+ if (refImage.isTurnaround) {
1073
+ objectPrefix.push(`[参考图${++index}]是名为"${refImage.name}"的人物角色三视图`);
1074
+ hasTurnaround = true;
1075
+ }
1076
+ else {
1077
+ objectPrefix.push(`[参考图${++index}]是名为"${refImage.name}"的人物角色形象`);
1078
+ }
1079
+ }
1080
+ else if (refImage.type === 'object') {
1081
+ if (refImage.isTurnaround) {
1082
+ objectPrefix.push(`[参考图${++index}]是名为"${refImage.name}"的物件三视图`);
1083
+ hasTurnaround = true;
1084
+ }
1085
+ else {
1086
+ objectPrefix.push(`[参考图${++index}]是名为"${refImage.name}"的物件`);
1087
+ }
1088
+ }
1089
+ else if (refImage.type === 'background') {
1090
+ objectPrefix.push(`[参考图${++index}]是背景图,描述了"${refImage.name}"`);
1091
+ }
1092
+ }
1093
+ catch (error) {
1094
+ console.error(`Failed to load reference image ${imageFilePath} for ${refImage.name}:`, error);
1095
+ return createErrorResponse(`Failed to load reference image ${imageFilePath} for ${refImage.name}: ${error}`, 'generate-image-series');
1096
+ }
1097
+ }
1098
+ const turnaroundMessage = hasTurnaround
1099
+ ? '\n\n⚠️ 注意**三视图**是指**同一个**人或物体的不同视角合成图,三部分都表示同一个人或物体,只能参考其信息画出**一个人或一个物体**,不要画成多个人或多个物体!\n'
1100
+ : '';
1101
+ if (objectPrefix.length > 0) {
1102
+ refPrefix = `${objectPrefix.join('\n')}${turnaroundMessage}`;
1103
+ }
1104
+ }
1105
+ const ai = currentSession.ai;
1106
+ let progress = 0;
1107
+ const res = await ai.generateImageSeries({
1108
+ prompts,
1109
+ refPrefix,
1110
+ size,
1111
+ watermark,
1112
+ image: imageBase64Array,
1113
+ max_count,
1114
+ onProgress: async (metaData) => {
1115
+ // 心跳机制,每分钟调用一次,传递空的 metaData
1116
+ try {
1117
+ await sendProgress(context, ++progress, undefined, JSON.stringify(metaData));
1118
+ }
1119
+ catch (progressError) {
1120
+ console.warn('Failed to send progress update:', progressError);
1121
+ }
1122
+ },
1123
+ });
1124
+ if (!res) {
1125
+ throw new Error('Failed to generate image series: no response from AI service');
1126
+ }
1127
+ if (res.urls && Array.isArray(res.urls)) {
1128
+ console.log(`Image series generated successfully (${res.urls.length} images), saving to materials...`);
1129
+ const results = [];
1130
+ const maxImages = Math.min(res.urls.length, validatedFileNames.length);
1131
+ for (let i = 0; i < maxImages; i++) {
1132
+ const url = res.urls[i];
1133
+ const fileName = validatedFileNames[i];
1134
+ try {
1135
+ const uri = await saveMaterial(currentSession, url, fileName);
1136
+ results.push({
1137
+ success: true,
1138
+ uri,
1139
+ fileName,
1140
+ index: i,
1141
+ timestamp: new Date().toISOString(),
1142
+ });
1143
+ }
1144
+ catch (error) {
1145
+ console.error(`Failed to save image ${i + 1}:`, error);
1146
+ results.push({
1147
+ success: false,
1148
+ error: `Failed to save image ${i + 1}: ${error}`,
1149
+ fileName,
1150
+ index: i,
1151
+ timestamp: new Date().toISOString(),
1152
+ });
1153
+ }
1154
+ }
1155
+ const result = {
1156
+ success: true,
1157
+ prompt: res.prompt,
1158
+ totalGenerated: res.urls.length,
1159
+ totalSaved: results.filter(r => r.success).length,
1160
+ results,
1161
+ timestamp: new Date().toISOString(),
1162
+ };
1163
+ return {
1164
+ content: [
1165
+ {
1166
+ type: 'text',
1167
+ text: JSON.stringify(result),
1168
+ },
1169
+ ],
1170
+ };
1171
+ }
1172
+ else {
1173
+ console.warn('Image series generation completed but no URLs returned');
1174
+ return {
1175
+ content: [
1176
+ {
1177
+ type: 'text',
1178
+ text: JSON.stringify({
1179
+ success: false,
1180
+ error: 'No image URLs returned from AI service',
1181
+ response: res,
1182
+ timestamp: new Date().toISOString(),
1183
+ }),
1184
+ },
1185
+ ],
1186
+ };
1187
+ }
1188
+ }
1189
+ catch (error) {
1190
+ return createErrorResponse(error, 'generate-image-series');
1191
+ }
1192
+ });
927
1193
  server.registerTool('edit-image', {
928
1194
  title: 'Edit Image',
929
1195
  description: 'Edit the image.',
@@ -1145,12 +1411,12 @@ server.registerTool('generate-video', {
1145
1411
  timestamp: new Date().toISOString(),
1146
1412
  ...opts,
1147
1413
  };
1148
- // Update media_durations.json
1414
+ // Update media_logs.json
1149
1415
  try {
1150
- await updateMediaDurations(currentSession, validatedFileName, result.durationMs);
1416
+ await updateMediaLogs(currentSession, validatedFileName, result);
1151
1417
  }
1152
1418
  catch (error) {
1153
- console.warn(`Failed to update media_durations.json for ${validatedFileName}:`, error);
1419
+ console.warn(`Failed to update media_logs.json for ${validatedFileName}:`, error);
1154
1420
  }
1155
1421
  return {
1156
1422
  content: [
@@ -1360,13 +1626,12 @@ server.registerTool('generate-sound-effect', {
1360
1626
  });
1361
1627
  if (res.url) {
1362
1628
  const uri = await saveMaterial(currentSession, res.url, saveToFileName);
1363
- // Update media_durations.json
1629
+ // Update media_logs.json
1364
1630
  try {
1365
- const durationMs = res.duration ? Math.floor(res.duration * 1000) : 0;
1366
- await updateMediaDurations(currentSession, saveToFileName, durationMs);
1631
+ await updateMediaLogs(currentSession, saveToFileName, res, 'audio');
1367
1632
  }
1368
1633
  catch (error) {
1369
- console.warn(`Failed to update media_durations.json for ${saveToFileName}:`, error);
1634
+ console.warn(`Failed to update media_logs.json for ${saveToFileName}:`, error);
1370
1635
  }
1371
1636
  return {
1372
1637
  content: [
@@ -1559,12 +1824,12 @@ server.registerTool('generate-song', {
1559
1824
  timestamp: new Date().toISOString(),
1560
1825
  ...opts,
1561
1826
  };
1562
- // Update media_durations.json
1827
+ // Update media_logs.json
1563
1828
  try {
1564
- await updateMediaDurations(currentSession, validatedFileName, result.durationMs);
1829
+ await updateMediaLogs(currentSession, validatedFileName, result, 'audio');
1565
1830
  }
1566
1831
  catch (error) {
1567
- console.warn(`Failed to update media_durations.json for ${validatedFileName}:`, error);
1832
+ console.warn(`Failed to update media_logs.json for ${validatedFileName}:`, error);
1568
1833
  }
1569
1834
  return {
1570
1835
  content: [
@@ -1645,12 +1910,12 @@ server.registerTool('generate-bgm', {
1645
1910
  timestamp: new Date().toISOString(),
1646
1911
  ...opts,
1647
1912
  };
1648
- // Update media_durations.json
1913
+ // Update media_logs.json
1649
1914
  try {
1650
- await updateMediaDurations(currentSession, validatedFileName, result.durationMs);
1915
+ await updateMediaLogs(currentSession, validatedFileName, result, 'audio');
1651
1916
  }
1652
1917
  catch (error) {
1653
- console.warn('Failed to update media_durations.json:', error);
1918
+ console.warn('Failed to update media_logs.json:', error);
1654
1919
  }
1655
1920
  return {
1656
1921
  content: [
@@ -1806,12 +2071,12 @@ server.registerTool('generate-scene-tts', {
1806
2071
  timestamp: new Date().toISOString(),
1807
2072
  ...opts,
1808
2073
  };
1809
- // Update media_durations.json
2074
+ // Update media_logs.json
1810
2075
  try {
1811
- await updateMediaDurations(currentSession, validatedFileName, result.durationMs);
2076
+ await updateMediaLogs(currentSession, validatedFileName, result, 'audio');
1812
2077
  }
1813
2078
  catch (error) {
1814
- console.warn(`Failed to update media_durations.json for ${validatedFileName}:`, error);
2079
+ console.warn(`Failed to update media_logs.json for ${validatedFileName}:`, error);
1815
2080
  }
1816
2081
  return {
1817
2082
  content: [
@@ -2018,6 +2283,10 @@ server.registerTool('get-video-project-schema', {
2018
2283
  inputSchema: {},
2019
2284
  }, async () => {
2020
2285
  try {
2286
+ // Check if syncSubtitles is false and show warning
2287
+ if (!syncSubtitles) {
2288
+ console.warn('Warning: syncSubtitles is false. Please call get-subtitle-contents first to sync subtitles.');
2289
+ }
2021
2290
  const schemaPath = (0, node_path_1.resolve)(__dirname, '../../utils/videoproject-schema.json');
2022
2291
  const schemaContent = await (0, promises_1.readFile)(schemaPath, 'utf-8');
2023
2292
  const schema = JSON.parse(schemaContent);
@@ -2028,6 +2297,9 @@ server.registerTool('get-video-project-schema', {
2028
2297
  text: JSON.stringify({
2029
2298
  success: true,
2030
2299
  schema,
2300
+ warning: !syncSubtitles
2301
+ ? '⚠️ Please call get-subtitle-contents to sync subtitles before creating the draft_content.json file.'
2302
+ : undefined,
2031
2303
  timestamp: new Date().toISOString(),
2032
2304
  }),
2033
2305
  },
@@ -2047,6 +2319,7 @@ server.registerTool('get-subtitle-contents', {
2047
2319
  .describe('The file path of the story_board.json file.'),
2048
2320
  },
2049
2321
  }, async ({ storyBoardFile }) => {
2322
+ syncSubtitles = true;
2050
2323
  try {
2051
2324
  const filePath = (0, node_path_1.resolve)(process.env.ZEROCUT_PROJECT_CWD || process.cwd(), storyBoardFile);
2052
2325
  if (!(0, node_fs_1.existsSync)(filePath)) {
@@ -2223,7 +2496,7 @@ server.registerTool('lip-sync', {
2223
2496
  .string()
2224
2497
  .describe('The filename to save the lip-synced video.'),
2225
2498
  },
2226
- }, async ({ videoFileName, audioFileName, saveToFileName }) => {
2499
+ }, async ({ videoFileName, audioFileName, saveToFileName }, context) => {
2227
2500
  try {
2228
2501
  // 验证session状态
2229
2502
  const currentSession = await validateSession('lip-sync');
@@ -2241,11 +2514,18 @@ server.registerTool('lip-sync', {
2241
2514
  console.log(`Video URL: ${videoUrl}`);
2242
2515
  console.log(`Audio URL: ${audioUrl}`);
2243
2516
  // 调用AI的lipSync方法,使用处理后的音频
2517
+ let progress = 0;
2244
2518
  const result = await currentSession.ai.lipSync({
2245
2519
  videoUrl,
2246
2520
  audioUrl,
2247
- onProgress: metaData => {
2521
+ onProgress: async (metaData) => {
2248
2522
  console.log('Lip sync progress:', metaData);
2523
+ try {
2524
+ await sendProgress(context, ++progress, undefined, JSON.stringify(metaData));
2525
+ }
2526
+ catch (progressError) {
2527
+ console.warn('Failed to send progress update:', progressError);
2528
+ }
2249
2529
  },
2250
2530
  });
2251
2531
  if (result.url) {