cerevox 2.43.1 → 3.0.0-alpha.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.
- package/dist/core/ai.d.ts +28 -3
- package/dist/core/ai.d.ts.map +1 -1
- package/dist/core/ai.js +45 -16
- package/dist/core/ai.js.map +1 -1
- package/dist/mcp/servers/prompts/actions/storyboard_optimization.md +5 -5
- package/dist/{utils/videoproject-schema.json → mcp/servers/prompts/draft_content-schema.json} +3 -3
- package/dist/mcp/servers/prompts/rules/creative-ad.md +6 -6
- package/dist/mcp/servers/prompts/rules/expert.md +25 -25
- package/dist/mcp/servers/prompts/rules/freeform.md +2 -3
- package/dist/mcp/servers/prompts/rules/general-video.md +10 -10
- package/dist/mcp/servers/prompts/rules/material-creation.md +2 -2
- package/dist/mcp/servers/prompts/rules/music-video.md +3 -3
- package/dist/mcp/servers/prompts/rules/stage-play.md +4 -4
- package/dist/mcp/servers/prompts/rules/story-telling.md +8 -8
- package/dist/mcp/servers/prompts/skills/storyboard/storyboard-optimization-skill.md +5 -5
- package/dist/mcp/servers/prompts/skills/video/continuity-techniques.md +1 -1
- package/dist/mcp/servers/prompts/skills/workflows/general-video.md +10 -10
- package/dist/mcp/servers/prompts/skills/workflows/music-video.md +3 -3
- package/dist/mcp/servers/prompts/zerocut-core.md +26 -27
- package/dist/mcp/servers/zerocut.d.ts.map +1 -1
- package/dist/mcp/servers/zerocut.js +397 -521
- package/dist/mcp/servers/zerocut.js.map +1 -1
- package/dist/utils/coze.d.ts +1 -0
- package/dist/utils/coze.d.ts.map +1 -1
- package/dist/utils/coze.js +19 -0
- package/dist/utils/coze.js.map +1 -1
- package/package.json +2 -2
- package/dist/timeline-editor/index.d.ts +0 -42
- package/dist/timeline-editor/index.d.ts.map +0 -1
- package/dist/timeline-editor/index.js +0 -82
- package/dist/timeline-editor/index.js.map +0 -1
- package/dist/timeline-editor/public/app.js +0 -2086
- package/dist/timeline-editor/public/index.html +0 -141
- package/dist/timeline-editor/public/style.css +0 -695
- package/dist/timeline-editor/server.d.ts +0 -137
- package/dist/timeline-editor/server.d.ts.map +0 -1
- package/dist/timeline-editor/server.js +0 -418
- package/dist/timeline-editor/server.js.map +0 -1
- /package/dist/{utils → mcp/servers/prompts}/storyboard-schema.json +0 -0
|
@@ -53,85 +53,8 @@ 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
58
|
function createErrorResponse(error, operation) {
|
|
136
59
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
137
60
|
console.error(`[${operation}] Error:`, error);
|
|
@@ -159,6 +82,10 @@ async function validateSession(operation) {
|
|
|
159
82
|
session = null;
|
|
160
83
|
throw new Error(`Session not initialized. Please call 'project-open' first before using ${operation}.`);
|
|
161
84
|
}
|
|
85
|
+
const projectRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `project_rules.md`);
|
|
86
|
+
if (!(0, node_fs_1.existsSync)(projectRulesFile)) {
|
|
87
|
+
throw new Error(`Project rules file not found: ${projectRulesFile}. Please call 'retrieve-rules-context' first.`);
|
|
88
|
+
}
|
|
162
89
|
return session;
|
|
163
90
|
}
|
|
164
91
|
// 文件名验证
|
|
@@ -173,20 +100,6 @@ function validateFileName(fileName) {
|
|
|
173
100
|
}
|
|
174
101
|
return fileName.trim();
|
|
175
102
|
}
|
|
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
103
|
function getMaterialUri(session, fileName) {
|
|
191
104
|
return session.sandbox.getUrl(`/zerocut/${session.terminal.id}/materials/${(0, node_path_1.basename)(fileName)}`);
|
|
192
105
|
}
|
|
@@ -211,7 +124,7 @@ async function initProject(session) {
|
|
|
211
124
|
// 创建素材目录和成品目录 materials、output
|
|
212
125
|
await (await terminal.run('mkdir materials output')).end();
|
|
213
126
|
// 文件包括(大部分不需要此时创建)
|
|
214
|
-
//
|
|
127
|
+
// storyboard.json 故事版
|
|
215
128
|
// draft_content.json 草稿内容,Agent在创作过程中,会根据项目规范,自动生成和修改该文件
|
|
216
129
|
return workDir;
|
|
217
130
|
}
|
|
@@ -692,6 +605,7 @@ server.registerTool('project-open', {
|
|
|
692
605
|
}
|
|
693
606
|
const result = {
|
|
694
607
|
success: true,
|
|
608
|
+
nextActionSuggest: '检查规则上下文是否已召回,若未召回,调用 retrieve_rules 工具召回规则上下文',
|
|
695
609
|
sessionId: session.id,
|
|
696
610
|
workDir,
|
|
697
611
|
projectLocalDir,
|
|
@@ -1158,7 +1072,7 @@ server.registerTool('generate-image', {
|
|
|
1158
1072
|
type: zod_1.z.enum(['banana', 'seedream']).optional().default('seedream'),
|
|
1159
1073
|
prompt: zod_1.z
|
|
1160
1074
|
.string()
|
|
1161
|
-
.describe('The prompt to generate. 一般要严格对应
|
|
1075
|
+
.describe('The prompt to generate. 一般要严格对应 storyboard 中当前场景的 start_frame 或 end_frame 中的字段描述'),
|
|
1162
1076
|
sceneIndex: zod_1.z
|
|
1163
1077
|
.number()
|
|
1164
1078
|
.min(1)
|
|
@@ -1167,7 +1081,7 @@ server.registerTool('generate-image', {
|
|
|
1167
1081
|
storyBoardFile: zod_1.z
|
|
1168
1082
|
.string()
|
|
1169
1083
|
.optional()
|
|
1170
|
-
.default('
|
|
1084
|
+
.default('storyboard.json')
|
|
1171
1085
|
.describe('故事板文件路径'),
|
|
1172
1086
|
skipConsistencyCheck: zod_1.z
|
|
1173
1087
|
.boolean()
|
|
@@ -1252,7 +1166,7 @@ server.registerTool('generate-image', {
|
|
|
1252
1166
|
\`\`\`
|
|
1253
1167
|
`),
|
|
1254
1168
|
},
|
|
1255
|
-
}, async ({ type = 'seedream', prompt, sceneIndex, storyBoardFile = '
|
|
1169
|
+
}, async ({ type = 'seedream', prompt, sceneIndex, storyBoardFile = 'storyboard.json', skipConsistencyCheck = false, size = '720x1280', saveToFileName, watermark, referenceImages, optimizePrompt, }) => {
|
|
1256
1170
|
try {
|
|
1257
1171
|
// 验证session状态
|
|
1258
1172
|
const currentSession = await validateSession('generate-image');
|
|
@@ -1260,10 +1174,10 @@ server.registerTool('generate-image', {
|
|
|
1260
1174
|
// 检查 storyboard 标志
|
|
1261
1175
|
if (!checkStoryboardFlag && (0, node_fs_1.existsSync)(storyBoardPath)) {
|
|
1262
1176
|
checkStoryboardFlag = true;
|
|
1263
|
-
return createErrorResponse('必须先审查生成的
|
|
1177
|
+
return createErrorResponse('必须先审查生成的 storyboard.json 内容,确保每个场景中的stage_atmosphere内容按照规则被正确融合到start_frame和video_prompt中,不得遗漏,检查完成后先汇报,如果有问题,应当先修改 storyboard.json 内容,然后再调用 generate-image 生成图片。注意修改 storyboard 内容时,仅修改相应字段的字符串值,不要破坏JSON格式!', 'generate-image');
|
|
1264
1178
|
}
|
|
1265
1179
|
const validatedFileName = validateFileName(saveToFileName);
|
|
1266
|
-
// 校验 prompt 与
|
|
1180
|
+
// 校验 prompt 与 storyboard.json 中场景设定的一致性
|
|
1267
1181
|
if (sceneIndex && !skipConsistencyCheck) {
|
|
1268
1182
|
try {
|
|
1269
1183
|
if ((0, node_fs_1.existsSync)(storyBoardPath)) {
|
|
@@ -1283,9 +1197,9 @@ server.registerTool('generate-image', {
|
|
|
1283
1197
|
const endFrame = scene.end_frame;
|
|
1284
1198
|
// 检查 prompt 是否严格等于 start_frame 或 end_frame
|
|
1285
1199
|
if (prompt !== startFrame && prompt !== endFrame) {
|
|
1286
|
-
return createErrorResponse('图片提示词必须严格遵照
|
|
1200
|
+
return createErrorResponse('图片提示词必须严格遵照storyboard的设定,如果用户明确指出不需要遵守,请将skipConsistencyCheck设置为true后再次调用', 'generate-image');
|
|
1287
1201
|
}
|
|
1288
|
-
// 校验 size 参数与
|
|
1202
|
+
// 校验 size 参数与 storyboard 的 orientation 属性一致性
|
|
1289
1203
|
if (size && storyBoard.orientation) {
|
|
1290
1204
|
const isLandscapeSize = [
|
|
1291
1205
|
'1152x864',
|
|
@@ -1330,7 +1244,7 @@ server.registerTool('generate-image', {
|
|
|
1330
1244
|
}
|
|
1331
1245
|
}
|
|
1332
1246
|
else {
|
|
1333
|
-
console.warn(`Scene index ${sceneIndex} not found in
|
|
1247
|
+
console.warn(`Scene index ${sceneIndex} not found in storyboard.json`);
|
|
1334
1248
|
}
|
|
1335
1249
|
}
|
|
1336
1250
|
}
|
|
@@ -1340,7 +1254,7 @@ server.registerTool('generate-image', {
|
|
|
1340
1254
|
}
|
|
1341
1255
|
catch (error) {
|
|
1342
1256
|
console.error('Failed to validate prompt with story board:', error);
|
|
1343
|
-
// 如果读取或解析
|
|
1257
|
+
// 如果读取或解析 storyboard.json 失败,继续执行但记录警告
|
|
1344
1258
|
}
|
|
1345
1259
|
}
|
|
1346
1260
|
// 检查并替换英文单引号包裹的中文内容为中文双引号
|
|
@@ -1647,7 +1561,7 @@ server.registerTool('generate-video', {
|
|
|
1647
1561
|
inputSchema: {
|
|
1648
1562
|
prompt: zod_1.z
|
|
1649
1563
|
.string()
|
|
1650
|
-
.describe('The prompt to generate. 一般要严格对应
|
|
1564
|
+
.describe('The prompt to generate. 一般要严格对应 storyboard 中当前场景的 video_prompt 字段描述'),
|
|
1651
1565
|
sceneIndex: zod_1.z
|
|
1652
1566
|
.number()
|
|
1653
1567
|
.min(1)
|
|
@@ -1656,7 +1570,7 @@ server.registerTool('generate-video', {
|
|
|
1656
1570
|
storyBoardFile: zod_1.z
|
|
1657
1571
|
.string()
|
|
1658
1572
|
.optional()
|
|
1659
|
-
.default('
|
|
1573
|
+
.default('storyboard.json')
|
|
1660
1574
|
.describe('故事板文件路径'),
|
|
1661
1575
|
skipConsistencyCheck: zod_1.z
|
|
1662
1576
|
.boolean()
|
|
@@ -1691,7 +1605,7 @@ server.registerTool('generate-video', {
|
|
|
1691
1605
|
.describe('The image file name of the start frame.'),
|
|
1692
1606
|
duration: zod_1.z
|
|
1693
1607
|
.number()
|
|
1694
|
-
.min(
|
|
1608
|
+
.min(1)
|
|
1695
1609
|
.max(23)
|
|
1696
1610
|
.describe('The duration of the video. 一般与 tts 配音音频时长向上取整秒(ceil)一致,或者与 timeline_analysis 中确定的歌曲片段时长一致'),
|
|
1697
1611
|
end_frame: zod_1.z
|
|
@@ -1718,7 +1632,7 @@ server.registerTool('generate-video', {
|
|
|
1718
1632
|
.default(false)
|
|
1719
1633
|
.describe('Whether to optimize the prompt.'),
|
|
1720
1634
|
},
|
|
1721
|
-
}, async ({ prompt, sceneIndex, storyBoardFile = '
|
|
1635
|
+
}, async ({ prompt, sceneIndex, storyBoardFile = 'storyboard.json', skipConsistencyCheck = false, saveToFileName, start_frame, end_frame, duration, watermark, resolution, type, optimizePrompt, saveLastFrameAs, }, context) => {
|
|
1722
1636
|
try {
|
|
1723
1637
|
// 验证session状态
|
|
1724
1638
|
const currentSession = await validateSession('generate-video');
|
|
@@ -1739,7 +1653,7 @@ server.registerTool('generate-video', {
|
|
|
1739
1653
|
console.warn(`zero 模型的视频仅支持 1080p 分辨率,用户指定的分辨率为 %s,已自动将 ${resolution} 转换为 1080p`, resolution);
|
|
1740
1654
|
resolution = '1080p';
|
|
1741
1655
|
}
|
|
1742
|
-
// 校验 prompt 与
|
|
1656
|
+
// 校验 prompt 与 storyboard.json 中场景设定的一致性以及视频时长与 timeline_analysis.json 中 proposed_video_scenes 的匹配
|
|
1743
1657
|
if (sceneIndex && !skipConsistencyCheck) {
|
|
1744
1658
|
try {
|
|
1745
1659
|
const storyBoardPath = (0, node_path_1.resolve)(process.env.ZEROCUT_PROJECT_CWD || process.cwd(), projectLocalDir, storyBoardFile);
|
|
@@ -1758,7 +1672,7 @@ server.registerTool('generate-video', {
|
|
|
1758
1672
|
if (scene) {
|
|
1759
1673
|
const videoPrompt = scene.video_prompt;
|
|
1760
1674
|
if (videoPrompt && prompt !== videoPrompt) {
|
|
1761
|
-
return createErrorResponse('视频提示词必须严格遵照
|
|
1675
|
+
return createErrorResponse('视频提示词必须严格遵照storyboard的设定,如果用户明确指出不需要遵守,请将skipConsistencyCheck设置为true后再次调用', 'generate-video');
|
|
1762
1676
|
}
|
|
1763
1677
|
if (scene.is_continuous && !end_frame) {
|
|
1764
1678
|
return createErrorResponse('连续场景必须指定end_frame参数,如果用户明确指出不需要遵守,请将skipConsistencyCheck设置为true后再次调用', 'generate-video');
|
|
@@ -1771,7 +1685,7 @@ server.registerTool('generate-video', {
|
|
|
1771
1685
|
}
|
|
1772
1686
|
}
|
|
1773
1687
|
else {
|
|
1774
|
-
console.warn(`Scene index ${sceneIndex} not found in
|
|
1688
|
+
console.warn(`Scene index ${sceneIndex} not found in storyboard.json`);
|
|
1775
1689
|
}
|
|
1776
1690
|
}
|
|
1777
1691
|
}
|
|
@@ -1781,7 +1695,7 @@ server.registerTool('generate-video', {
|
|
|
1781
1695
|
}
|
|
1782
1696
|
catch (error) {
|
|
1783
1697
|
console.error('Failed to validate prompt with story board:', error);
|
|
1784
|
-
// 如果读取或解析
|
|
1698
|
+
// 如果读取或解析 storyboard.json 失败,继续执行但记录警告
|
|
1785
1699
|
}
|
|
1786
1700
|
// 校验视频时长与 timeline_analysis.json 中 proposed_video_scenes 的匹配
|
|
1787
1701
|
try {
|
|
@@ -1818,10 +1732,10 @@ server.registerTool('generate-video', {
|
|
|
1818
1732
|
if (!checkAudioVideoDurationFlag) {
|
|
1819
1733
|
checkAudioVideoDurationFlag = true;
|
|
1820
1734
|
if (scene.audio_mode === 'vo_sync') {
|
|
1821
|
-
return createErrorResponse('请先自我检查 media_logs 中的音频时长,确保
|
|
1735
|
+
return createErrorResponse('请先自我检查 media_logs 中的音频时长,确保 storyboard 中视频时长为音频时长向上取整 即 ceil(音频时长),然后再按照正确的视频时长创建视频', 'generate-video');
|
|
1822
1736
|
}
|
|
1823
1737
|
else if (scene.audio_mode === 'dialogue') {
|
|
1824
|
-
return createErrorResponse('请先自我检查 media_logs 中的音频时长,确保
|
|
1738
|
+
return createErrorResponse('请先自我检查 media_logs 中的音频时长,确保 storyboard 中视频时长**不小于**音频时长向上取整 即 ceil(音频时长),然后再按照正确的视频时长创建视频', 'generate-video');
|
|
1825
1739
|
}
|
|
1826
1740
|
}
|
|
1827
1741
|
}
|
|
@@ -1946,10 +1860,10 @@ server.registerTool('generate-video', {
|
|
|
1946
1860
|
type: 'string',
|
|
1947
1861
|
description: 'kenburns 视频 宽x高',
|
|
1948
1862
|
},
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1863
|
+
duration: {
|
|
1864
|
+
type: 'number',
|
|
1865
|
+
description: 'kenburns 视频 时长',
|
|
1866
|
+
},
|
|
1953
1867
|
},
|
|
1954
1868
|
required: ['camera_motion', 'size', 'duration'],
|
|
1955
1869
|
},
|
|
@@ -2091,7 +2005,7 @@ server.registerTool('generate-video', {
|
|
|
2091
2005
|
console.warn('Failed to send progress update:', progressError);
|
|
2092
2006
|
}
|
|
2093
2007
|
},
|
|
2094
|
-
waitForFinish:
|
|
2008
|
+
waitForFinish: true,
|
|
2095
2009
|
});
|
|
2096
2010
|
if (!res) {
|
|
2097
2011
|
throw new Error('Failed to generate video: no response from AI service');
|
|
@@ -2139,7 +2053,7 @@ server.registerTool('generate-video', {
|
|
|
2139
2053
|
type: 'text',
|
|
2140
2054
|
text: JSON.stringify({
|
|
2141
2055
|
success: true,
|
|
2142
|
-
message: '
|
|
2056
|
+
message: '该视频生成任务正在运行中,它是异步任务,且执行时间较长,你应立即调用工具 wait-for-task-finish 来等待任务结束,如该工具调用超时,你应立即再次重新调用直到任务结束。',
|
|
2143
2057
|
taskUrl: res.taskUrl,
|
|
2144
2058
|
}),
|
|
2145
2059
|
},
|
|
@@ -2194,6 +2108,7 @@ server.registerTool('wait-for-task-finish', {
|
|
|
2194
2108
|
console.warn('Failed to send progress update:', progressError);
|
|
2195
2109
|
}
|
|
2196
2110
|
},
|
|
2111
|
+
timeout: 300000,
|
|
2197
2112
|
});
|
|
2198
2113
|
if (res.url) {
|
|
2199
2114
|
const uri = await saveMaterial(currentSession, res.url, saveToFileName);
|
|
@@ -2423,7 +2338,7 @@ server.registerTool('generate-scene-tts', {
|
|
|
2423
2338
|
storyBoardFile: zod_1.z
|
|
2424
2339
|
.string()
|
|
2425
2340
|
.optional()
|
|
2426
|
-
.default('
|
|
2341
|
+
.default('storyboard.json')
|
|
2427
2342
|
.describe('故事板文件路径'),
|
|
2428
2343
|
skipConsistencyCheck: zod_1.z
|
|
2429
2344
|
.boolean()
|
|
@@ -2478,7 +2393,9 @@ server.registerTool('generate-scene-tts', {
|
|
|
2478
2393
|
'ASMR',
|
|
2479
2394
|
])
|
|
2480
2395
|
.optional(),
|
|
2481
|
-
voiceID: zod_1.z
|
|
2396
|
+
voiceID: zod_1.z
|
|
2397
|
+
.string()
|
|
2398
|
+
.describe(`适合作为视频配音的音色ID,除非用户指定,否则你必须已通过 search_voice 工具检查确定该音色确实是存在的。`),
|
|
2482
2399
|
explicit_language: zod_1.z.enum(['zh', 'en', 'ja']).optional().default('zh'),
|
|
2483
2400
|
},
|
|
2484
2401
|
}, async ({ text, sceneIndex, storyBoardFile, skipConsistencyCheck, voiceID, saveToFileName, speed, pitch, volume, emotion, explicit_language, }) => {
|
|
@@ -2489,7 +2406,7 @@ server.registerTool('generate-scene-tts', {
|
|
|
2489
2406
|
const finalSpeed = speed ?? 1;
|
|
2490
2407
|
volume = volume ?? 1;
|
|
2491
2408
|
let scene = null;
|
|
2492
|
-
// 校验 text 与
|
|
2409
|
+
// 校验 text 与 storyboard.json 中场景设定的一致性
|
|
2493
2410
|
if (sceneIndex && !skipConsistencyCheck) {
|
|
2494
2411
|
try {
|
|
2495
2412
|
const storyBoardPath = (0, node_path_1.resolve)(process.env.ZEROCUT_PROJECT_CWD || process.cwd(), projectLocalDir, storyBoardFile);
|
|
@@ -2524,11 +2441,11 @@ server.registerTool('generate-scene-tts', {
|
|
|
2524
2441
|
}
|
|
2525
2442
|
}
|
|
2526
2443
|
if (!isValidText) {
|
|
2527
|
-
return createErrorResponse('配音文本必须严格遵照
|
|
2444
|
+
return createErrorResponse('配音文本必须严格遵照storyboard的设定,如果用户明确指出不需要遵守,请将skipConsistencyCheck设置为true后再次调用', 'generate-scene-tts');
|
|
2528
2445
|
}
|
|
2529
2446
|
}
|
|
2530
2447
|
else {
|
|
2531
|
-
console.warn(`Scene index ${sceneIndex} not found in
|
|
2448
|
+
console.warn(`Scene index ${sceneIndex} not found in storyboard.json`);
|
|
2532
2449
|
}
|
|
2533
2450
|
}
|
|
2534
2451
|
}
|
|
@@ -2538,11 +2455,14 @@ server.registerTool('generate-scene-tts', {
|
|
|
2538
2455
|
}
|
|
2539
2456
|
catch (error) {
|
|
2540
2457
|
console.error('Failed to validate text with story board:', error);
|
|
2541
|
-
// 如果读取或解析
|
|
2458
|
+
// 如果读取或解析 storyboard.json 失败,继续执行但记录警告
|
|
2542
2459
|
}
|
|
2543
2460
|
}
|
|
2544
2461
|
console.log(`Generating TTS with voice: ${voiceID}, speed: ${finalSpeed}, text: ${text.substring(0, 100)}...`);
|
|
2545
2462
|
const ai = currentSession.ai;
|
|
2463
|
+
if (voiceID.startsWith('BV0')) {
|
|
2464
|
+
throw new Error(`BV0* 系列音色已弃用,你必须已通过 search_voice 工具检查确定该音色确实是存在的。`);
|
|
2465
|
+
}
|
|
2546
2466
|
const type = voiceID.startsWith('zh_') ||
|
|
2547
2467
|
voiceID.startsWith('en_') ||
|
|
2548
2468
|
voiceID.startsWith('multi_') ||
|
|
@@ -2592,7 +2512,7 @@ server.registerTool('generate-scene-tts', {
|
|
|
2592
2512
|
console.log('TTS generated successfully, saving to materials...');
|
|
2593
2513
|
const { url, duration, ...opts } = res;
|
|
2594
2514
|
if (!skipConsistencyCheck && duration > 16) {
|
|
2595
|
-
return createErrorResponse('TTS duration exceeds 16 seconds, 建议调整文本长度、提升语速或拆分场景...,⚠️如简化文本内容或拆分文本,需要立即更新
|
|
2515
|
+
return createErrorResponse('TTS duration exceeds 16 seconds, 建议调整文本长度、提升语速或拆分场景...,⚠️如简化文本内容或拆分文本,需要立即更新 storyboard 以保持内容同步!如仍要生成,可设置 skipConsistencyCheck 为 true,跳过一致性检查。', 'generate-scene-tts');
|
|
2596
2516
|
}
|
|
2597
2517
|
if (!duration) {
|
|
2598
2518
|
return createErrorResponse('TTS duration not returned from AI service', 'generate-scene-tts');
|
|
@@ -2678,8 +2598,8 @@ server.registerTool('compile-and-run', {
|
|
|
2678
2598
|
checkStoryboardSubtitlesFlag = true;
|
|
2679
2599
|
return createErrorResponse(`请先对 draft_content 进行以下一致性检查:
|
|
2680
2600
|
|
|
2681
|
-
1. 检查字幕文字内容是否与
|
|
2682
|
-
2. 检查视频 resolution 设定是否与
|
|
2601
|
+
1. 检查字幕文字内容是否与 storyboard 中各个场景的 script 或 dialog 内容完全一致(⚠️ 允许字幕分段展示,只要最终文本保持一致就行)
|
|
2602
|
+
2. 检查视频 resolution 设定是否与 storyboard 的 orientation 设置一致,默认 720p 情况下视频尺寸应为横屏 1280x720,竖屏 720x1280,若视频为 1080p 则尺寸应分别为横屏 1920x1080 和竖屏 1080x1920,切勿设反
|
|
2683
2603
|
3. 除非用户明确表示不要背景音乐,否则应检查是否有生成并配置了 BGM,若无,则生成 BGM 并将其加入素材和轨道配置
|
|
2684
2604
|
|
|
2685
2605
|
以上检查任何一项有问题,先修复 draft_content 使其符合要求后再进行合成`, 'compile-and-run');
|
|
@@ -2837,47 +2757,26 @@ server.registerTool('compile-and-run', {
|
|
|
2837
2757
|
return createErrorResponse(error, 'compile-and-run');
|
|
2838
2758
|
}
|
|
2839
2759
|
});
|
|
2840
|
-
server.registerTool('get-
|
|
2841
|
-
title: 'Get
|
|
2842
|
-
description: 'Get the complete
|
|
2843
|
-
inputSchema: {
|
|
2844
|
-
|
|
2760
|
+
server.registerTool('get-schema', {
|
|
2761
|
+
title: 'Get Storyboard Schema or Draft Content Schema',
|
|
2762
|
+
description: 'Get the complete Storyboard or Draft Content JSON Schema definition. Use this schema to validate storyboard.json or draft_content.json files.',
|
|
2763
|
+
inputSchema: {
|
|
2764
|
+
type: zod_1.z.enum(['storyboard', 'draft_content']),
|
|
2765
|
+
},
|
|
2766
|
+
}, async ({ type }) => {
|
|
2845
2767
|
try {
|
|
2846
|
-
const schemaPath = (0, node_path_1.resolve)(__dirname,
|
|
2768
|
+
const schemaPath = (0, node_path_1.resolve)(__dirname, `./prompts/${type}-schema.json`);
|
|
2847
2769
|
const schemaContent = await (0, promises_1.readFile)(schemaPath, 'utf-8');
|
|
2848
2770
|
const schema = JSON.parse(schemaContent);
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
type: 'text',
|
|
2853
|
-
text: JSON.stringify({
|
|
2854
|
-
success: true,
|
|
2855
|
-
schema,
|
|
2856
|
-
important_guidelines: `⚠️ 生成文件时请严格遵守输出规范,字幕文本内容必须与 story_board.json 中的 script(或dialog) 字段的文本内容完全一致。
|
|
2771
|
+
let important_guidelines = '';
|
|
2772
|
+
if (type === 'draft_content') {
|
|
2773
|
+
important_guidelines = `⚠️ 生成文件时请严格遵守输出规范,字幕文本内容必须与 storyboard.json 中的 script(或dialog) 字段的文本内容完全一致。
|
|
2857
2774
|
|
|
2858
2775
|
** 字幕优化 **
|
|
2859
|
-
* 在保证字幕文本内容与
|
|
2776
|
+
* 在保证字幕文本内容与 storyboard.json 中的 script(或dialog) 字段的文本内容完全一致的前提下,可根据 tts 返回的 \`captions.utterances\` 字段对字幕的显示进行优化,将过长的字幕分段显示,在 draft_content.json 中使用分段字幕,captions 的内容在 media_logs.json 中可查询到。
|
|
2860
2777
|
* 如用户未特殊指定,字幕样式(字体及大小)务必遵守输出规范
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
}),
|
|
2864
|
-
},
|
|
2865
|
-
],
|
|
2866
|
-
};
|
|
2867
|
-
}
|
|
2868
|
-
catch (error) {
|
|
2869
|
-
return createErrorResponse(error, 'get-video-project-schema');
|
|
2870
|
-
}
|
|
2871
|
-
});
|
|
2872
|
-
server.registerTool('get-storyboard-schema', {
|
|
2873
|
-
title: 'Get Storyboard Schema',
|
|
2874
|
-
description: 'Get the complete Storyboard JSON Schema definition.',
|
|
2875
|
-
inputSchema: {},
|
|
2876
|
-
}, async () => {
|
|
2877
|
-
try {
|
|
2878
|
-
const schemaPath = (0, node_path_1.resolve)(__dirname, '../../utils/storyboard-schema.json');
|
|
2879
|
-
const schemaContent = await (0, promises_1.readFile)(schemaPath, 'utf-8');
|
|
2880
|
-
const schema = JSON.parse(schemaContent);
|
|
2778
|
+
`;
|
|
2779
|
+
}
|
|
2881
2780
|
return {
|
|
2882
2781
|
content: [
|
|
2883
2782
|
{
|
|
@@ -2885,6 +2784,7 @@ server.registerTool('get-storyboard-schema', {
|
|
|
2885
2784
|
text: JSON.stringify({
|
|
2886
2785
|
success: true,
|
|
2887
2786
|
schema,
|
|
2787
|
+
important_guidelines,
|
|
2888
2788
|
timestamp: new Date().toISOString(),
|
|
2889
2789
|
}),
|
|
2890
2790
|
},
|
|
@@ -2892,7 +2792,7 @@ server.registerTool('get-storyboard-schema', {
|
|
|
2892
2792
|
};
|
|
2893
2793
|
}
|
|
2894
2794
|
catch (error) {
|
|
2895
|
-
return createErrorResponse(error, 'get-
|
|
2795
|
+
return createErrorResponse(error, 'get-schema');
|
|
2896
2796
|
}
|
|
2897
2797
|
});
|
|
2898
2798
|
server.registerTool('do-storyboard-optimization', {
|
|
@@ -2912,7 +2812,7 @@ server.registerTool('do-storyboard-optimization', {
|
|
|
2912
2812
|
text: JSON.stringify({
|
|
2913
2813
|
content: {
|
|
2914
2814
|
guideline: storyboardOptimizationGuidelines,
|
|
2915
|
-
action: '你应当根据guideline优化
|
|
2815
|
+
action: '你应当根据guideline优化storyboard.json',
|
|
2916
2816
|
},
|
|
2917
2817
|
}),
|
|
2918
2818
|
},
|
|
@@ -3296,180 +3196,206 @@ server.registerTool('media-analyzer', {
|
|
|
3296
3196
|
return createErrorResponse(error, 'media-analyzer');
|
|
3297
3197
|
}
|
|
3298
3198
|
});
|
|
3299
|
-
server.registerTool(
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
}
|
|
3199
|
+
// server.registerTool(
|
|
3200
|
+
// 'image-aligner',
|
|
3201
|
+
// {
|
|
3202
|
+
// title: 'Image Aligner',
|
|
3203
|
+
// description:
|
|
3204
|
+
// 'Analyze image quality and alignment with prompt using AI Image Quality Inspector.',
|
|
3205
|
+
// inputSchema: {
|
|
3206
|
+
// imageFileName: z
|
|
3207
|
+
// .string()
|
|
3208
|
+
// .describe('The image file name in materials directory to analyze.'),
|
|
3209
|
+
// sceneIndex: z.number().min(1).describe('场景索引,从1开始的下标'),
|
|
3210
|
+
// storyBoardFile: z
|
|
3211
|
+
// .string()
|
|
3212
|
+
// .optional()
|
|
3213
|
+
// .default('storyboard.json')
|
|
3214
|
+
// .describe('故事板文件路径'),
|
|
3215
|
+
// imagePrompt: z
|
|
3216
|
+
// .string()
|
|
3217
|
+
// .optional()
|
|
3218
|
+
// .describe('可选的图片提示词,如果提供则覆盖storyboard中的提示词'),
|
|
3219
|
+
// customPrompt: z
|
|
3220
|
+
// .string()
|
|
3221
|
+
// .optional()
|
|
3222
|
+
// .describe('可选的额外用户要求,用于补充图片质量评估的特定需求'),
|
|
3223
|
+
// },
|
|
3224
|
+
// },
|
|
3225
|
+
// async ({
|
|
3226
|
+
// imageFileName,
|
|
3227
|
+
// sceneIndex,
|
|
3228
|
+
// storyBoardFile = 'storyboard.json',
|
|
3229
|
+
// imagePrompt,
|
|
3230
|
+
// customPrompt,
|
|
3231
|
+
// }) => {
|
|
3232
|
+
// try {
|
|
3233
|
+
// const currentSession = await validateSession('image-aligner');
|
|
3234
|
+
// // 验证图片文件
|
|
3235
|
+
// validateImageFile(imageFileName);
|
|
3236
|
+
// // 获取图片 URL
|
|
3237
|
+
// const imageUrl = getMaterialUri(currentSession, imageFileName);
|
|
3238
|
+
// // 确定要使用的提示词
|
|
3239
|
+
// let finalPrompt = imagePrompt;
|
|
3240
|
+
// // 如果没有提供imagePrompt,则从storyboard中获取
|
|
3241
|
+
// if (!imagePrompt) {
|
|
3242
|
+
// try {
|
|
3243
|
+
// const storyBoardPath = resolve(
|
|
3244
|
+
// process.env.ZEROCUT_PROJECT_CWD || process.cwd(),
|
|
3245
|
+
// projectLocalDir,
|
|
3246
|
+
// storyBoardFile
|
|
3247
|
+
// );
|
|
3248
|
+
// if (existsSync(storyBoardPath)) {
|
|
3249
|
+
// const storyBoardContent = await readFile(storyBoardPath, 'utf8');
|
|
3250
|
+
// const storyBoard = JSON.parse(storyBoardContent);
|
|
3251
|
+
// if (storyBoard.scenes && Array.isArray(storyBoard.scenes)) {
|
|
3252
|
+
// const scene = storyBoard.scenes[sceneIndex - 1]; // sceneIndex 从1开始,数组从0开始
|
|
3253
|
+
// if (scene) {
|
|
3254
|
+
// // 根据文件名判断优先级:若end_frame存在且imageFileName包含"_end"则优先取end_frame,否则取start_frame
|
|
3255
|
+
// if (scene.end_frame && imageFileName.includes('_end')) {
|
|
3256
|
+
// finalPrompt = scene.end_frame;
|
|
3257
|
+
// } else {
|
|
3258
|
+
// finalPrompt = scene.start_frame || scene.end_frame;
|
|
3259
|
+
// }
|
|
3260
|
+
// if (!finalPrompt) {
|
|
3261
|
+
// return createErrorResponse(
|
|
3262
|
+
// `场景 ${sceneIndex} 中未找到 start_frame 或 end_frame 提示词`,
|
|
3263
|
+
// 'image-aligner'
|
|
3264
|
+
// );
|
|
3265
|
+
// }
|
|
3266
|
+
// } else {
|
|
3267
|
+
// return createErrorResponse(
|
|
3268
|
+
// `在 ${storyBoardFile} 中未找到场景索引 ${sceneIndex}`,
|
|
3269
|
+
// 'image-aligner'
|
|
3270
|
+
// );
|
|
3271
|
+
// }
|
|
3272
|
+
// } else {
|
|
3273
|
+
// return createErrorResponse(
|
|
3274
|
+
// `${storyBoardFile} 文件格式不正确,缺少 scenes 数组`,
|
|
3275
|
+
// 'image-aligner'
|
|
3276
|
+
// );
|
|
3277
|
+
// }
|
|
3278
|
+
// } else {
|
|
3279
|
+
// return createErrorResponse(
|
|
3280
|
+
// `故事板文件不存在: ${storyBoardPath}`,
|
|
3281
|
+
// 'image-aligner'
|
|
3282
|
+
// );
|
|
3283
|
+
// }
|
|
3284
|
+
// } catch (error) {
|
|
3285
|
+
// return createErrorResponse(
|
|
3286
|
+
// `读取或解析故事板文件失败: ${error}`,
|
|
3287
|
+
// 'image-aligner'
|
|
3288
|
+
// );
|
|
3289
|
+
// }
|
|
3290
|
+
// }
|
|
3291
|
+
// // 如果仍然没有提示词,返回错误
|
|
3292
|
+
// if (!finalPrompt) {
|
|
3293
|
+
// return createErrorResponse(
|
|
3294
|
+
// '未提供 imagePrompt 且无法从故事板中获取提示词',
|
|
3295
|
+
// 'image-aligner'
|
|
3296
|
+
// );
|
|
3297
|
+
// }
|
|
3298
|
+
// // 读取图片质量检查指南
|
|
3299
|
+
// const alignerGuidelinePath = resolve(
|
|
3300
|
+
// __dirname,
|
|
3301
|
+
// './prompts/reasonings/image_aligner.md'
|
|
3302
|
+
// );
|
|
3303
|
+
// let alignerGuideline = '';
|
|
3304
|
+
// try {
|
|
3305
|
+
// alignerGuideline = await readFile(alignerGuidelinePath, 'utf8');
|
|
3306
|
+
// } catch (error) {
|
|
3307
|
+
// console.warn('无法读取图片质量检查指南:', error);
|
|
3308
|
+
// alignerGuideline =
|
|
3309
|
+
// '请对图片质量进行评估,包括构图、色彩、清晰度等方面。';
|
|
3310
|
+
// }
|
|
3311
|
+
// // 构建系统提示
|
|
3312
|
+
// const systemPrompt = `你是一个专业的AI图片质量检查员。请根据以下指南对图片进行评估:
|
|
3313
|
+
// ${alignerGuideline}
|
|
3314
|
+
// 请严格按照指南中的JSON格式返回评估结果。`;
|
|
3315
|
+
// // 构建用户提示
|
|
3316
|
+
// const userPrompt = `请对这张图片进行质量评估。
|
|
3317
|
+
// 原始提示词:${finalPrompt}${
|
|
3318
|
+
// customPrompt
|
|
3319
|
+
// ? `
|
|
3320
|
+
// 额外要求:${customPrompt}`
|
|
3321
|
+
// : ''
|
|
3322
|
+
// }
|
|
3323
|
+
// 请按照指南要求,返回包含评分、问题列表和优化建议的JSON格式结果。`;
|
|
3324
|
+
// // 调用AI模型进行图片质量评估
|
|
3325
|
+
// const ai = currentSession.ai;
|
|
3326
|
+
// const completion = await ai.getCompletions({
|
|
3327
|
+
// model: 'Doubao-Seed-1.6',
|
|
3328
|
+
// messages: [
|
|
3329
|
+
// {
|
|
3330
|
+
// role: 'system',
|
|
3331
|
+
// content: systemPrompt,
|
|
3332
|
+
// },
|
|
3333
|
+
// {
|
|
3334
|
+
// role: 'user',
|
|
3335
|
+
// content: [
|
|
3336
|
+
// {
|
|
3337
|
+
// type: 'image_url',
|
|
3338
|
+
// image_url: {
|
|
3339
|
+
// url: imageUrl,
|
|
3340
|
+
// },
|
|
3341
|
+
// },
|
|
3342
|
+
// {
|
|
3343
|
+
// type: 'text',
|
|
3344
|
+
// text: userPrompt,
|
|
3345
|
+
// },
|
|
3346
|
+
// ],
|
|
3347
|
+
// },
|
|
3348
|
+
// ],
|
|
3349
|
+
// });
|
|
3350
|
+
// const result = completion.choices[0]?.message?.content;
|
|
3351
|
+
// if (!result) {
|
|
3352
|
+
// throw new Error('No response from AI model');
|
|
3353
|
+
// }
|
|
3354
|
+
// // 解析AI响应
|
|
3355
|
+
// let alignmentResult;
|
|
3356
|
+
// try {
|
|
3357
|
+
// // 尝试从响应中提取JSON
|
|
3358
|
+
// const jsonMatch =
|
|
3359
|
+
// result.match(/```json\s*([\s\S]*?)\s*```/) ||
|
|
3360
|
+
// result.match(/\{[\s\S]*\}/);
|
|
3361
|
+
// if (jsonMatch) {
|
|
3362
|
+
// alignmentResult = JSON.parse(jsonMatch[1] || jsonMatch[0]);
|
|
3363
|
+
// } else {
|
|
3364
|
+
// // 如果没有找到JSON格式,尝试直接解析整个响应
|
|
3365
|
+
// alignmentResult = JSON.parse(result);
|
|
3366
|
+
// }
|
|
3367
|
+
// } catch (error) {
|
|
3368
|
+
// // 如果解析失败,返回原始响应
|
|
3369
|
+
// alignmentResult = {
|
|
3370
|
+
// error: 'JSON解析失败',
|
|
3371
|
+
// raw_response: result,
|
|
3372
|
+
// };
|
|
3373
|
+
// }
|
|
3374
|
+
// return {
|
|
3375
|
+
// content: [
|
|
3376
|
+
// {
|
|
3377
|
+
// type: 'text',
|
|
3378
|
+
// text: JSON.stringify({
|
|
3379
|
+
// success: true,
|
|
3380
|
+
// imageFileName,
|
|
3381
|
+
// sceneIndex,
|
|
3382
|
+
// storyBoardFile,
|
|
3383
|
+
// imagePrompt: finalPrompt,
|
|
3384
|
+
// customPrompt,
|
|
3385
|
+
// promptSource: imagePrompt ? 'manual_override' : 'storyboard',
|
|
3386
|
+
// analysis: alignmentResult,
|
|
3387
|
+
// imageUrl,
|
|
3388
|
+
// nextActionSuggest:
|
|
3389
|
+
// '可根据分析结果调整提示词,修改storyboard后,重新生成图片。',
|
|
3390
|
+
// }),
|
|
3391
|
+
// },
|
|
3392
|
+
// ],
|
|
3393
|
+
// };
|
|
3394
|
+
// } catch (error) {
|
|
3395
|
+
// return createErrorResponse(error, 'image-aligner');
|
|
3396
|
+
// }
|
|
3397
|
+
// }
|
|
3398
|
+
// );
|
|
3473
3399
|
server.registerTool('audio-video-sync', {
|
|
3474
3400
|
title: 'Audio Video Sync',
|
|
3475
3401
|
description: 'Generate audio-video-synced video by matching video with audio. 还可以对口型。',
|
|
@@ -3640,7 +3566,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3640
3566
|
.describe('Array of reference image objects with name, url and type. Can be empty for text-only generation.'),
|
|
3641
3567
|
duration: zod_1.z
|
|
3642
3568
|
.number()
|
|
3643
|
-
.min(
|
|
3569
|
+
.min(1)
|
|
3644
3570
|
.max(16)
|
|
3645
3571
|
.optional()
|
|
3646
3572
|
.default(5)
|
|
@@ -3671,7 +3597,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3671
3597
|
storyBoardFile: zod_1.z
|
|
3672
3598
|
.string()
|
|
3673
3599
|
.optional()
|
|
3674
|
-
.default('
|
|
3600
|
+
.default('storyboard.json')
|
|
3675
3601
|
.describe('故事板文件路径'),
|
|
3676
3602
|
skipConsistencyCheck: zod_1.z
|
|
3677
3603
|
.boolean()
|
|
@@ -3696,17 +3622,17 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3696
3622
|
// 检查 storyboard 标志
|
|
3697
3623
|
if (!checkStoryboardFlag && (0, node_fs_1.existsSync)(storyBoardPath)) {
|
|
3698
3624
|
checkStoryboardFlag = true;
|
|
3699
|
-
return createErrorResponse(`必须先审查生成的
|
|
3625
|
+
return createErrorResponse(`必须先审查生成的 storyboard.json 内容,按照如下步骤:
|
|
3700
3626
|
|
|
3701
3627
|
1. 确保每个场景中的stage_atmosphere内容按照规则被正确融合到video_prompt中,不得遗漏
|
|
3702
3628
|
2. 如有main_characters设定且包含了reference_image,或有reference_objects,需确保video_prompt描述已包含该场景相关main_characters和所有reference_objects中的物品或背景,并确保参考图具体内容已经在video_prompt中有明确描述,如果没有,可忽略。
|
|
3703
3629
|
3. 如有配音,先自我检查 media_logs 中的查音频时长,确保以匹配音频时长来生成视频
|
|
3704
3630
|
|
|
3705
|
-
检查完上述问题后先汇报,如果有需要,应当先修改
|
|
3631
|
+
检查完上述问题后先汇报,如果有需要,应当先修改 storyboard.json 内容,然后再调用 generate-video-by-ref 生成视频。注意修改 storyboard 内容时,仅修改相应字段的字符串值,不要破坏JSON格式!
|
|
3706
3632
|
|
|
3707
3633
|
再次调用 generate-video-by-ref 时,如需要参考图,要确保referenceImages使用正确(main_characters中的reference_image作为参考人物,reference_objects中的image作为参考物品或参考背景)`, 'generate-image');
|
|
3708
3634
|
}
|
|
3709
|
-
// 校验 prompt 与
|
|
3635
|
+
// 校验 prompt 与 storyboard.json 中场景设定的一致性(如果提供了 sceneIndex)
|
|
3710
3636
|
if (!skipConsistencyCheck && sceneIndex) {
|
|
3711
3637
|
try {
|
|
3712
3638
|
if ((0, node_fs_1.existsSync)(storyBoardPath)) {
|
|
@@ -3724,7 +3650,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3724
3650
|
if (scene) {
|
|
3725
3651
|
const videoPrompt = scene.video_prompt;
|
|
3726
3652
|
if (videoPrompt && prompt !== videoPrompt) {
|
|
3727
|
-
return createErrorResponse('视频提示词必须严格遵照
|
|
3653
|
+
return createErrorResponse('视频提示词必须严格遵照storyboard的设定,如果用户明确指出不需要遵守,请将skipConsistencyCheck设置为true后再次调用', 'generate-video-by-ref');
|
|
3728
3654
|
}
|
|
3729
3655
|
// 检查 scene.is_continuous 是否为 true
|
|
3730
3656
|
if (scene.is_continuous === true) {
|
|
@@ -3791,7 +3717,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3791
3717
|
}
|
|
3792
3718
|
}
|
|
3793
3719
|
else {
|
|
3794
|
-
console.warn(`Scene index ${sceneIndex} not found in
|
|
3720
|
+
console.warn(`Scene index ${sceneIndex} not found in storyboard.json`);
|
|
3795
3721
|
}
|
|
3796
3722
|
}
|
|
3797
3723
|
}
|
|
@@ -3801,7 +3727,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3801
3727
|
}
|
|
3802
3728
|
catch (error) {
|
|
3803
3729
|
console.error('Failed to validate prompt with story board:', error);
|
|
3804
|
-
// 如果读取或解析
|
|
3730
|
+
// 如果读取或解析 storyboard.json 失败,继续执行但记录警告
|
|
3805
3731
|
}
|
|
3806
3732
|
}
|
|
3807
3733
|
const validatedFileName = validateFileName(saveToFileName);
|
|
@@ -4026,6 +3952,132 @@ server.registerTool('extend-video-duration', {
|
|
|
4026
3952
|
return createErrorResponse(error, 'extend-video');
|
|
4027
3953
|
}
|
|
4028
3954
|
});
|
|
3955
|
+
server.registerTool('generate-video-by-template', {
|
|
3956
|
+
title: 'Generate Video by Template',
|
|
3957
|
+
description: 'Generate a video based on a template. The template must be a valid JSON string.',
|
|
3958
|
+
inputSchema: {
|
|
3959
|
+
purpose: zod_1.z
|
|
3960
|
+
.string()
|
|
3961
|
+
.describe('The prompt to generate the video. 自动根据意图匹配模板'),
|
|
3962
|
+
text_prompts: zod_1.z
|
|
3963
|
+
.array(zod_1.z.string().describe('Text prompt for the template to build video.'))
|
|
3964
|
+
.optional()
|
|
3965
|
+
.describe('Optional text prompts to use in the template.'),
|
|
3966
|
+
materials: zod_1.z
|
|
3967
|
+
.array(zod_1.z.string().describe('Material file name in materials directory.'))
|
|
3968
|
+
.optional()
|
|
3969
|
+
.describe('Optional materials to use in the template.'),
|
|
3970
|
+
saveToFileName: zod_1.z
|
|
3971
|
+
.string()
|
|
3972
|
+
.describe('The filename to save the generated video.'),
|
|
3973
|
+
},
|
|
3974
|
+
}, async ({ purpose, text_prompts, saveToFileName, materials }) => {
|
|
3975
|
+
try {
|
|
3976
|
+
const templates = {
|
|
3977
|
+
'7569583728302817331': '宠物唱歌',
|
|
3978
|
+
'7569605825011367976': '万圣节宠物弹吉他',
|
|
3979
|
+
};
|
|
3980
|
+
const currentSession = await validateSession('generate-video-by-template');
|
|
3981
|
+
const validatedFileName = validateFileName(saveToFileName);
|
|
3982
|
+
const ai = currentSession.ai;
|
|
3983
|
+
let completion = await ai.getCompletions({
|
|
3984
|
+
model: 'Doubao-Seed-1.6-flash',
|
|
3985
|
+
messages: [
|
|
3986
|
+
{
|
|
3987
|
+
role: 'system',
|
|
3988
|
+
content: `你根据用户需求,从以下模板中选择一个匹配的模板,返回模板ID:\n\n${JSON.stringify(templates)}\n\n**约束**:只输出模板ID,不需要其他解释。`,
|
|
3989
|
+
},
|
|
3990
|
+
{
|
|
3991
|
+
role: 'user',
|
|
3992
|
+
content: `用户意图:${purpose}`,
|
|
3993
|
+
},
|
|
3994
|
+
],
|
|
3995
|
+
});
|
|
3996
|
+
const templateId = completion.choices[0]?.message?.content.trim();
|
|
3997
|
+
if (!templateId) {
|
|
3998
|
+
throw new Error('Failed to get template ID');
|
|
3999
|
+
}
|
|
4000
|
+
const workflowInfo = await ai.getCozeWorkflowInfo(templateId);
|
|
4001
|
+
const materialUrls = materials?.map(material => getMaterialUri(currentSession, material));
|
|
4002
|
+
const schema = {
|
|
4003
|
+
name: 'workflow_parameters',
|
|
4004
|
+
schema: {
|
|
4005
|
+
type: 'object',
|
|
4006
|
+
properties: {
|
|
4007
|
+
parameters: {
|
|
4008
|
+
type: 'object',
|
|
4009
|
+
description: 'The parameters for the workflow.',
|
|
4010
|
+
},
|
|
4011
|
+
},
|
|
4012
|
+
required: ['parameters'],
|
|
4013
|
+
},
|
|
4014
|
+
};
|
|
4015
|
+
const prompt = `你根据模板工作流输入 schema 和 prompt、materials 生成一个 JSON 字符串,作为模板工作流的参数。
|
|
4016
|
+
|
|
4017
|
+
## **工作流输入 schema**:
|
|
4018
|
+
${JSON.stringify(workflowInfo.data.workflow_detail.description)}
|
|
4019
|
+
|
|
4020
|
+
## **prompt**:
|
|
4021
|
+
${text_prompts}
|
|
4022
|
+
|
|
4023
|
+
## **materials**:
|
|
4024
|
+
${JSON.stringify(materialUrls)}`;
|
|
4025
|
+
completion = await ai.getCompletions({
|
|
4026
|
+
model: 'Doubao-Seed-1.6-flash',
|
|
4027
|
+
messages: [
|
|
4028
|
+
{
|
|
4029
|
+
role: 'system',
|
|
4030
|
+
content: prompt,
|
|
4031
|
+
},
|
|
4032
|
+
{
|
|
4033
|
+
role: 'user',
|
|
4034
|
+
content: `生成模板调用的正确parameters参数`,
|
|
4035
|
+
},
|
|
4036
|
+
],
|
|
4037
|
+
response_format: {
|
|
4038
|
+
type: 'json_schema',
|
|
4039
|
+
json_schema: schema,
|
|
4040
|
+
},
|
|
4041
|
+
});
|
|
4042
|
+
const parameters = completion.choices[0]?.message?.content.trim();
|
|
4043
|
+
if (!parameters) {
|
|
4044
|
+
throw new Error('Failed to get parameters');
|
|
4045
|
+
}
|
|
4046
|
+
console.log(parameters);
|
|
4047
|
+
const result = await ai.runCozeWorkflow(templateId, JSON.parse(parameters).parameters);
|
|
4048
|
+
if (result.url) {
|
|
4049
|
+
// 保存到项目材料目录
|
|
4050
|
+
const uri = await saveMaterial(currentSession, result.url, validatedFileName);
|
|
4051
|
+
return {
|
|
4052
|
+
content: [
|
|
4053
|
+
{
|
|
4054
|
+
type: 'text',
|
|
4055
|
+
text: JSON.stringify({
|
|
4056
|
+
success: true,
|
|
4057
|
+
parameters,
|
|
4058
|
+
uri,
|
|
4059
|
+
}),
|
|
4060
|
+
},
|
|
4061
|
+
],
|
|
4062
|
+
};
|
|
4063
|
+
}
|
|
4064
|
+
return {
|
|
4065
|
+
content: [
|
|
4066
|
+
{
|
|
4067
|
+
type: 'text',
|
|
4068
|
+
text: JSON.stringify({
|
|
4069
|
+
success: false,
|
|
4070
|
+
result,
|
|
4071
|
+
parameters,
|
|
4072
|
+
}),
|
|
4073
|
+
},
|
|
4074
|
+
],
|
|
4075
|
+
};
|
|
4076
|
+
}
|
|
4077
|
+
catch (error) {
|
|
4078
|
+
return createErrorResponse(error, 'generate-video-by-template');
|
|
4079
|
+
}
|
|
4080
|
+
});
|
|
4029
4081
|
server.registerTool('run-ffmpeg-command', {
|
|
4030
4082
|
title: 'Run FFmpeg Command',
|
|
4031
4083
|
description: 'Run ffmpeg or ffprobe commands in the sandbox environment. Only ffmpeg and ffprobe commands are allowed. The default working directory is the materials directory.',
|
|
@@ -4269,182 +4321,6 @@ server.registerTool('build-capcat-draft', {
|
|
|
4269
4321
|
return createErrorResponse(error, 'build-capcat-draft');
|
|
4270
4322
|
}
|
|
4271
4323
|
});
|
|
4272
|
-
// server.registerTool(
|
|
4273
|
-
// 'custom-edit-draft',
|
|
4274
|
-
// {
|
|
4275
|
-
// title: 'Custom Edit Draft',
|
|
4276
|
-
// description:
|
|
4277
|
-
// 'Launch timeline editor server for custom draft editing. Starts a local server at specified port with draft file configuration.',
|
|
4278
|
-
// inputSchema: {
|
|
4279
|
-
// draftContentFileName: z
|
|
4280
|
-
// .string()
|
|
4281
|
-
// .optional()
|
|
4282
|
-
// .default('draft_content.json')
|
|
4283
|
-
// .describe('Draft content filename (default: draft_content.json)'),
|
|
4284
|
-
// port: z
|
|
4285
|
-
// .number()
|
|
4286
|
-
// .optional()
|
|
4287
|
-
// .default(6789)
|
|
4288
|
-
// .describe('Server port (default: 6789)'),
|
|
4289
|
-
// },
|
|
4290
|
-
// },
|
|
4291
|
-
// async ({ draftContentFileName = 'draft_content.json', port = 6789 }) => {
|
|
4292
|
-
// try {
|
|
4293
|
-
// // 使用项目本地目录
|
|
4294
|
-
// const workDir = resolve(
|
|
4295
|
-
// process.env.ZEROCUT_PROJECT_CWD || process.cwd(),
|
|
4296
|
-
// projectLocalDir
|
|
4297
|
-
// );
|
|
4298
|
-
// // 检查 draft_content.json 文件是否存在
|
|
4299
|
-
// const draftFilePath = resolve(workDir, draftContentFileName);
|
|
4300
|
-
// if (!existsSync(draftFilePath)) {
|
|
4301
|
-
// return createErrorResponse(
|
|
4302
|
-
// new Error(
|
|
4303
|
-
// `Draft content file not found: ${draftContentFileName}. Please generate draft content first using the generate-draft-content tool.`
|
|
4304
|
-
// ),
|
|
4305
|
-
// 'custom-edit-draft'
|
|
4306
|
-
// );
|
|
4307
|
-
// }
|
|
4308
|
-
// // 备份原始的 draft_content.json 文件
|
|
4309
|
-
// const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
4310
|
-
// const backupFileName = `${draftContentFileName}.backup.${timestamp}`;
|
|
4311
|
-
// const backupFilePath = resolve(workDir, backupFileName);
|
|
4312
|
-
// try {
|
|
4313
|
-
// const { copyFileSync } = await import('fs');
|
|
4314
|
-
// copyFileSync(draftFilePath, backupFilePath);
|
|
4315
|
-
// console.log(`Draft file backed up to: ${backupFileName}`);
|
|
4316
|
-
// } catch (backupError) {
|
|
4317
|
-
// console.warn(`Failed to backup draft file: ${backupError}`);
|
|
4318
|
-
// // 继续执行,不因备份失败而中断
|
|
4319
|
-
// }
|
|
4320
|
-
// // 构建启动命令参数
|
|
4321
|
-
// const timelineEditorPath = resolve(
|
|
4322
|
-
// __dirname,
|
|
4323
|
-
// '../../../dist/timeline-editor/index.js'
|
|
4324
|
-
// );
|
|
4325
|
-
// console.log(`Starting timeline editor server...`);
|
|
4326
|
-
// console.log(`Working directory: ${workDir}`);
|
|
4327
|
-
// console.log(`Draft file: ${draftContentFileName}`);
|
|
4328
|
-
// console.log(`Server port: ${port}`);
|
|
4329
|
-
// console.log(`Timeline editor path: ${timelineEditorPath}`);
|
|
4330
|
-
// // 检查端口是否被占用,如果被占用则停止占用的进程
|
|
4331
|
-
// if (await isPortInUse(port)) {
|
|
4332
|
-
// console.log(`Port ${port} is in use, killing existing processes...`);
|
|
4333
|
-
// await killPortProcess(port);
|
|
4334
|
-
// }
|
|
4335
|
-
// // 直接启动服务器进程
|
|
4336
|
-
// const platform = os.platform();
|
|
4337
|
-
// const nodeCommand = platform === 'win32' ? 'node.exe' : 'node';
|
|
4338
|
-
// const serverProcess = spawn(
|
|
4339
|
-
// nodeCommand,
|
|
4340
|
-
// [
|
|
4341
|
-
// timelineEditorPath,
|
|
4342
|
-
// '--draft-file',
|
|
4343
|
-
// draftContentFileName,
|
|
4344
|
-
// '--project-dir',
|
|
4345
|
-
// workDir,
|
|
4346
|
-
// '--port',
|
|
4347
|
-
// port.toString(),
|
|
4348
|
-
// ],
|
|
4349
|
-
// {
|
|
4350
|
-
// cwd: workDir,
|
|
4351
|
-
// stdio: 'pipe',
|
|
4352
|
-
// shell: platform === 'win32', // 在 Windows 下使用 shell
|
|
4353
|
-
// }
|
|
4354
|
-
// );
|
|
4355
|
-
// const serverUrl = `http://localhost:${port}`;
|
|
4356
|
-
// // 等待服务器启动 - 使用健康检查 API 轮询
|
|
4357
|
-
// await new Promise((resolve, reject) => {
|
|
4358
|
-
// const timeout = setTimeout(() => {
|
|
4359
|
-
// reject(new Error('Server startup timeout'));
|
|
4360
|
-
// }, 30000); // 增加超时时间到30秒
|
|
4361
|
-
// const checkHealth = async () => {
|
|
4362
|
-
// try {
|
|
4363
|
-
// const healthUrl = `${serverUrl}/health`;
|
|
4364
|
-
// const req = http.get(healthUrl, (res: any) => {
|
|
4365
|
-
// let data = '';
|
|
4366
|
-
// res.on('data', (chunk: any) => {
|
|
4367
|
-
// data += chunk;
|
|
4368
|
-
// });
|
|
4369
|
-
// res.on('end', () => {
|
|
4370
|
-
// try {
|
|
4371
|
-
// const healthData = JSON.parse(data);
|
|
4372
|
-
// if (healthData.status === 'ok') {
|
|
4373
|
-
// clearTimeout(timeout);
|
|
4374
|
-
// console.log('Server health check passed');
|
|
4375
|
-
// resolve(void 0);
|
|
4376
|
-
// } else {
|
|
4377
|
-
// // 继续轮询
|
|
4378
|
-
// setTimeout(checkHealth, 1000);
|
|
4379
|
-
// }
|
|
4380
|
-
// } catch (parseError) {
|
|
4381
|
-
// // 继续轮询
|
|
4382
|
-
// setTimeout(checkHealth, 1000);
|
|
4383
|
-
// }
|
|
4384
|
-
// });
|
|
4385
|
-
// });
|
|
4386
|
-
// req.on('error', () => {
|
|
4387
|
-
// // 服务器还未启动,继续轮询
|
|
4388
|
-
// setTimeout(checkHealth, 1000);
|
|
4389
|
-
// });
|
|
4390
|
-
// req.setTimeout(2000, () => {
|
|
4391
|
-
// req.destroy();
|
|
4392
|
-
// // 继续轮询
|
|
4393
|
-
// setTimeout(checkHealth, 1000);
|
|
4394
|
-
// });
|
|
4395
|
-
// } catch (error) {
|
|
4396
|
-
// // 继续轮询
|
|
4397
|
-
// setTimeout(checkHealth, 1000);
|
|
4398
|
-
// }
|
|
4399
|
-
// };
|
|
4400
|
-
// serverProcess.stdout?.on('data', (data: any) => {
|
|
4401
|
-
// console.log('Server output:', data.toString());
|
|
4402
|
-
// });
|
|
4403
|
-
// serverProcess.stderr?.on('data', (data: any) => {
|
|
4404
|
-
// console.error('Server error:', data.toString());
|
|
4405
|
-
// });
|
|
4406
|
-
// serverProcess.on('error', (error: any) => {
|
|
4407
|
-
// clearTimeout(timeout);
|
|
4408
|
-
// reject(error);
|
|
4409
|
-
// });
|
|
4410
|
-
// // 开始健康检查轮询
|
|
4411
|
-
// setTimeout(checkHealth, 2000); // 2秒后开始第一次检查
|
|
4412
|
-
// });
|
|
4413
|
-
// return {
|
|
4414
|
-
// content: [
|
|
4415
|
-
// {
|
|
4416
|
-
// type: 'text',
|
|
4417
|
-
// text: JSON.stringify(
|
|
4418
|
-
// {
|
|
4419
|
-
// success: true,
|
|
4420
|
-
// message: 'Timeline editor server started successfully',
|
|
4421
|
-
// serverUrl: serverUrl,
|
|
4422
|
-
// draftFile: draftContentFileName,
|
|
4423
|
-
// backupFile: backupFileName,
|
|
4424
|
-
// projectDirectory: workDir,
|
|
4425
|
-
// port: port,
|
|
4426
|
-
// pid: serverProcess.pid,
|
|
4427
|
-
// instructions: [
|
|
4428
|
-
// `Timeline editor is now running at: ${serverUrl}`,
|
|
4429
|
-
// 'Open the URL in your browser to edit the draft',
|
|
4430
|
-
// 'The server will automatically save changes to the draft file',
|
|
4431
|
-
// `Original draft file has been backed up as: ${backupFileName}`,
|
|
4432
|
-
// `Server process ID: ${serverProcess.pid}`,
|
|
4433
|
-
// 'The server will continue running in the background',
|
|
4434
|
-
// ],
|
|
4435
|
-
// },
|
|
4436
|
-
// null,
|
|
4437
|
-
// 2
|
|
4438
|
-
// ),
|
|
4439
|
-
// },
|
|
4440
|
-
// ],
|
|
4441
|
-
// };
|
|
4442
|
-
// } catch (error) {
|
|
4443
|
-
// console.error('Error starting timeline editor:', error);
|
|
4444
|
-
// return createErrorResponse(error, 'custom-edit-draft');
|
|
4445
|
-
// }
|
|
4446
|
-
// }
|
|
4447
|
-
// );
|
|
4448
4324
|
async function run() {
|
|
4449
4325
|
// Start receiving messages on stdin and sending messages on stdout
|
|
4450
4326
|
const transport = new stdio_js_1.StdioServerTransport();
|