@visionengine/remotion-file-bridge 1.0.1 → 1.0.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/server.js +83 -12
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
1
2
|
import { FastMCP } from 'fastmcp';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import { deleteProjectFile, downloadProjectFile, listProjectFiles, readProjectFile, saveProjectFile } from './client.js';
|
|
@@ -44,21 +45,91 @@ server.addTool({
|
|
|
44
45
|
}),
|
|
45
46
|
execute: async (args) => JSON.stringify(await downloadProjectFile(args), null, 2),
|
|
46
47
|
});
|
|
48
|
+
const saveFileParameters = z.object({
|
|
49
|
+
userId: z.string().optional(),
|
|
50
|
+
sourceType: z.enum(['local_path', 'text', 'base64', 'remote_url']).describe('Choose local_path for local files. Choose text/base64/remote_url when content is provided in content field.'),
|
|
51
|
+
localPath: z.string().trim().min(1).optional().describe('Required when sourceType=local_path. Can be an absolute path or a path relative to WORKDIR.'),
|
|
52
|
+
content: z.string().optional().describe('Required for sourceType=text, base64, or remote_url. For remote_url, put URL in this field.'),
|
|
53
|
+
fileName: z.string().min(1).describe('Final file name in remote workspace, including extension, e.g. baidu_ai.png.'),
|
|
54
|
+
targetPath: z.string().optional().describe('Target directory path only, workspace-relative, e.g. public/images/photos. Do not include fileName.'),
|
|
55
|
+
contentType: z.string().optional(),
|
|
56
|
+
overwrite: z.boolean().optional(),
|
|
57
|
+
}).superRefine((value, ctx) => {
|
|
58
|
+
if (value.sourceType === 'local_path' && !value.localPath) {
|
|
59
|
+
ctx.addIssue({
|
|
60
|
+
code: z.ZodIssueCode.custom,
|
|
61
|
+
path: ['localPath'],
|
|
62
|
+
message: 'localPath is required when sourceType=local_path',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (value.sourceType !== 'local_path' && typeof value.content !== 'string') {
|
|
66
|
+
ctx.addIssue({
|
|
67
|
+
code: z.ZodIssueCode.custom,
|
|
68
|
+
path: ['content'],
|
|
69
|
+
message: `content is required when sourceType=${value.sourceType}`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
function normalizeTargetDirectory(targetPath, fileName) {
|
|
74
|
+
if (!targetPath?.trim()) {
|
|
75
|
+
return { targetPath };
|
|
76
|
+
}
|
|
77
|
+
const normalized = targetPath.trim().replaceAll('\\', '/').replace(/^\/+/, '').replace(/\/+/g, '/');
|
|
78
|
+
const parts = normalized.split('/').filter(Boolean);
|
|
79
|
+
if (parts.length === 0) {
|
|
80
|
+
return { targetPath: undefined };
|
|
81
|
+
}
|
|
82
|
+
const last = parts[parts.length - 1];
|
|
83
|
+
const normalizedFileName = path.posix.basename(fileName).toLowerCase();
|
|
84
|
+
const hasSameFileNameAsTail = last.toLowerCase() === normalizedFileName;
|
|
85
|
+
if (hasSameFileNameAsTail) {
|
|
86
|
+
parts.pop();
|
|
87
|
+
}
|
|
88
|
+
const normalizedTargetPath = parts.join('/') || undefined;
|
|
89
|
+
const looksLikeFilePath = /\.[^/.]+$/.test(last);
|
|
90
|
+
if (hasSameFileNameAsTail || looksLikeFilePath) {
|
|
91
|
+
return {
|
|
92
|
+
targetPath: normalizedTargetPath,
|
|
93
|
+
warning: {
|
|
94
|
+
code: 'target_path_should_be_directory',
|
|
95
|
+
message: 'targetPath should be a directory path and should not include fileName. The value was normalized before saving.',
|
|
96
|
+
source: 'save_file',
|
|
97
|
+
details: {
|
|
98
|
+
fileName,
|
|
99
|
+
originalTargetPath: targetPath,
|
|
100
|
+
normalizedTargetPath,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return { targetPath: normalizedTargetPath };
|
|
106
|
+
}
|
|
47
107
|
server.addTool({
|
|
48
108
|
name: 'save_file',
|
|
49
|
-
description: 'Use only when the user explicitly wants to save or upload content into the remote Remotion/VEC cloud workspace. Saves local files, text, base64 payloads, or remote URLs into the current user remote workspace.',
|
|
109
|
+
description: 'Use only when the user explicitly wants to save or upload content into the remote Remotion/VEC cloud workspace. Saves local files, text, base64 payloads, or remote URLs into the current user remote workspace. Always provide localPath when sourceType=local_path. targetPath should be a directory, and fileName should contain the final file name.',
|
|
50
110
|
annotations: { title: 'Save Remotion Project File', readOnlyHint: false, openWorldHint: true },
|
|
51
|
-
parameters:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
111
|
+
parameters: saveFileParameters,
|
|
112
|
+
execute: async (args) => {
|
|
113
|
+
const normalized = normalizeTargetDirectory(args.targetPath, args.fileName);
|
|
114
|
+
const normalizedArgs = {
|
|
115
|
+
...args,
|
|
116
|
+
targetPath: normalized.targetPath,
|
|
117
|
+
};
|
|
118
|
+
const result = await saveProjectFile(normalizedArgs);
|
|
119
|
+
if (!normalized.warning) {
|
|
120
|
+
return JSON.stringify(result, null, 2);
|
|
121
|
+
}
|
|
122
|
+
const metadata = result.metadata && typeof result.metadata === 'object'
|
|
123
|
+
? result.metadata
|
|
124
|
+
: {};
|
|
125
|
+
return JSON.stringify({
|
|
126
|
+
...result,
|
|
127
|
+
metadata: {
|
|
128
|
+
...metadata,
|
|
129
|
+
warnings: [...(Array.isArray(metadata.warnings) ? metadata.warnings : []), normalized.warning],
|
|
130
|
+
},
|
|
131
|
+
}, null, 2);
|
|
132
|
+
},
|
|
62
133
|
});
|
|
63
134
|
server.addTool({
|
|
64
135
|
name: 'delete_file',
|