cerevox 3.0.0-alpha.1 → 3.0.0-alpha.10

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.
@@ -53,86 +53,9 @@ const node_path_1 = __importStar(require("node:path"));
53
53
  const doubao_voices_full_1 = require("./helper/doubao_voices_full");
54
54
  const node_fs_1 = require("node:fs");
55
55
  const coze_1 = require("../../utils/coze");
56
- const node_child_process_1 = require("node:child_process");
57
- const node_util_1 = require("node:util");
58
- const os = __importStar(require("node:os"));
59
56
  const mp3_duration_1 = __importDefault(require("mp3-duration"));
60
57
  const image_size_1 = __importDefault(require("image-size"));
61
- const execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
62
- // 错误处理工具函数
63
- const handleError = (error) => {
64
- if (error instanceof Error) {
65
- return error.message;
66
- }
67
- return String(error);
68
- };
69
- // 检查端口是否被占用 - 跨平台版本
70
- const isPortInUse = async (port) => {
71
- try {
72
- const platform = os.platform();
73
- let command;
74
- if (platform === 'win32') {
75
- // Windows 使用 netstat
76
- command = `netstat -ano | findstr :${port}`;
77
- }
78
- else {
79
- // Unix/Linux/macOS 使用 lsof
80
- command = `lsof -ti:${port}`;
81
- }
82
- const { stdout } = await execAsync(command);
83
- return stdout.trim().length > 0;
84
- }
85
- catch {
86
- return false;
87
- }
88
- };
89
- // 停止占用端口的进程 - 跨平台版本
90
- const killPortProcess = async (port) => {
91
- try {
92
- const platform = os.platform();
93
- if (platform === 'win32') {
94
- // Windows 版本
95
- const { stdout } = await execAsync(`netstat -ano | findstr :${port}`);
96
- const lines = stdout.trim().split('\n');
97
- for (const line of lines) {
98
- const parts = line.trim().split(/\s+/);
99
- const pid = parts[parts.length - 1];
100
- if (pid && /^\d+$/.test(pid)) {
101
- try {
102
- await execAsync(`taskkill /F /PID ${pid}`);
103
- console.log(`Killed process ${pid} on port ${port}`);
104
- }
105
- catch (error) {
106
- console.warn(`Failed to kill process ${pid}:`, error);
107
- }
108
- }
109
- }
110
- }
111
- else {
112
- // Unix/Linux/macOS 版本
113
- const { stdout } = await execAsync(`lsof -ti:${port}`);
114
- const pids = stdout
115
- .trim()
116
- .split('\n')
117
- .filter(pid => pid);
118
- for (const pid of pids) {
119
- try {
120
- await execAsync(`kill -9 ${pid}`);
121
- console.log(`Killed process ${pid} on port ${port}`);
122
- }
123
- catch (error) {
124
- console.warn(`Failed to kill process ${pid}:`, error);
125
- }
126
- }
127
- }
128
- // 等待一下确保进程被杀死
129
- await new Promise(resolve => setTimeout(resolve, 1000));
130
- }
131
- catch (error) {
132
- console.warn(`Failed to kill processes on port ${port}:`, error);
133
- }
134
- };
135
- function createErrorResponse(error, operation) {
58
+ function createErrorResponse(error, operation, details) {
136
59
  const errorMessage = error instanceof Error ? error.message : String(error);
137
60
  console.error(`[${operation}] Error:`, error);
138
61
  return {
@@ -144,6 +67,7 @@ function createErrorResponse(error, operation) {
144
67
  error: errorMessage,
145
68
  operation,
146
69
  timestamp: new Date().toISOString(),
70
+ details,
147
71
  }),
148
72
  },
149
73
  ],
@@ -159,6 +83,10 @@ async function validateSession(operation) {
159
83
  session = null;
160
84
  throw new Error(`Session not initialized. Please call 'project-open' first before using ${operation}.`);
161
85
  }
86
+ const projectRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `project_rules.md`);
87
+ if (!(0, node_fs_1.existsSync)(projectRulesFile)) {
88
+ throw new Error(`Project rules file not found: ${projectRulesFile}. Please call 'retrieve-rules-context' first.`);
89
+ }
162
90
  return session;
163
91
  }
