cerevox 3.0.0-alpha.9 → 3.0.0-beta.2

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.
@@ -74,7 +74,7 @@ function createErrorResponse(error, operation, details) {
74
74
  };
75
75
  }
76
76
  // Session 状态检查
77
- async function validateSession(operation) {
77
+ async function validateSession(operation, checkRules = true) {
78
78
  if (closeSessionTimerId) {
79
79
  clearTimeout(closeSessionTimerId);
80
80
  closeSessionTimerId = null;
@@ -84,7 +84,7 @@ async function validateSession(operation) {
84
84
  throw new Error(`Session not initialized. Please call 'project-open' first before using ${operation}.`);
85
85
  }
86
86
  const projectRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `project_rules.md`);
87
- if (!(0, node_fs_1.existsSync)(projectRulesFile)) {
87
+ if (checkRules && !(0, node_fs_1.existsSync)(projectRulesFile)) {
88
88
  throw new Error(`Project rules file not found: ${projectRulesFile}. Please call 'retrieve-rules-context' first.`);
89
89
  }
90
90
  return session;
@@ -353,131 +353,6 @@ server.registerPrompt('zerocut-guideline', {
353
353
  throw new Error(`Failed to load zerocut-guideline prompt: ${error}`);
354
354
  }
355
355
  });
356
- server.registerTool('retrieve-rules-context', {
357
- title: 'ZeroCut Basic Rules',
358
- description: `ZeroCut-${constants_1.VERSION} Basic Rules`,
359
- inputSchema: {
360
- purpose: zod_1.z
361
- .enum([
362
- 'general-video',
363
- 'music-video',
364
- 'stage-play',
365
- 'story-telling',
366
- 'creative-ad',
367
- 'expert',
368
- 'material-creation',
369
- 'freeform',
370
- 'custom',
371
- ])
372
- .default('general-video')
373
- .describe(`The purpose of the rules context to retrieve.
374
-
375
- - general-video 创建通用视频
376
- - music-video 创建音乐视频
377
- - stage-play 创建舞台播放视频
378
- - story-telling 创建故事讲述视频
379
- - creative-ad 创建创意广告视频
380
- - expert 以专家模式创建视频,必须用户主动要求才触发
381
- - material-creation 素材创作模式,必须用户主动要求才触发
382
- - freeform 自由创作模式,必须用户主动要求才触发
383
- - custom 自定义模式`),
384
- },
385
- }, async ({ purpose }) => {
386
- const projectRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `project_rules.md`);
387
- const customRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `custom_rules.md`);
388
- let promptContent = '';
389
- if ((0, node_fs_1.existsSync)(projectRulesFile)) {
390
- promptContent = await (0, promises_1.readFile)(projectRulesFile, 'utf-8');
391
- if ((0, node_fs_1.existsSync)(customRulesFile)) {
392
- promptContent +=
393
- '\n\n---\n\n' + (await (0, promises_1.readFile)(customRulesFile, 'utf-8'));
394
- }
395
- }
396
- else {
397
- // 当 projectRulesFile 不存在时,设置 checkStoryboardFlag 为 false
398
- checkStoryboardFlag = false;
399
- // 当 projectRulesFile 不存在时,设置 checkAudioVideoDurationFlag 为 false
400
- checkAudioVideoDurationFlag = false;
401
- // 当 projectRulesFile 不存在时,设置 checkStoryboardSubtitlesFlag 为 false
402
- checkStoryboardSubtitlesFlag = false;
403
- if (purpose !== 'general-video' &&
404
- purpose !== 'music-video' &&
405
- purpose !== 'stage-play' &&
406
- purpose !== 'story-telling' &&
407
- purpose !== 'creative-ad' &&
408
- purpose !== 'expert' &&
409
- purpose !== 'material-creation' &&
410
- purpose !== 'freeform') {
411
- return createErrorResponse(`Project rules file not found: ${projectRulesFile}`, 'retrieve-rules-context');
412
- }
413
- }
414
- try {
415
- if (!promptContent) {
416
- const promptPath = (0, node_path_1.resolve)(__dirname, `./prompts/rules/${purpose}.md`);
417
- promptContent = await (0, promises_1.readFile)(promptPath, 'utf-8');
418
- // 确保目录存在
419
- await (0, promises_1.mkdir)((0, node_path_1.dirname)(projectRulesFile), { recursive: true });
420
- await (0, promises_1.writeFile)(projectRulesFile, promptContent);
421
- }
422
- // 如果 purpose 为 freeform,复制 skills 和 knowledge 目录
423
- if (purpose === 'freeform') {
424
- // 递归复制目录的通用函数
425
- const copyDirectory = async (src, dest) => {
426
- const entries = await (0, promises_1.readdir)(src, { withFileTypes: true });
427
- for (const entry of entries) {
428
- const srcPath = (0, node_path_1.resolve)(src, entry.name);
429
- const destPath = (0, node_path_1.resolve)(dest, entry.name);
430
- if (entry.isDirectory()) {
431
- await (0, promises_1.mkdir)(destPath, { recursive: true });
432
- await copyDirectory(srcPath, destPath);
433
- }
434
- else {
435
- const content = await (0, promises_1.readFile)(srcPath, 'utf-8');
436
- await (0, promises_1.writeFile)(destPath, content);
437
- }
438
- }
439
- };
440
- // 复制 skills 目录
441
- const sourceSkillsDir = (0, node_path_1.resolve)(__dirname, './prompts/skills');
442
- const targetSkillsDir = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'skills');
443
- try {
444
- await (0, promises_1.mkdir)(targetSkillsDir, { recursive: true });
445
- await copyDirectory(sourceSkillsDir, targetSkillsDir);
446
- console.log(`Skills directory copied to ${targetSkillsDir}`);
447
- }
448
- catch (skillsError) {
449
- console.warn(`Failed to copy skills directory: ${skillsError}`);
450
- }
451
- // 复制 knowledge 目录
452
- const sourceKnowledgeDir = (0, node_path_1.resolve)(__dirname, './prompts/knowledge');
453
- const targetKnowledgeDir = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'knowledge');
454
- try {
455
- await (0, promises_1.mkdir)(targetKnowledgeDir, { recursive: true });
456
- await copyDirectory(sourceKnowledgeDir, targetKnowledgeDir);
457
- console.log(`Knowledge directory copied to ${targetKnowledgeDir}`);
458
- }
459
- catch (knowledgeError) {
460
- console.warn(`Failed to copy knowledge directory: ${knowledgeError}`);
461
- }
462
- }
463
- return {
464
- content: [
465
- {
466
- type: 'text',
467
- text: JSON.stringify({
468
- type: 'project_rules',
469
- content: promptContent,
470
- projectRulesFile,
471
- }),
472
- },
473
- ],
474
- };
475
- }
476
- catch (error) {
477
- console.error(`Failed to load rules context prompt for ${purpose}:`, error);
478
- return createErrorResponse(`Failed to load rules context prompt for ${purpose}: ${error}`, 'retrieve-rules-context');
479
- }
480
- });
481
356
  server.registerTool('project-open', {
482
357
  title: 'Open Project',
483
358
  description: 'Launch a new Cerevox session with a Chromium browser instance and open a new project context. Supports smart file filtering to optimize upload performance.',
@@ -673,6 +548,121 @@ server.registerTool('project-close', {
673
548
  return createErrorResponse(error, 'project-close');
674
549
  }
675
550
  });
551
+ server.registerTool('retrieve-rules-context', {
552
+ title: 'ZeroCut Basic Rules',
553
+ description: `ZeroCut-${constants_1.VERSION} Basic Rules`,
554
+ inputSchema: {
555
+ request: zod_1.z.string().describe(`根据用户的具体的指令召回规则上下文,具体指令为通常为如下格式之一:
556
+
557
+ - 启用xxx模式
558
+ - 使用xxx助手
559
+ - 启用xxx角色
560
+ - 切换上下文为xxx
561
+ - 召回原规则上下文(配合type:memo使用)
562
+ `),
563
+ type: zod_1.z
564
+ .enum(['new', 'memo', 'switch'])
565
+ .describe('new: 新创建规则上下文\nmemo: 根据需要重新召回规则上下文(记忆)\nswitch: 根据用户指令,切换规则上下文'),
566
+ },
567
+ }, async ({ request, type }) => {
568
+ const currentSession = await validateSession('retrieve-rules-context', false);
569
+ const projectRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `project_rules.md`);
570
+ const customRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `custom_rules.md`);
571
+ let promptContent = '';
572
+ if ((0, node_fs_1.existsSync)(projectRulesFile)) {
573
+ promptContent = await (0, promises_1.readFile)(projectRulesFile, 'utf-8');
574
+ if ((0, node_fs_1.existsSync)(customRulesFile)) {
575
+ promptContent +=
576
+ '\n\n---\n\n' + (await (0, promises_1.readFile)(customRulesFile, 'utf-8'));
577
+ }
578
+ }
579
+ else {
580
+ // 当 projectRulesFile 不存在时,设置 checkStoryboardFlag 为 false
581
+ checkStoryboardFlag = false;
582
+ // 当 projectRulesFile 不存在时,设置 checkAudioVideoDurationFlag 为 false
583
+ checkAudioVideoDurationFlag = false;
584
+ // 当 projectRulesFile 不存在时,设置 checkStoryboardSubtitlesFlag 为 false
585
+ checkStoryboardSubtitlesFlag = false;
586
+ }
587
+ if (type === 'switch') {
588
+ (0, node_fs_1.rmSync)(projectRulesFile, { force: true });
589
+ (0, node_fs_1.rmSync)(customRulesFile, { force: true });
590
+ promptContent = '';
591
+ }
592
+ try {
593
+ if (!promptContent) {
594
+ const ai = currentSession.ai;
595
+ const { data: rules } = await ai.listContextRules();
596
+ const rulesList = rules.map((rule) => ({
597
+ name: rule.name,
598
+ trigger: rule.trigger,
599
+ }));
600
+ const chooseRulePrompt = `
601
+ 请分析用户具体指令,匹配可用规则中的trigger,选择最合适的规则。
602
+
603
+ ## 可用规则:
604
+ ${JSON.stringify(rulesList, null, 2)}
605
+
606
+ 请返回一个 JSON 字符串,包含用户意图对应的规则名称。格式为:{"rule_name": "规则名称"}
607
+ `;
608
+ const schema = {
609
+ name: 'choose_rule',
610
+ schema: {
611
+ type: 'object',
612
+ properties: {
613
+ rule_name: {
614
+ type: 'string',
615
+ description: '用户意图对应的规则名称',
616
+ },
617
+ },
618
+ required: ['rule_name'],
619
+ },
620
+ };
621
+ const completion = await ai.getCompletions({
622
+ model: 'Doubao-Seed-1.6-flash',
623
+ messages: [
624
+ {
625
+ role: 'system',
626
+ content: chooseRulePrompt,
627
+ },
628
+ {
629
+ role: 'user',
630
+ content: request,
631
+ },
632
+ ],
633
+ response_format: {
634
+ type: 'json_schema',
635
+ json_schema: schema,
636
+ },
637
+ });
638
+ const ruleName = JSON.parse(completion.choices[0].message.content).rule_name;
639
+ let rule = rules.find((rule) => rule.name === ruleName);
640
+ if (!rule) {
641
+ rule = rules.find((rule) => rule.name === '通用视频助手');
642
+ }
643
+ promptContent = rule.prompt;
644
+ // 确保目录存在
645
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(projectRulesFile), { recursive: true });
646
+ await (0, promises_1.writeFile)(projectRulesFile, promptContent);
647
+ }
648
+ return {
649
+ content: [
650
+ {
651
+ type: 'text',
652
+ text: JSON.stringify({
653
+ type: 'project_rules',
654
+ content: promptContent,
655
+ projectRulesFile,
656
+ }),
657
+ },
658
+ ],
659
+ };
660
+ }
661
+ catch (error) {
662
+ console.error(`Failed to load rules context prompt for ${prompt}:`, error);
663
+ return createErrorResponse(`Failed to load rules context prompt for ${prompt}: ${error}`, 'retrieve-rules-context');
664
+ }
665
+ });
676
666
  // 列出项目下的所有文件
