@mixio-pro/kalaasetu-mcp 1.0.11 → 1.0.13

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mixio-pro/kalaasetu-mcp",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "A powerful Model Context Protocol server providing AI tools for content generation and analysis",
5
5
  "type": "module",
6
6
  "module": "src/index.ts",
@@ -245,16 +245,13 @@ export const geminiTextToImage = {
245
245
  const imageData = part.inlineData.data;
246
246
  if (args.output_path) {
247
247
  const storage = getStorage();
248
- const timestampedPath = generateTimestampedFilename(
249
- args.output_path
250
- );
251
248
  const url = await storage.writeFile(
252
- timestampedPath,
249
+ args.output_path,
253
250
  Buffer.from(imageData, "base64")
254
251
  );
255
252
  images.push({
256
253
  url,
257
- filename: timestampedPath,
254
+ filename: args.output_path,
258
255
  mimeType: "image/png",
259
256
  });
260
257
  }
@@ -326,16 +323,13 @@ export const geminiEditImage = {
326
323
  const imageData = part.inlineData.data;
327
324
  if (args.output_path) {
328
325
  const storage = getStorage();
329
- const timestampedPath = generateTimestampedFilename(
330
- args.output_path
331
- );
332
326
  const url = await storage.writeFile(
333
- timestampedPath,
327
+ args.output_path,
334
328
  Buffer.from(imageData, "base64")
335
329
  );
336
330
  images.push({
337
331
  url,
338
- filename: timestampedPath,
332
+ filename: args.output_path,
339
333
  mimeType: "image/png",
340
334
  });
341
335
  }
@@ -470,12 +464,11 @@ export const geminiSingleSpeakerTts = {
470
464
 
471
465
  const audioBuffer = Buffer.from(data, "base64");
472
466
 
473
- // Generate output filename if not provided
474
- const outputPath = args.output_path || "voice_output.wav";
475
- const timestampedPath = generateTimestampedFilename(outputPath);
467
+ // Use provided output path or generate default with timestamp
468
+ const outputPath = args.output_path || generateTimestampedFilename("voice_output.wav");
476
469
 
477
470
  const storage = getStorage();
478
- const url = await storage.writeFile(timestampedPath, audioBuffer);
471
+ const url = await storage.writeFile(outputPath, audioBuffer);
479
472
 
480
473
  return JSON.stringify({
481
474
  audio: {
@@ -1,4 +1,3 @@
1
- import * as fs from "fs";
2
1
  import { GoogleAuth } from "google-auth-library";
3
2
  import { exec } from "child_process";
4
3
  import * as path from "path";
@@ -99,7 +98,9 @@ export const imageToVideo = {
99
98
  duration_seconds: z
100
99
  .string()
101
100
  .optional()
102
- .describe("Video duration in seconds: '4', '6', or '8' (default: '6')"),
101
+ .describe(
102
+ "Video duration in seconds. MUST be one of: '4', '6', or '8' (default: '6'). Other values will be rejected by Vertex AI."
103
+ ),
103
104
  resolution: z
104
105
  .string()
105
106
  .optional()
@@ -164,6 +165,32 @@ export const imageToVideo = {
164
165
  const location = args.location_id || "us-central1";
165
166
  const modelId = args.model_id || "veo-3.1-fast-generate-preview";
166
167
 
168
+ // Validate and parse duration_seconds - snap to nearest 4, 6, or 8
169
+ let durationSeconds = parseInt(args.duration_seconds || "6");
170
+ if (isNaN(durationSeconds)) durationSeconds = 6;
171
+
172
+ const validDurations = [4, 6, 8];
173
+ // Find nearest valid duration
174
+ durationSeconds = validDurations.reduce((prev, curr) => {
175
+ return Math.abs(curr - durationSeconds) < Math.abs(prev - durationSeconds)
176
+ ? curr
177
+ : prev;
178
+ });
179
+
180
+ // Tie-breaking: if equidistant (e.g. 5), the reduce above keeps the first one (4) because < is strict.
181
+ // However, user requested "nearest duration with the ceil", effectively meaning round up if equidistant.
182
+ // Let's explicitly handle the equidistant cases or just use a custom finder.
183
+ // 5 -> equidistant to 4 and 6. "With ceil" implies 6.
184
+ // 7 -> equidistant to 6 and 8. "With ceil" implies 8.
185
+
186
+ // Simpler logic for these specific values:
187
+ if (durationSeconds === 4 && parseInt(args.duration_seconds || "6") === 5) {
188
+ durationSeconds = 6;
189
+ }
190
+ if (durationSeconds === 6 && parseInt(args.duration_seconds || "6") === 7) {
191
+ durationSeconds = 8;
192
+ }
193
+
167
194
  const token = await fetchAccessToken();
168
195
 
169
196
  const url = `https://${location}-aiplatform.googleapis.com/v1/projects/${projectId}/locations/${location}/publishers/google/models/${modelId}:predictLongRunning`;
@@ -242,7 +269,7 @@ export const imageToVideo = {
242
269
 
243
270
  const parameters: any = {
244
271
  aspectRatio: args.aspect_ratio || "9:16",
245
- durationSeconds: parseInt(args.duration_seconds || "6") || 6,
272
+ durationSeconds: durationSeconds,
246
273
  resolution: args.resolution || "720p",
247
274
  negativePrompt: args.negative_prompt,
248
275
  generateAudio: args.generate_audio || false,
@@ -302,13 +329,20 @@ export const imageToVideo = {
302
329
  [];
303
330
  const saveVideo = async (base64: string, index: number) => {
304
331
  if (!base64) return;
305
- const baseFilename = args.output_path
306
- ? index === 0
307
- ? args.output_path
308
- : args.output_path.replace(/\.mp4$/i, `_${index}.mp4`)
309
- : `video_output${index > 0 ? `_${index}` : ""}.mp4`;
310
332
 
311
- const filePath = generateTimestampedFilename(baseFilename);
333
+ // Use provided output path or generate default with timestamp
334
+ let filePath: string;
335
+ if (args.output_path) {
336
+ // User provided path - use as-is for first video, add index for subsequent
337
+ filePath =
338
+ index === 0
339
+ ? args.output_path
340
+ : args.output_path.replace(/\.mp4$/i, `_${index}.mp4`);
341
+ } else {
342
+ // No path provided - generate timestamped default
343
+ const defaultName = `video_output${index > 0 ? `_${index}` : ""}.mp4`;
344
+ filePath = generateTimestampedFilename(defaultName);
345
+ }
312
346
 
313
347
  const buf = Buffer.from(base64, "base64");
314
348
  const storage = getStorage();
@@ -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(basename: string): string {
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
- return `${timestamp}_${name}${ext}`;
27
+ timestampedFilename = `${timestamp}_${name}${ext}`;
28
+ } else {
29
+ timestampedFilename = `${timestamp}_${basename}`;
19
30
  }
20
31
 
21
- return `${timestamp}_${basename}`;
32
+ // Reconstruct the full path
33
+ if (dir === ".") {
34
+ return timestampedFilename;
35
+ }
36
+
37
+ return path.join(dir, timestampedFilename);
22
38
  }