164
92
  // 文件名验证
@@ -173,20 +101,6 @@ function validateFileName(fileName) {
173
101
  }
174
102
  return fileName.trim();
175
103
  }
176
- // 图片文件验证
177
- function validateImageFile(localPath) {
178
- if (!localPath || localPath.trim() === '') {
179
- throw new Error('Local path cannot be empty');
180
- }
181
- const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'];
182
- const extension = localPath
183
- .toLowerCase()
184
- .substring(localPath.lastIndexOf('.'));
185
- if (!allowedExtensions.includes(extension)) {
186
- throw new Error(`Unsupported image format: ${extension}. Allowed formats: ${allowedExtensions.join(', ')}`);
187
- }
188
- return localPath.trim();
189
- }
190
104
  function getMaterialUri(session, fileName) {
191
105
  return session.sandbox.getUrl(`/zerocut/${session.terminal.id}/materials/${(0, node_path_1.basename)(fileName)}`);
192
106
  }
@@ -692,6 +606,7 @@ server.registerTool('project-open', {
692
606
  }
693
607
  const result = {
694
608
  success: true,
609
+ nextActionSuggest: '检查规则上下文是否已召回,若未召回,调用 retrieve_rules 工具召回规则上下文',
695
610
  sessionId: session.id,
696
611
  workDir,
697
612
  projectLocalDir,
@@ -825,7 +740,10 @@ server.registerTool('generate-character-image', {
825
740
  title: 'Generate Character Image',
826
741
  description: 'Generate a turnaround image or portrait for any character.',
827
742
  inputSchema: {
828
- type: zod_1.z.enum(['banana', 'seedream']).optional().default('banana'),
743
+ type: zod_1.z
744
+ .enum(['banana', 'banana-pro', 'seedream'])
745
+ .optional()
746
+ .default('banana'),
829
747
  name: zod_1.z.string().describe('The name of the character.'),
830
748
  gender: zod_1.z
831
749
  .enum(['male', 'female'])
@@ -1155,7 +1073,10 @@ server.registerTool('generate-image', {
1155
1073
  title: 'Generate Image',
1156
1074
  description: `生成图片`,
1157
1075
  inputSchema: {
1158
- type: zod_1.z.enum(['banana', 'seedream']).optional().default('seedream'),
1076
+ type: zod_1.z
1077
+ .enum(['banana', 'banana-pro', 'seedream'])
1078
+ .optional()
1079
+ .default('seedream'),
1159
1080
  prompt: zod_1.z
1160
1081
  .string()
1161
1082
  .describe('The prompt to generate. 一般要严格对应 storyboard 中当前场景的 start_frame 或 end_frame 中的字段描述'),
@@ -1520,6 +1441,11 @@ server.registerTool('edit-image', {
1520
1441
  description: 'Edit the image.',
1521
1442
  inputSchema: {
1522
1443
  prompt: zod_1.z.string().describe('要编辑图片的中文提示词'),
1444
+ type: zod_1.z
1445
+ .enum(['banana-pro', 'banana', 'seedream'])
1446
+ .optional()
1447
+ .default('seedream')
1448
+ .describe('The type of image model to use.'),
1523
1449
  sourceImageFileName: zod_1.z.string().describe('The source image file name.'),
1524
1450
  saveToFileName: zod_1.z.string().describe('The filename to save.'),
1525
1451
  size: zod_1.z
@@ -1563,7 +1489,7 @@ server.registerTool('edit-image', {
1563
1489
  .default(false)
1564
1490
  .describe('Whether to add watermark to the image.'),
1565
1491
  },
1566
- }, async ({ prompt, sourceImageFileName, saveToFileName, size, watermark }) => {
1492
+ }, async ({ prompt, type, sourceImageFileName, saveToFileName, size, watermark, }) => {
1567
1493
  try {
1568
1494
  // 验证session状态
1569
1495
  const currentSession = await validateSession('edit-image');
@@ -1589,8 +1515,9 @@ server.registerTool('edit-image', {
1589
1515
  const base64String = `data:${mimeType};base64,${imageBuffer.toString('base64')}`;
1590
1516
  imageBase64Array.push(base64String);
1591
1517
  const ai = currentSession.ai;
1592
- const processedPrompt = `转绘参考图片,${prompt}`;
1518
+ const processedPrompt = `转绘我提供的参考图片,${prompt}`;
1593
1519
  const res = await ai.generateImage({
1520
+ type,
1594
1521
  prompt: processedPrompt,
1595
1522
  size,
1596
1523
  watermark,
@@ -2091,7 +2018,7 @@ server.registerTool('generate-video', {
2091
2018
  console.warn('Failed to send progress update:', progressError);
2092
2019
  }
2093
2020
  },
2094
- waitForFinish: type !== 'zero', // zero 模型由于时间比较长,因此要异步查询而不是等待
2021
+ waitForFinish: true,
2095
2022
  });
2096
2023
  if (!res) {
2097
2024
  throw new Error('Failed to generate video: no response from AI service');
@@ -2139,7 +2066,7 @@ server.registerTool('generate-video', {
2139
2066
  type: 'text',
2140
2067
  text: JSON.stringify({
2141
2068
  success: true,
2142
- message: '该视频生成任务已提交,它是异步任务,且执行时间较长,你应立即调用工具 wait-for-task-finish 来等待任务结束,如该工具调用超时,你应立即再次重新调用直到任务结束。',
2069
+ message: '该视频生成任务正在运行中,它是异步任务,且执行时间较长,你应立即调用工具 wait-for-task-finish 来等待任务结束,如该工具调用超时,你应立即再次重新调用直到任务结束。',
2143
2070
  taskUrl: res.taskUrl,
2144
2071
  }),
2145
2072
  },
@@ -2194,6 +2121,7 @@ server.registerTool('wait-for-task-finish', {
2194
2121
  console.warn('Failed to send progress update:', progressError);
2195
2122
  }
2196
2123
  },
2124
+ timeout: 300000,
2197
2125
  });
2198
2126
  if (res.url) {
2199
2127
  const uri = await saveMaterial(currentSession, res.url, saveToFileName);
@@ -2478,7 +2406,9 @@ server.registerTool('generate-scene-tts', {
2478
2406
  'ASMR',
2479
2407
  ])
2480
2408
  .optional(),
2481
- voiceID: zod_1.z.string().describe(`适合作为视频配音的音色ID`),
2409
+ voiceID: zod_1.z
2410
+ .string()
2411
+ .describe(`适合作为视频配音的音色ID,除非用户指定,否则你必须已通过 search_voice 工具检查确定该音色确实是存在的。`),
2482
2412
  explicit_language: zod_1.z.enum(['zh', 'en', 'ja']).optional().default('zh'),
2483
2413
  },
2484
2414
  }, async ({ text, sceneIndex, storyBoardFile, skipConsistencyCheck, voiceID, saveToFileName, speed, pitch, volume, emotion, explicit_language, }) => {
@@ -2543,6 +2473,9 @@ server.registerTool('generate-scene-tts', {
2543
2473
  }
2544
2474
  console.log(`Generating TTS with voice: ${voiceID}, speed: ${finalSpeed}, text: ${text.substring(0, 100)}...`);
2545
2475
  const ai = currentSession.ai;
2476
+ if (voiceID.startsWith('BV0')) {
2477
+ throw new Error(`BV0* 系列音色已弃用,你必须已通过 search_voice 工具检查确定该音色确实是存在的。`);
2478
+ }
2546
2479
  const type = voiceID.startsWith('zh_') ||
2547
2480
  voiceID.startsWith('en_') ||
2548
2481
  voiceID.startsWith('multi_') ||
@@ -2841,7 +2774,9 @@ server.registerTool('get-schema', {
2841
2774
  title: 'Get Storyboard Schema or Draft Content Schema',
2842
2775
  description: 'Get the complete Storyboard or Draft Content JSON Schema definition. Use this schema to validate storyboard.json or draft_content.json files.',
2843
2776
  inputSchema: {
2844
- type: zod_1.z.enum(['storyboard', 'draft_content']),
2777
+ type: zod_1.z
2778
+ .enum(['storyboard', 'draft_content'])
2779
+ .describe('The type of schema to retrieve. Must be either "storyboard" or "draft_content". 用 type: storyboard 的 schema 生成 storyboard.json;用 type: draft_content 的 schema 生成 draft_content.json'),
2845
2780
  },
2846
2781
  }, async ({ type }) => {
2847
2782
  try {
@@ -3631,6 +3566,11 @@ server.registerTool('generate-video-by-ref', {
3631
3566
  prompt: zod_1.z
3632
3567
  .string()
3633
3568
  .describe('The prompt to generate video with or without reference images.'),
3569
+ rewritePrompt: zod_1.z
3570
+ .boolean()
3571
+ .optional()
3572
+ .default(true)
3573
+ .describe('Whether to rewrite the prompt.'),
3634
3574
  referenceImages: zod_1.z
3635
3575
  .array(zod_1.z.object({
3636
3576
  name: zod_1.z
@@ -3694,7 +3634,7 @@ server.registerTool('generate-video-by-ref', {
3694
3634
  .default(false)
3695
3635
  .describe('Whether to optimize the prompt.'),
3696
3636
  },
3697
- }, async ({ prompt, referenceImages, duration, size, watermark, type, mute, saveToFileName, sceneIndex, storyBoardFile, skipConsistencyCheck, optimizePrompt, }, context) => {
3637
+ }, async ({ prompt, rewritePrompt, referenceImages, duration, size, watermark, type, mute, saveToFileName, sceneIndex, storyBoardFile, skipConsistencyCheck, optimizePrompt, }, context) => {
3698
3638
  try {
3699
3639
  // 验证session状态
3700
3640
  const currentSession = await validateSession('generate-video-by-ref');
@@ -3861,14 +3801,17 @@ server.registerTool('generate-video-by-ref', {
3861
3801
  url: imageUrl,
3862
3802
  });
3863
3803
  console.log(`Added reference image URL: ${imageUrl} (name: ${imageRef.name}, type: ${imageRef.type})`);
3864
- promptPrefix += `参考“${imageRef.name}”(图${referenceImageUrls.length})${imageRef.type === 'subject' ? '主体形象' : '背景'}\n`;
3804
+ if (rewritePrompt) {
3805
+ promptPrefix += `参考“${imageRef.name}”(图${referenceImageUrls.length})${imageRef.type === 'subject' ? '主体形象' : '背景'}\n`;
3806
+ }
3865
3807
  }
3866
3808
  if (promptPrefix) {
3867
3809
  promptPrefix += '\n';
3868
3810
  }
3811
+ const finalPrompt = `${promptPrefix}${prompt}`;
3869
3812
  // 调用 referencesToVideo 函数
3870
3813
  const result = await currentSession.ai.referencesToVideo({
3871
- prompt: `${promptPrefix}${prompt}`,
3814
+ prompt: finalPrompt,
3872
3815
  reference_images: referenceImageUrls, // 使用URL数组而不是base64数组
3873
3816
  duration,
3874
3817
  size,
@@ -3911,6 +3854,8 @@ server.registerTool('generate-video-by-ref', {
3911
3854
  ratio: result.ratio,
3912
3855
  url: result.url,
3913
3856
  last_frame_url: result.last_frame_url,
3857
+ referenceImageUrls,
3858
+ prompt: finalPrompt,
3914
3859
  }, null, 2),
3915
3860
  },
3916
3861
  ],
@@ -4036,9 +3981,7 @@ server.registerTool('generate-video-by-template', {
4036
3981
  title: 'Generate Video by Template',
4037
3982
  description: 'Generate a video based on a template. The template must be a valid JSON string.',
4038
3983
  inputSchema: {
4039
- purpose: zod_1.z
4040
- .string()
4041
- .describe('The prompt to generate the video. 自动根据意图匹配模板'),
3984
+ user_request: zod_1.z.string().describe('用户请求,根据意图自动匹配模板'),
4042
3985
  text_prompts: zod_1.z
4043
3986
  .array(zod_1.z.string().describe('Text prompt for the template to build video.'))
4044
3987
  .optional()
@@ -4051,30 +3994,52 @@ server.registerTool('generate-video-by-template', {
4051
3994
  .string()
4052
3995
  .describe('The filename to save the generated video.'),
4053
3996
  },
4054
- }, async ({ purpose, text_prompts, saveToFileName, materials }) => {
3997
+ }, async ({ user_request, text_prompts, saveToFileName, materials }) => {
4055
3998
  try {
4056
- const templates = {
4057
- '7569583728302817331': '宠物唱歌',
4058
- '7569605825011367976': '万圣节宠物弹吉他',
4059
- };
3999
+ const templates = [
4000
+ {
4001
+ id: '7569583728302817331',
4002
+ name: '宠物唱歌',
4003
+ description: '用一张宠物照片,生成一段宠物唱歌的视频。',
4004
+ trigger: '根据{A图片}生成一段{宠物A}唱歌的视频',
4005
+ },
4006
+ {
4007
+ id: '7569605825011367976',
4008
+ name: '万圣节宠物弹吉他',
4009
+ description: '用一张宠物照片,生成一段宠物弹吉他的视频。',
4010
+ trigger: '根据{A图片}生成一段{宠物A}弹吉他的视频',
4011
+ },
4012
+ {
4013
+ id: '7572443489834844223',
4014
+ name: '图生动作模仿视频',
4015
+ description: '用一张图片和动作视频,生成一段图片主体模仿该动作视频的新视频。',
4016
+ trigger: '生成{A图片}模仿{B视频}的视频',
4017
+ },
4018
+ {
4019
+ id: '7575160546555674670',
4020
+ name: '文生动作模仿视频',
4021
+ description: '用一段提示词和视频,生成一段模仿该视频的新视频。',
4022
+ trigger: '生成一段{提示词A}模仿{B视频}的视频',
4023
+ },
4024
+ ];
4060
4025
  const currentSession = await validateSession('generate-video-by-template');
4061
4026
  const validatedFileName = validateFileName(saveToFileName);
4062
4027
  const ai = currentSession.ai;
4063
4028
  let completion = await ai.getCompletions({
4064
- model: 'Doubao-Seed-1.6-flash',
4029
+ model: 'Doubao-Seed-1.6',
4065
4030
  messages: [
4066
4031
  {
4067
4032
  role: 'system',
4068
- content: `你根据用户需求,从以下模板中选择一个匹配的模板,返回模板ID:\n\n${JSON.stringify(templates)}\n\n**约束**:只输出模板ID,不需要其他解释。`,
4033
+ content: `你根据用户需求,从以下模板中选择一个匹配的模板,返回模板ID:\n\n${JSON.stringify(templates)}\n\n**约束**:只输出模板ID,不需要其他解释,如果没有匹配的模版,输出"无匹配模版"`,
4069
4034
  },
4070
4035
  {
4071
4036
  role: 'user',
4072
- content: `用户意图:${purpose}`,
4037
+ content: user_request,
4073
4038
  },
4074
4039
  ],
4075
4040
  });
4076
4041
  const templateId = completion.choices[0]?.message?.content.trim();
4077
- if (!templateId) {
4042
+ if (!templateId || templateId === '无匹配模版') {
4078
4043
  throw new Error('Failed to get template ID');
4079
4044
  }
4080
4045
  const workflowInfo = await ai.getCozeWorkflowInfo(templateId);
@@ -4102,6 +4067,7 @@ ${text_prompts}
4102
4067
 
4103
4068
  ## **materials**:
4104
4069
  ${JSON.stringify(materialUrls)}`;
4070
+ // console.log(prompt);
4105
4071
  completion = await ai.getCompletions({
4106
4072
  model: 'Doubao-Seed-1.6-flash',
4107
4073
  messages: [
@@ -4123,7 +4089,20 @@ ${JSON.stringify(materialUrls)}`;
4123
4089
  if (!parameters) {
4124
4090
  throw new Error('Failed to get parameters');
4125
4091
  }
4126
- console.log(parameters);
4092
+ // console.log(parameters);
4093
+ // return {
4094
+ // content: [
4095
+ // {
4096
+ // type: 'text',
4097
+ // text: JSON.stringify({
4098
+ // success: true,
4099
+ // prompt,
4100
+ // templateId,
4101
+ // parameters,
4102
+ // }),
4103
+ // },
4104
+ // ],
4105
+ // };
4127
4106
  const result = await ai.runCozeWorkflow(templateId, JSON.parse(parameters).parameters);
4128
4107
  if (result.url) {
4129
4108
  // 保存到项目材料目录