677
667
  server.registerTool('list-project-files', {
678
668
  title: 'List Project Files',
@@ -2810,34 +2800,6 @@ server.registerTool('get-schema', {
2810
2800
  return createErrorResponse(error, 'get-schema');
2811
2801
  }
2812
2802
  });
2813
- server.registerTool('do-storyboard-optimization', {
2814
- title: 'Do Storyboard Optimization',
2815
- description: 'Get storyboard optimization guidelines and action instructions.',
2816
- inputSchema: {},
2817
- }, async () => {
2818
- try {
2819
- // 调用 do-storyboard-optimization 工具时,设置 checkStoryboardFlag 为 true
2820
- checkStoryboardFlag = true;
2821
- const guidelinePath = (0, node_path_1.resolve)(__dirname, './prompts/actions/storyboard_optimization.md');
2822
- const storyboardOptimizationGuidelines = await (0, promises_1.readFile)(guidelinePath, 'utf-8');
2823
- return {
2824
- content: [
2825
- {
2826
- type: 'text',
2827
- text: JSON.stringify({
2828
- content: {
2829
- guideline: storyboardOptimizationGuidelines,
2830
- action: '你应当根据guideline优化storyboard.json',
2831
- },
2832
- }),
2833
- },
2834
- ],
2835
- };
2836
- }
2837
- catch (error) {
2838
- return createErrorResponse(error, 'do-storyboard-optimization');
2839
- }
2840
- });
2841
2803
  server.registerTool('search-voices', {
2842
2804
  title: 'Search Voices',
2843
2805
  description: 'Search voices from doubao_voices_full based on scenes, emotions, languages, and gender.',
@@ -3416,7 +3378,11 @@ server.registerTool('audio-video-sync', {
3416
3378
  description: 'Generate audio-video-synced video by matching video with audio. 还可以对口型。',
3417
3379
  inputSchema: {
3418
3380
  lipSync: zod_1.z.boolean().default(false),
3419
- lipSyncType: zod_1.z.enum(['pixv', 'vidu', 'basic', 'lite']).default('pixv'),
3381
+ lipSyncType: zod_1.z
3382
+ .enum(['pixv', 'vidu', 'basic', 'lite'])
3383
+ .optional()
3384
+ .default('pixv'),
3385
+ lipSyncPadAudio: zod_1.z.boolean().optional().default(true),
3420
3386
  videoFileName: zod_1.z
3421
3387
  .string()
3422
3388
  .describe('The video file name in materials directory.'),
@@ -3435,7 +3401,7 @@ server.registerTool('audio-video-sync', {
3435
3401
  .string()
3436
3402
  .describe('The filename to save the audio-video-synced video.'),
3437
3403
  },
3438
- }, async ({ lipSync, lipSyncType, videoFileName, audioFileName, audioInMs, refPhotoFileName, saveToFileName, }, context) => {
3404
+ }, async ({ lipSync, lipSyncType, lipSyncPadAudio, videoFileName, audioFileName, audioInMs, refPhotoFileName, saveToFileName, }, context) => {
3439
3405
  try {
3440
3406
  // 验证session状态
3441
3407
  const currentSession = await validateSession('audio-video-sync');
@@ -3507,6 +3473,7 @@ server.registerTool('audio-video-sync', {
3507
3473
  audioUrl,
3508
3474
  audioInMs,
3509
3475
  ref_photo_url: refPhotoUrl,
3476
+ pad_audio: lipSyncPadAudio,
3510
3477
  onProgress: async (metaData) => {
3511
3478
  console.log('Lip sync progress:', metaData);
3512
3479
  try {
@@ -3561,7 +3528,7 @@ server.registerTool('audio-video-sync', {
3561
3528
  });
3562
3529
  server.registerTool('generate-video-by-ref', {
3563
3530
  title: 'Generate Video by Reference Images',
3564
- description: 'Generate video using reference images. Supports sora2, veo3.1, veo3.1-pro (1 image max), lite and pro (4 images max), vidu (7 images max). Can work without reference images (0 images).',
3531
+ description: 'Generate video using reference images. Supports sora2, sora2-pro (1 image max), veo3.1, veo3.1-pro (1 image max), lite and pro (4 images max), vidu (7 images max). Can work without reference images (0 images).',
3565
3532
  inputSchema: {
3566
3533
  prompt: zod_1.z
3567
3534
  .string()
@@ -3591,21 +3558,34 @@ server.registerTool('generate-video-by-ref', {
3591
3558
  .optional()
3592
3559
  .default(5)
3593
3560
  .describe('The duration of the video in seconds.'),
3594
- size: zod_1.z.enum(['720x1280', '1280x720']).describe('The size of the video.'),
3561
+ aspectRatio: zod_1.z
3562
+ .enum(['16:9', '9:16'])
3563
+ .describe('The aspect ratio of the video.'),
3564
+ resolution: zod_1.z
3565
+ .enum(['720p', '1080p'])
3566
+ .describe('The resolution of the video.'),
3595
3567
  watermark: zod_1.z
3596
3568
  .boolean()
3597
3569
  .optional()
3598
3570
  .default(false)
3599
3571
  .describe('Whether to add watermark to the video.'),
3600
3572
  type: zod_1.z
3601
- .enum(['lite', 'sora2', 'veo3.1', 'veo3.1-pro', 'vidu', 'pixv'])
3573
+ .enum([
3574
+ 'lite',
3575
+ 'sora2',
3576
+ 'sora2-pro',
3577
+ 'veo3.1',
3578
+ 'veo3.1-pro',
3579
+ 'vidu',
3580
+ 'pixv',
3581
+ ])
3602
3582
  .default('lite')
3603
- .describe('The model type to use. sora2 allows max 1 reference image, lite allow max 4, vidu allow max 7.'),
3583
+ .describe('The model type to use. sora2 and veo3.1 allows max 1 reference image, lite allow max 4, vidu allow max 7.'),
3604
3584
  mute: zod_1.z
3605
3585
  .boolean()
3606
3586
  .optional()
3607
3587
  .default(false)
3608
- .describe('Whether to mute the video (effective for sora2).'),
3588
+ .describe('Whether to mute the video (effective for sora2 and veo3.1).'),
3609
3589
  saveToFileName: zod_1.z
3610
3590
  .string()
3611
3591
  .describe('The filename to save the generated video.'),
@@ -3634,7 +3614,7 @@ server.registerTool('generate-video-by-ref', {
3634
3614
  .default(false)
3635
3615
  .describe('Whether to optimize the prompt.'),
3636
3616
  },
3637
- }, async ({ prompt, rewritePrompt, referenceImages, duration, size, watermark, type, mute, saveToFileName, sceneIndex, storyBoardFile, skipConsistencyCheck, optimizePrompt, }, context) => {
3617
+ }, async ({ prompt, rewritePrompt, referenceImages, duration, aspectRatio, resolution, watermark, type, mute, saveToFileName, sceneIndex, storyBoardFile, skipConsistencyCheck, optimizePrompt, }, context) => {
3638
3618
  try {
3639
3619
  // 验证session状态
3640
3620
  const currentSession = await validateSession('generate-video-by-ref');
@@ -3676,6 +3656,9 @@ server.registerTool('generate-video-by-ref', {
3676
3656
  if (scene.is_continuous === true) {
3677
3657
  return createErrorResponse('连续镜头应使用首尾帧,请修改连续镜头设置,或将本场景改为首尾帧方式实现', 'generate-video-by-ref');
3678
3658
  }
3659
+ if (scene.video_type !== 'references') {
3660
+ return createErrorResponse(`场景 ${sceneIndex} 中的 video_type (${scene.video_type}) 未设置为 'references',不应当使用参考生视频,请使用图生视频 generate-video 方式生成`, 'generate-video-by-ref');
3661
+ }
3679
3662
  // 检查 use_video_model 与 type 参数的一致性
3680
3663
  if (scene.use_video_model &&
3681
3664
  type &&
@@ -3724,15 +3707,15 @@ server.registerTool('generate-video-by-ref', {
3724
3707
  }
3725
3708
  // 检查 storyBoard.orientation 与 size 参数的一致性
3726
3709
  if (storyBoard.orientation) {
3727
- const isLandscapeSize = size === '1280x720';
3728
- const isPortraitSize = size === '720x1280';
3710
+ const isLandscapeSize = aspectRatio === '16:9';
3711
+ const isPortraitSize = aspectRatio === '9:16';
3729
3712
  if (storyBoard.orientation === 'landscape' &&
3730
3713
  !isLandscapeSize) {
3731
- return createErrorResponse(`故事板设定为横屏模式(orientation: landscape),但视频尺寸 ${size} 为竖屏格式,请使用横屏尺寸 1280x720`, 'generate-video-by-ref');
3714
+ return createErrorResponse(`故事板设定为横屏模式(orientation: landscape),但视频为竖屏格式,请使用横屏尺寸 1280x720`, 'generate-video-by-ref');
3732
3715
  }
3733
3716
  if (storyBoard.orientation === 'portrait' &&
3734
3717
  !isPortraitSize) {
3735
- return createErrorResponse(`故事板设定为竖屏模式(orientation: portrait),但视频尺寸 ${size} 为横屏格式,请使用竖屏尺寸 720x1280`, 'generate-video-by-ref');
3718
+ return createErrorResponse(`故事板设定为竖屏模式(orientation: portrait),但视频为横屏格式,请使用竖屏尺寸 720x1280`, 'generate-video-by-ref');
3736
3719
  }
3737
3720
  }
3738
3721
  }
@@ -3778,7 +3761,10 @@ server.registerTool('generate-video-by-ref', {
3778
3761
  }
3779
3762
  }
3780
3763
  // 验证参考图数量限制
3781
- if ((type === 'sora2' || type === 'veo3.1' || type === 'veo3.1-pro') &&
3764
+ if ((type === 'sora2' ||
3765
+ type === 'sora2-pro' ||
3766
+ type === 'veo3.1' ||
3767
+ type === 'veo3.1-pro') &&
3782
3768
  referenceImages.length > 1) {
3783
3769
  return createErrorResponse(`${type} model only supports maximum 1 reference image`, 'generate-video-by-ref');
3784
3770
  }
@@ -3808,12 +3794,14 @@ server.registerTool('generate-video-by-ref', {
3808
3794
  if (promptPrefix) {
3809
3795
  promptPrefix += '\n';
3810
3796
  }
3797
+ const finalPrompt = `${promptPrefix}${prompt}`;
3811
3798
  // 调用 referencesToVideo 函数
3812
3799
  const result = await currentSession.ai.referencesToVideo({
3813
- prompt: `${promptPrefix}${prompt}`,
3800
+ prompt: finalPrompt,
3814
3801
  reference_images: referenceImageUrls, // 使用URL数组而不是base64数组
3815
3802
  duration,
3816
- size,
3803
+ aspect_ratio: aspectRatio,
3804
+ resolution,
3817
3805
  watermark,
3818
3806
  type,
3819
3807
  mute,
@@ -3825,9 +3813,23 @@ server.registerTool('generate-video-by-ref', {
3825
3813
  if (result.error) {
3826
3814
  return createErrorResponse(result.error, 'generate-video-by-ref');
3827
3815
  }
3828
- if (!result.url) {
3816
+ if (!result.url && !result.taskUrl) {
3829
3817
  return createErrorResponse('Video generation failed: no video URL returned', 'generate-video-by-ref');
3830
3818
  }
3819
+ else if (result.taskUrl) {
3820
+ return {
3821
+ content: [
3822
+ {
3823
+ type: 'text',
3824
+ text: JSON.stringify({
3825
+ success: true,
3826
+ message: '该视频生成任务正在运行中,它是异步任务,且执行时间较长,你应立即调用工具 wait-for-task-finish 来等待任务结束,如该工具调用超时,你应立即再次重新调用直到任务结束。',
3827
+ taskUrl: result.taskUrl,
3828
+ }),
3829
+ },
3830
+ ],
3831
+ };
3832
+ }
3831
3833
  // 下载生成的视频
3832
3834
  await saveMaterial(currentSession, result.url, validatedFileName);
3833
3835
  // 更新媒体日志
@@ -3854,7 +3856,7 @@ server.registerTool('generate-video-by-ref', {
3854
3856
  url: result.url,
3855
3857
  last_frame_url: result.last_frame_url,
3856
3858
  referenceImageUrls,
3857
- prompt,
3859
+ prompt: finalPrompt,
3858
3860
  }, null, 2),
3859
3861
  },
3860
3862
  ],
@@ -3878,6 +3880,10 @@ server.registerTool('extend-video-duration', {
3878
3880
  .max(7)
3879
3881
  .default(3)
3880
3882
  .describe('Duration to extend the video in seconds (1-7).'),
3883
+ resolution: zod_1.z
3884
+ .enum(['720p', '1080p'])
3885
+ .default('720p')
3886
+ .describe('The resolution of the video.'),
3881
3887
  prompt: zod_1.z
3882
3888
  .string()
3883
3889
  .optional()
@@ -3894,7 +3900,7 @@ server.registerTool('extend-video-duration', {
3894
3900
  .string()
3895
3901
  .describe('The filename to save the extended video.'),
3896
3902
  },
3897
- }, async ({ videoFileName, duration, prompt, type = 'turbo', endFrame, saveToFileName, }, context) => {
3903
+ }, async ({ videoFileName, duration, resolution, prompt, type = 'turbo', endFrame, saveToFileName, }, context) => {
3898
3904
  try {
3899
3905
  await validateSession('extend-video');
3900
3906
  validateFileName(videoFileName);
@@ -3923,6 +3929,7 @@ server.registerTool('extend-video-duration', {
3923
3929
  video_url: videoUri,
3924
3930
  prompt: finalPrompt,
3925
3931
  duration,
3932
+ resolution,
3926
3933
  end_frame: endFrameUri,
3927
3934
  onProgress: async (metaData) => {
3928
3935
  sendProgress(context, ++progress, undefined, `Extension progress: ${Math.round(progress * 100)}%`);
@@ -3976,54 +3983,31 @@ server.registerTool('extend-video-duration', {
3976
3983
  return createErrorResponse(error, 'extend-video');
3977
3984
  }
3978
3985
  });
3979
- server.registerTool('generate-video-by-template', {
3980
- title: 'Generate Video by Template',
3981
- description: 'Generate a video based on a template. The template must be a valid JSON string.',
3986
+ server.registerTool('use-template', {
3987
+ title: 'Use Template',
3988
+ description: 'Find a template that matches the user request, and use it to generate a new material.',
3982
3989
  inputSchema: {
3983
3990
  user_request: zod_1.z.string().describe('用户请求,根据意图自动匹配模板'),
3984
- text_prompts: zod_1.z
3985
- .array(zod_1.z.string().describe('Text prompt for the template to build video.'))
3986
- .optional()
3987
- .describe('Optional text prompts to use in the template.'),
3988
3991
  materials: zod_1.z
3989
3992
  .array(zod_1.z.string().describe('Material file name in materials directory.'))
3990
3993
  .optional()
3991
3994
  .describe('Optional materials to use in the template.'),
3992
3995
  saveToFileName: zod_1.z
3993
3996
  .string()
3994
- .describe('The filename to save the generated video.'),
3997
+ .describe('The filename to save the generated material.'),
3995
3998
  },
3996
- }, async ({ user_request, text_prompts, saveToFileName, materials }) => {
3999
+ }, async ({ user_request, saveToFileName, materials }) => {
3997
4000
  try {
3998
- const templates = [
3999
- {
4000
- id: '7569583728302817331',
4001
- name: '宠物唱歌',
4002
- description: '用一张宠物照片,生成一段宠物唱歌的视频。',
4003
- trigger: '根据{A图片}生成一段{宠物A}唱歌的视频',
4004
- },
4005
- {
4006
- id: '7569605825011367976',
4007
- name: '万圣节宠物弹吉他',
4008
- description: '用一张宠物照片,生成一段宠物弹吉他的视频。',
4009
- trigger: '根据{A图片}生成一段{宠物A}弹吉他的视频',
4010
- },
4011
- {
4012
- id: '7572443489834844223',
4013
- name: '图生动作模仿视频',
4014
- description: '用一张图片和动作视频,生成一段图片主体模仿该动作视频的新视频。',
4015
- trigger: '生成{A图片}模仿{B视频}的视频',
4016
- },
4017
- {
4018
- id: '7575160546555674670',
4019
- name: '文生动作模仿视频',
4020
- description: '用一段提示词和视频,生成一段模仿该视频的新视频。',
4021
- trigger: '生成一段{提示词A}模仿{B视频}的视频',
4022
- },
4023
- ];
4024
4001
  const currentSession = await validateSession('generate-video-by-template');
4025
- const validatedFileName = validateFileName(saveToFileName);
4026
4002
  const ai = currentSession.ai;
4003
+ const data = await ai.listTemplates('all');
4004
+ const templates = data.map(item => ({
4005
+ id: item.workflow_id,
4006
+ name: item.details.name,
4007
+ description: item.details.description,
4008
+ trigger: item.details.trigger,
4009
+ }));
4010
+ const validatedFileName = validateFileName(saveToFileName);
4027
4011
  let completion = await ai.getCompletions({
4028
4012
  model: 'Doubao-Seed-1.6',
4029
4013
  messages: [
@@ -4056,13 +4040,13 @@ server.registerTool('generate-video-by-template', {
4056
4040
  required: ['parameters'],
4057
4041
  },
4058
4042
  };
4059
- const prompt = `你根据模板工作流输入 schemaprompt、materials 生成一个 JSON 字符串,作为模板工作流的参数。
4043
+ const prompt = `你根据模板工作流描述中的 input_schema、用户需求 prompt 和 materials 生成一个 JSON 对象,作为模板工作流的调用参数。
4060
4044
 
4061
- ## **工作流输入 schema**:
4062
- ${JSON.stringify(workflowInfo.data.workflow_detail.description)}
4045
+ ## **工作流描述**:
4046
+ ${workflowInfo.data.workflow_detail.description}
4063
4047
 
4064
- ## **prompt**:
4065
- ${text_prompts}
4048
+ ## **用户需求 prompt**:
4049
+ ${user_request}
4066
4050
 
4067
4051
  ## **materials**:
4068
4052
  ${JSON.stringify(materialUrls)}`;
@@ -4088,20 +4072,6 @@ ${JSON.stringify(materialUrls)}`;
4088
4072
  if (!parameters) {
4089
4073
  throw new Error('Failed to get parameters');
4090
4074
  }
4091
- // console.log(parameters);
4092
- // return {
4093
- // content: [
4094
- // {
4095
- // type: 'text',
4096
- // text: JSON.stringify({
4097
- // success: true,
4098
- // prompt,
4099
- // templateId,
4100
- // parameters,
4101
- // }),
4102
- // },
4103
- // ],
4104
- // };
4105
4075
  const result = await ai.runCozeWorkflow(templateId, JSON.parse(parameters).parameters);
4106
4076
  if (result.url) {
4107
4077
  // 保存到项目材料目录
@@ -4133,7 +4103,7 @@ ${JSON.stringify(materialUrls)}`;
4133
4103
  };
4134
4104
  }
4135
4105
  catch (error) {
4136
- return createErrorResponse(error, 'generate-video-by-template');
4106
+ return createErrorResponse(error, 'use-template');
4137
4107
  }
4138
4108
  });
4139
4109
  server.registerTool('run-ffmpeg-command', {
@@ -4230,6 +4200,34 @@ server.registerTool('run-ffmpeg-command', {
4230
4200
  return createErrorResponse(error, 'run-ffmpeg-command');
4231
4201
  }
4232
4202
  });
4203
+ server.registerTool('do-storyboard-optimization', {
4204
+ title: 'Do Storyboard Optimization',
4205
+ description: 'Get storyboard optimization guidelines and action instructions.',
4206
+ inputSchema: {},
4207
+ }, async () => {
4208
+ try {
4209
+ // 调用 do-storyboard-optimization 工具时,设置 checkStoryboardFlag 为 true
4210
+ checkStoryboardFlag = true;
4211
+ const guidelinePath = (0, node_path_1.resolve)(__dirname, './prompts/actions/storyboard_optimization.md');
4212
+ const storyboardOptimizationGuidelines = await (0, promises_1.readFile)(guidelinePath, 'utf-8');
4213
+ return {
4214
+ content: [
4215
+ {
4216
+ type: 'text',
4217
+ text: JSON.stringify({
4218
+ content: {
4219
+ guideline: storyboardOptimizationGuidelines,
4220
+ action: '你应当根据guideline优化storyboard.json',
4221
+ },
4222
+ }),
4223
+ },
4224
+ ],
4225
+ };
4226
+ }
4227
+ catch (error) {
4228
+ return createErrorResponse(error, 'do-storyboard-optimization');
4229
+ }
4230
+ });
4233
4231
  server.registerTool('search-context', {
4234
4232
  title: 'Search Context',
4235
4233
  description: 'Search the context.',