@mixio-pro/kalaasetu-mcp 1.0.10 → 1.0.12
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/package.json +1 -1
- package/src/storage/index.ts +1 -0
- package/src/storage/local.ts +6 -1
- package/src/tools/gemini.ts +36 -18
- package/src/tools/image-to-video.ts +28 -8
- package/src/utils/filename.ts +19 -3
package/package.json
CHANGED
package/src/storage/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export function getStorage(): StorageProvider {
|
|
|
8
8
|
if (!storageInstance) {
|
|
9
9
|
const type = process.env.STORAGE_PROVIDER || "local";
|
|
10
10
|
console.error(`Initializing storage provider: ${type}`);
|
|
11
|
+
console.error(`Base path (cwd): ${process.cwd()}`);
|
|
11
12
|
|
|
12
13
|
if (type === "gcs") {
|
|
13
14
|
const bucket = process.env.GCS_BUCKET;
|
package/src/storage/local.ts
CHANGED
|
@@ -40,7 +40,12 @@ export class LocalStorageProvider implements StorageProvider {
|
|
|
40
40
|
if (!path.isAbsolute(filePath)) {
|
|
41
41
|
fullPath = path.resolve(this.basePath, filePath);
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
try {
|
|
44
|
+
await fs.promises.access(fullPath, fs.constants.F_OK);
|
|
45
|
+
return true;
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
async getPublicUrl(filePath: string): Promise<string> {
|
package/src/tools/gemini.ts
CHANGED
|
@@ -18,9 +18,23 @@ const ai = new GoogleGenAI({
|
|
|
18
18
|
|
|
19
19
|
async function fileToGenerativePart(filePath: string) {
|
|
20
20
|
const storage = getStorage();
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
|
|
22
|
+
// Check if file exists
|
|
23
|
+
const exists = await storage.exists(filePath);
|
|
24
|
+
if (!exists) {
|
|
25
|
+
// Try to provide more helpful error information
|
|
26
|
+
const isAbsolute = path.isAbsolute(filePath);
|
|
27
|
+
const resolvedPath = isAbsolute
|
|
28
|
+
? filePath
|
|
29
|
+
: path.resolve(process.cwd(), filePath);
|
|
30
|
+
throw new Error(
|
|
31
|
+
`File not found: ${filePath}\n` +
|
|
32
|
+
`Resolved path: ${resolvedPath}\n` +
|
|
33
|
+
`Is absolute: ${isAbsolute}\n` +
|
|
34
|
+
`CWD: ${process.cwd()}`
|
|
35
|
+
);
|
|
23
36
|
}
|
|
37
|
+
|
|
24
38
|
const imageBytes = await storage.readFile(filePath);
|
|
25
39
|
return {
|
|
26
40
|
inlineData: {
|
|
@@ -153,8 +167,19 @@ async function processVideoInput(
|
|
|
153
167
|
} else {
|
|
154
168
|
// Local file processing - use File Upload API
|
|
155
169
|
const storage = getStorage();
|
|
156
|
-
|
|
157
|
-
|
|
170
|
+
|
|
171
|
+
// Check if file exists
|
|
172
|
+
const exists = await storage.exists(input);
|
|
173
|
+
if (!exists) {
|
|
174
|
+
// Try to provide more helpful error information
|
|
175
|
+
const isAbsolute = path.isAbsolute(input);
|
|
176
|
+
const resolvedPath = isAbsolute ? input : path.resolve(process.cwd(), input);
|
|
177
|
+
throw new Error(
|
|
178
|
+
`Video file not found: ${input}\n` +
|
|
179
|
+
`Resolved path: ${resolvedPath}\n` +
|
|
180
|
+
`Is absolute: ${isAbsolute}\n` +
|
|
181
|
+
`CWD: ${process.cwd()}`
|
|
182
|
+
);
|
|
158
183
|
}
|
|
159
184
|
|
|
160
185
|
// Upload file to Gemini API
|
|
@@ -220,16 +245,13 @@ export const geminiTextToImage = {
|
|
|
220
245
|
const imageData = part.inlineData.data;
|
|
221
246
|
if (args.output_path) {
|
|
222
247
|
const storage = getStorage();
|
|
223
|
-
const timestampedPath = generateTimestampedFilename(
|
|
224
|
-
args.output_path
|
|
225
|
-
);
|
|
226
248
|
const url = await storage.writeFile(
|
|
227
|
-
|
|
249
|
+
args.output_path,
|
|
228
250
|
Buffer.from(imageData, "base64")
|
|
229
251
|
);
|
|
230
252
|
images.push({
|
|
231
253
|
url,
|
|
232
|
-
filename:
|
|
254
|
+
filename: args.output_path,
|
|
233
255
|
mimeType: "image/png",
|
|
234
256
|
});
|
|
235
257
|
}
|
|
@@ -301,16 +323,13 @@ export const geminiEditImage = {
|
|
|
301
323
|
const imageData = part.inlineData.data;
|
|
302
324
|
if (args.output_path) {
|
|
303
325
|
const storage = getStorage();
|
|
304
|
-
const timestampedPath = generateTimestampedFilename(
|
|
305
|
-
args.output_path
|
|
306
|
-
);
|
|
307
326
|
const url = await storage.writeFile(
|
|
308
|
-
|
|
327
|
+
args.output_path,
|
|
309
328
|
Buffer.from(imageData, "base64")
|
|
310
329
|
);
|
|
311
330
|
images.push({
|
|
312
331
|
url,
|
|
313
|
-
filename:
|
|
332
|
+
filename: args.output_path,
|
|
314
333
|
mimeType: "image/png",
|
|
315
334
|
});
|
|
316
335
|
}
|
|
@@ -445,12 +464,11 @@ export const geminiSingleSpeakerTts = {
|
|
|
445
464
|
|
|
446
465
|
const audioBuffer = Buffer.from(data, "base64");
|
|
447
466
|
|
|
448
|
-
//
|
|
449
|
-
const outputPath = args.output_path || "voice_output.wav";
|
|
450
|
-
const timestampedPath = generateTimestampedFilename(outputPath);
|
|
467
|
+
// Use provided output path or generate default with timestamp
|
|
468
|
+
const outputPath = args.output_path || generateTimestampedFilename("voice_output.wav");
|
|
451
469
|
|
|
452
470
|
const storage = getStorage();
|
|
453
|
-
const url = await storage.writeFile(
|
|
471
|
+
const url = await storage.writeFile(outputPath, audioBuffer);
|
|
454
472
|
|
|
455
473
|
return JSON.stringify({
|
|
456
474
|
audio: {
|
|
@@ -54,9 +54,23 @@ async function fileToBase64(
|
|
|
54
54
|
filePath: string
|
|
55
55
|
): Promise<{ data: string; mimeType: string }> {
|
|
56
56
|
const storage = getStorage();
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
|
|
58
|
+
// Check if file exists
|
|
59
|
+
const exists = await storage.exists(filePath);
|
|
60
|
+
if (!exists) {
|
|
61
|
+
// Try to provide more helpful error information
|
|
62
|
+
const isAbsolute = path.isAbsolute(filePath);
|
|
63
|
+
const resolvedPath = isAbsolute
|
|
64
|
+
? filePath
|
|
65
|
+
: path.resolve(process.cwd(), filePath);
|
|
66
|
+
throw new Error(
|
|
67
|
+
`File not found: ${filePath}\n` +
|
|
68
|
+
`Resolved path: ${resolvedPath}\n` +
|
|
69
|
+
`Is absolute: ${isAbsolute}\n` +
|
|
70
|
+
`CWD: ${process.cwd()}`
|
|
71
|
+
);
|
|
59
72
|
}
|
|
73
|
+
|
|
60
74
|
const buf = await storage.readFile(filePath);
|
|
61
75
|
const data = Buffer.from(buf).toString("base64");
|
|
62
76
|
// Default to PNG if not sure, similar to existing code
|
|
@@ -288,13 +302,19 @@ export const imageToVideo = {
|
|
|
288
302
|
[];
|
|
289
303
|
const saveVideo = async (base64: string, index: number) => {
|
|
290
304
|
if (!base64) return;
|
|
291
|
-
|
|
292
|
-
|
|
305
|
+
|
|
306
|
+
// Use provided output path or generate default with timestamp
|
|
307
|
+
let filePath: string;
|
|
308
|
+
if (args.output_path) {
|
|
309
|
+
// User provided path - use as-is for first video, add index for subsequent
|
|
310
|
+
filePath = index === 0
|
|
293
311
|
? args.output_path
|
|
294
|
-
: args.output_path.replace(/\.mp4$/i, `_${index}.mp4`)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
312
|
+
: args.output_path.replace(/\.mp4$/i, `_${index}.mp4`);
|
|
313
|
+
} else {
|
|
314
|
+
// No path provided - generate timestamped default
|
|
315
|
+
const defaultName = `video_output${index > 0 ? `_${index}` : ""}.mp4`;
|
|
316
|
+
filePath = generateTimestampedFilename(defaultName);
|
|
317
|
+
}
|
|
298
318
|
|
|
299
319
|
const buf = Buffer.from(base64, "base64");
|
|
300
320
|
const storage = getStorage();
|
package/src/utils/filename.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Generate a timestamped filename to avoid conflicts
|
|
3
5
|
* Format: YYYYMMDD_HHmmss_filename.ext
|
|
6
|
+
* Preserves directory structure if present in the input path
|
|
4
7
|
*/
|
|
5
|
-
export function generateTimestampedFilename(
|
|
8
|
+
export function generateTimestampedFilename(filePath: string): string {
|
|
6
9
|
const now = new Date();
|
|
7
10
|
const timestamp = now
|
|
8
11
|
.toISOString()
|
|
@@ -10,13 +13,26 @@ export function generateTimestampedFilename(basename: string): string {
|
|
|
10
13
|
.replace(/\.\d{3}Z$/, "")
|
|
11
14
|
.replace("T", "_");
|
|
12
15
|
|
|
16
|
+
// Split into directory and filename
|
|
17
|
+
const dir = path.dirname(filePath);
|
|
18
|
+
const basename = path.basename(filePath);
|
|
19
|
+
|
|
13
20
|
// Extract extension if present
|
|
14
21
|
const lastDot = basename.lastIndexOf(".");
|
|
22
|
+
let timestampedFilename: string;
|
|
23
|
+
|
|
15
24
|
if (lastDot > 0) {
|
|
16
25
|
const name = basename.substring(0, lastDot);
|
|
17
26
|
const ext = basename.substring(lastDot);
|
|
18
|
-
|
|
27
|
+
timestampedFilename = `${timestamp}_${name}${ext}`;
|
|
28
|
+
} else {
|
|
29
|
+
timestampedFilename = `${timestamp}_${basename}`;
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
// Reconstruct the full path
|
|
33
|
+
if (dir === ".") {
|
|
34
|
+
return timestampedFilename;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return path.join(dir, timestampedFilename);
|
|
22
38
|
}
|