@agimon-ai/video-editor-mcp 0.7.0 → 0.8.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.
Files changed (39) hide show
  1. package/README.md +117 -5
  2. package/dist/cli.cjs +9 -4
  3. package/dist/cli.mjs +9 -4
  4. package/dist/index.cjs +1 -2
  5. package/dist/index.mjs +1 -2
  6. package/dist/stdio-CH0Bk0m7.cjs +17 -0
  7. package/dist/stdio-KMqWPqUm.mjs +17 -0
  8. package/package.json +12 -4
  9. package/schemas/main-composition.schema.json +2283 -0
  10. package/src/remotion/Root.tsx +87 -0
  11. package/src/remotion/compositions/Main.tsx +51 -0
  12. package/src/remotion/compositions/Square.tsx +18 -0
  13. package/src/remotion/compositions/Vertical.tsx +18 -0
  14. package/src/remotion/compositions/audio/AudioLayer.tsx +130 -0
  15. package/src/remotion/compositions/captions/CaptionOverlay.tsx +250 -0
  16. package/src/remotion/compositions/captions/index.ts +1 -0
  17. package/src/remotion/compositions/clips/AudioClipRenderer.tsx +66 -0
  18. package/src/remotion/compositions/clips/GifClipRenderer.tsx +71 -0
  19. package/src/remotion/compositions/clips/ImageClipRenderer.tsx +103 -0
  20. package/src/remotion/compositions/clips/LottieClipRenderer.tsx +57 -0
  21. package/src/remotion/compositions/clips/SubtitleClipRenderer.tsx +85 -0
  22. package/src/remotion/compositions/clips/TextClipRenderer.tsx +131 -0
  23. package/src/remotion/compositions/clips/VideoClipRenderer.tsx +94 -0
  24. package/src/remotion/compositions/clips/index.tsx +51 -0
  25. package/src/remotion/index.ts +10 -0
  26. package/src/remotion/utils/calculateMainMetadata.ts +76 -0
  27. package/src/schemas/clips.ts +202 -0
  28. package/src/schemas/compositions.ts +160 -0
  29. package/src/schemas/index.ts +19 -0
  30. package/src/utils/assetPaths.ts +64 -0
  31. package/src/utils/index.ts +9 -0
  32. package/dist/cli.cjs.map +0 -1
  33. package/dist/cli.mjs.map +0 -1
  34. package/dist/index.cjs.map +0 -1
  35. package/dist/index.mjs.map +0 -1
  36. package/dist/stdio-B6Hg5voC.mjs +0 -4
  37. package/dist/stdio-B6Hg5voC.mjs.map +0 -1
  38. package/dist/stdio-Dwc6SWvA.cjs +0 -4
  39. package/dist/stdio-Dwc6SWvA.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"stdio-B6Hg5voC.mjs","names":["execFileAsync"],"sources":["../src/services/CaptionService.ts","../src/services/FontService.ts","../src/services/MediaInfoService.ts","../src/services/RenderService.ts","../src/services/VoiceoverService.ts","../src/tools/BaseTool.ts","../src/types/index.ts","../src/tools/GenerateCaptionsTool.ts","../src/tools/GenerateVoiceoverTool.ts","../src/tools/GetMediaInfoTool.ts","../src/tools/ImportSrtTool.ts","../src/tools/ListCompositionsTool.ts","../src/tools/PreviewFrameTool.ts","../src/tools/RenderVideoTool.ts","../src/container/index.ts","../src/server/index.ts","../src/transports/stdio.ts"],"sourcesContent":["/**\n * CaptionService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Dependency injection via InversifyJS\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n */\n\nimport { injectable } from 'inversify';\nimport type { Caption } from '../types/index.js';\n\n/**\n * TikTok-style caption page with grouped tokens\n */\nexport interface CaptionPage {\n startMs: number;\n endMs: number;\n tokens: Caption[];\n}\n\n@injectable()\nexport class CaptionService {\n /**\n * Parse SRT format content into Caption array\n *\n * SRT format:\n * 1\n * 00:00:01,000 --> 00:00:04,000\n * Welcome to the video\n *\n * 2\n * 00:00:04,500 --> 00:00:08,000\n * This is the second caption\n */\n parseSrt(srtContent: string): Caption[] {\n const captions: Caption[] = [];\n const lines = srtContent.split('\\n');\n let i = 0;\n\n while (i < lines.length) {\n // Skip empty lines\n if (!lines[i]?.trim()) {\n i++;\n continue;\n }\n\n // Skip sequence number\n i++;\n\n // Parse timestamp line\n const timestampLine = lines[i];\n if (!timestampLine?.includes('-->')) {\n i++;\n continue;\n }\n\n const [startStr, endStr] = timestampLine.split('-->').map((s) => s.trim());\n if (!startStr || !endStr) {\n i++;\n continue;\n }\n\n const startMs = this.parseTimestamp(startStr);\n const endMs = this.parseTimestamp(endStr);\n\n // Collect text lines until empty line or end\n i++;\n const textLines: string[] = [];\n while (i < lines.length && lines[i]?.trim()) {\n textLines.push(lines[i] ?? '');\n i++;\n }\n\n const text = textLines.join('\\n');\n if (text) {\n captions.push({ text, startMs, endMs });\n }\n }\n\n return captions;\n }\n\n /**\n * Parse SRT timestamp to milliseconds\n * Format: HH:MM:SS,mmm\n */\n private parseTimestamp(timestamp: string): number {\n const [time, ms] = timestamp.split(',');\n if (!time || !ms) {\n throw new Error(`Invalid timestamp format: ${timestamp}`);\n }\n\n const [hours, minutes, seconds] = time.split(':').map(Number);\n if (hours === undefined || minutes === undefined || seconds === undefined) {\n throw new Error(`Invalid time format: ${time}`);\n }\n\n const totalSeconds = hours * 3600 + minutes * 60 + seconds;\n return totalSeconds * 1000 + Number(ms);\n }\n\n /**\n * Create TikTok-style caption pages by grouping captions\n * Combines tokens that appear within combineMs milliseconds of each other\n */\n createTikTokCaptions(captions: Caption[], combineMs: number): { pages: CaptionPage[] } {\n const pages: CaptionPage[] = [];\n\n if (captions.length === 0) {\n return { pages };\n }\n\n let currentPage: CaptionPage = {\n startMs: captions[0]?.startMs ?? 0,\n endMs: captions[0]?.endMs ?? 0,\n tokens: [captions[0] as Caption],\n };\n\n for (let i = 1; i < captions.length; i++) {\n const caption = captions[i];\n if (!caption) continue;\n\n const gap = caption.startMs - currentPage.endMs;\n\n if (gap <= combineMs) {\n // Combine into current page\n currentPage.tokens.push(caption);\n currentPage.endMs = caption.endMs;\n } else {\n // Start new page\n pages.push(currentPage);\n currentPage = {\n startMs: caption.startMs,\n endMs: caption.endMs,\n tokens: [caption],\n };\n }\n }\n\n // Add final page\n if (currentPage.tokens.length > 0) {\n pages.push(currentPage);\n }\n\n return { pages };\n }\n}\n","/**\n * FontService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Dependency injection via InversifyJS\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n */\n\nimport { injectable } from 'inversify';\n\n/**\n * Curated list of popular Google Fonts\n * These fonts are widely used and well-supported\n */\nconst AVAILABLE_GOOGLE_FONTS = [\n 'Inter',\n 'Roboto',\n 'Open Sans',\n 'Montserrat',\n 'Lato',\n 'Poppins',\n 'Oswald',\n 'Raleway',\n 'Playfair Display',\n 'Merriweather',\n 'Source Sans Pro',\n 'PT Sans',\n 'Ubuntu',\n 'Nunito',\n 'Rubik',\n 'Work Sans',\n 'Karla',\n 'Noto Sans',\n 'Fira Sans',\n 'DM Sans',\n] as const;\n\n@injectable()\nexport class FontService {\n /**\n * Get list of available Google Fonts\n */\n getAvailableGoogleFonts(): string[] {\n return [...AVAILABLE_GOOGLE_FONTS];\n }\n\n /**\n * Validate if a font family is in the available list\n * Case-insensitive comparison\n */\n validateFontFamily(family: string): boolean {\n const normalizedFamily = family.toLowerCase();\n return AVAILABLE_GOOGLE_FONTS.some((font) => font.toLowerCase() === normalizedFamily);\n }\n}\n","/**\n * MediaInfoService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Dependency injection via InversifyJS\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n */\n\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { injectable } from 'inversify';\n\nconst execFileAsync = promisify(execFile);\n\n@injectable()\nexport class MediaInfoService {\n /**\n * Get video duration in seconds using ffprobe\n */\n async getVideoDuration(src: string): Promise<number> {\n try {\n const { stdout } = await execFileAsync('ffprobe', [\n '-v',\n 'error',\n '-show_entries',\n 'format=duration',\n '-of',\n 'json',\n src,\n ]);\n\n const result = JSON.parse(stdout);\n const duration = Number.parseFloat(result.format?.duration ?? '0');\n\n if (Number.isNaN(duration) || duration <= 0) {\n throw new Error(`Invalid duration for video: ${src}`);\n }\n\n return duration;\n } catch (error) {\n throw new Error(\n `Failed to get video duration for ${src}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n }\n\n /**\n * Get audio duration in seconds using ffprobe\n */\n async getAudioDuration(src: string): Promise<number> {\n try {\n const { stdout } = await execFileAsync('ffprobe', [\n '-v',\n 'error',\n '-show_entries',\n 'format=duration',\n '-of',\n 'json',\n src,\n ]);\n\n const result = JSON.parse(stdout);\n const duration = Number.parseFloat(result.format?.duration ?? '0');\n\n if (Number.isNaN(duration) || duration <= 0) {\n throw new Error(`Invalid duration for audio: ${src}`);\n }\n\n return duration;\n } catch (error) {\n throw new Error(\n `Failed to get audio duration for ${src}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n }\n\n /**\n * Get video dimensions (width and height) using ffprobe\n */\n async getVideoDimensions(src: string): Promise<{ width: number; height: number }> {\n try {\n const { stdout } = await execFileAsync('ffprobe', [\n '-v',\n 'error',\n '-select_streams',\n 'v:0',\n '-show_entries',\n 'stream=width,height',\n '-of',\n 'json',\n src,\n ]);\n\n const result = JSON.parse(stdout);\n const stream = result.streams?.[0];\n\n if (!stream?.width || !stream?.height) {\n throw new Error(`No video stream found in: ${src}`);\n }\n\n return {\n width: Number(stream.width),\n height: Number(stream.height),\n };\n } catch (error) {\n throw new Error(\n `Failed to get video dimensions for ${src}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n /**\n * Check if a media file can be decoded using ffprobe\n */\n async canDecode(src: string): Promise<boolean> {\n try {\n await execFileAsync('ffprobe', ['-v', 'error', '-show_entries', 'format=duration', '-of', 'json', src]);\n return true;\n } catch {\n return false;\n }\n }\n}\n","/**\n * RenderService\n *\n * Uses @remotion/renderer to render videos from JSON props.\n */\n\nimport path from 'node:path';\nimport { bundle } from '@remotion/bundler';\nimport { renderMedia, renderStill, selectComposition } from '@remotion/renderer';\nimport { injectable } from 'inversify';\nimport type { RenderOptions, RenderResult, RenderStillOptions } from '../types/index.js';\n\n@injectable()\nexport class RenderService {\n private bundlePromise: Promise<string> | null = null;\n\n private async getServeUrl(): Promise<string> {\n if (!this.bundlePromise) {\n const projectRoot = path.resolve(import.meta.dirname, '../..');\n this.bundlePromise = bundle({\n entryPoint: path.resolve(projectRoot, 'src/remotion/index.ts'),\n publicDir: path.resolve(projectRoot, 'public'),\n webpackOverride: (config) => ({\n ...config,\n resolve: {\n ...config.resolve,\n extensionAlias: {\n '.js': ['.ts', '.tsx', '.js', '.jsx'],\n },\n },\n }),\n });\n }\n return this.bundlePromise;\n }\n\n async render(options: RenderOptions): Promise<RenderResult> {\n const serveUrl = await this.getServeUrl();\n\n const composition = await selectComposition({\n serveUrl,\n id: options.compositionId,\n inputProps: options.inputProps,\n });\n\n await renderMedia({\n composition,\n serveUrl,\n codec: options.codec ?? 'h264',\n outputLocation: options.outputPath,\n inputProps: options.inputProps,\n });\n\n return { outputPath: options.outputPath };\n }\n\n async renderStill(options: RenderStillOptions): Promise<RenderResult> {\n const serveUrl = await this.getServeUrl();\n\n const composition = await selectComposition({\n serveUrl,\n id: options.compositionId,\n inputProps: options.inputProps,\n });\n\n await renderStill({\n composition,\n serveUrl,\n output: options.outputPath,\n frame: options.frame ?? 0,\n imageFormat: options.imageFormat ?? 'png',\n inputProps: options.inputProps,\n });\n\n return { outputPath: options.outputPath };\n }\n}\n","/**\n * VoiceoverService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Dependency injection via InversifyJS\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n * - Use execFile for command execution (prevents command injection)\n */\n\nimport { execFile } from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\nimport { injectable } from 'inversify';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Available Kokoro TTS voices\n */\nconst AVAILABLE_VOICES = [\n 'af_sarah',\n 'af_nicole',\n 'af_bella',\n 'af_sky',\n 'am_adam',\n 'am_michael',\n 'bf_emma',\n 'bf_isabella',\n 'bm_george',\n 'bm_lewis',\n] as const;\n\nexport interface VoiceoverOptions {\n text: string;\n voice?: string;\n speed?: number;\n outputPath: string;\n}\n\nexport interface VoiceoverResult {\n outputPath: string;\n duration?: number;\n}\n\n@injectable()\nexport class VoiceoverService {\n /**\n * Check if kokoro-tts CLI is installed\n */\n async isAvailable(): Promise<boolean> {\n try {\n await execFileAsync('kokoro-tts', ['--version']);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Generate voiceover using Kokoro TTS\n */\n async generateVoiceover(options: VoiceoverOptions): Promise<VoiceoverResult> {\n const { text, voice = 'af_sarah', speed = 1.0, outputPath } = options;\n\n // Create temp file for text input\n const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'kokoro-'));\n const tempTextFile = path.join(tempDir, 'input.txt');\n\n try {\n // Write text to temp file\n await fs.writeFile(tempTextFile, text, 'utf-8');\n\n // Run kokoro-tts CLI\n const args = [tempTextFile, outputPath, '--voice', voice, '--speed', speed.toString()];\n\n await execFileAsync('kokoro-tts', args);\n\n return { outputPath };\n } catch (error) {\n throw new Error(`Failed to generate voiceover: ${error instanceof Error ? error.message : String(error)}`, {\n cause: error,\n });\n } finally {\n // Cleanup temp file\n try {\n await fs.rm(tempDir, { recursive: true, force: true });\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n\n /**\n * Get list of available Kokoro voice IDs\n */\n listVoices(): string[] {\n return [...AVAILABLE_VOICES];\n }\n}\n","/**\n * BaseTool - Abstract base class for video editor MCP tools\n *\n * DESIGN PATTERNS:\n * - Template Method pattern for consistent tool interface\n * - Dependency Injection via InversifyJS for services\n * - Helper methods for response formatting\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Return CallToolResult with content array\n * - Handle errors gracefully with isError flag\n *\n * AVOID:\n * - Business logic in base class (delegate to services)\n * - Exposing internal errors to users\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { injectable } from 'inversify';\n\n@injectable()\nexport abstract class BaseTool {\n /**\n * Creates a success response with text content.\n * @param text - The success message or content.\n * @returns A CallToolResult with the content.\n */\n protected success(text: string): CallToolResult {\n return {\n content: [{ type: 'text', text }],\n };\n }\n\n /**\n * Creates a success response with JSON content.\n * @param data - The data to serialize as JSON.\n * @returns A CallToolResult with the JSON content.\n */\n protected successJson(data: unknown): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],\n };\n }\n\n /**\n * Creates an error response.\n * @param message - The error message.\n * @returns A CallToolResult with isError flag set.\n */\n protected error(message: string): CallToolResult {\n return {\n content: [{ type: 'text', text: `Error: ${message}` }],\n isError: true,\n };\n }\n\n /**\n * Wraps execution with standard error handling.\n * @param fn - The async function to execute.\n * @returns The result of the function or an error response.\n */\n protected async safeExecute<T>(fn: () => Promise<T>): Promise<T | CallToolResult> {\n try {\n return await fn();\n } catch (err) {\n return this.error(err instanceof Error ? err.message : 'Unknown error');\n }\n }\n}\n","/**\n * Shared TypeScript Types\n *\n * DESIGN PATTERNS:\n * - Type-first development with Zod schema inference\n * - Interface segregation\n *\n * CODING STANDARDS:\n * - Export all shared types from this file\n * - Derive types from Zod schemas via z.infer<>\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { z } from 'zod';\nimport type {\n AudioClipSchema,\n ClipSchema,\n GifClipSchema,\n ImageClipSchema,\n LottieClipSchema,\n SubtitleClipSchema,\n TextClipSchema,\n VideoClipSchema,\n} from '../schemas/clips.js';\nimport type {\n CaptionConfigSchema,\n CaptionSchema,\n FontConfigSchema,\n MainCompositionSchema,\n} from '../schemas/compositions.js';\n\n/**\n * DI Container Symbols\n */\nexport const TYPES = {\n RenderService: Symbol.for('RenderService'),\n MediaInfoService: Symbol.for('MediaInfoService'),\n CaptionService: Symbol.for('CaptionService'),\n FontService: Symbol.for('FontService'),\n VoiceoverService: Symbol.for('VoiceoverService'),\n Tool: Symbol.for('Tool'),\n} as const;\n\n/**\n * Tool definition for MCP\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\n/**\n * Base tool interface following MCP SDK patterns\n */\nexport interface Tool<TInput = unknown> {\n getDefinition(): ToolDefinition;\n getInputSchema(): z.ZodObject<z.ZodRawShape>;\n execute(input: TInput): Promise<CallToolResult>;\n}\n\n/**\n * Render options for Remotion\n */\nexport interface RenderOptions {\n compositionId: string;\n inputProps: Record<string, unknown>;\n outputPath: string;\n codec?: 'h264' | 'h265' | 'vp8' | 'vp9';\n}\n\n/**\n * Render still options\n */\nexport interface RenderStillOptions {\n compositionId: string;\n inputProps: Record<string, unknown>;\n outputPath: string;\n frame?: number;\n imageFormat?: 'png' | 'jpeg';\n}\n\n/**\n * Render result from Remotion\n */\nexport interface RenderResult {\n outputPath: string;\n}\n\n// Clip types derived from Zod schemas\nexport type VideoClip = z.infer<typeof VideoClipSchema>;\nexport type ImageClip = z.infer<typeof ImageClipSchema>;\nexport type TextClip = z.infer<typeof TextClipSchema>;\nexport type AudioClip = z.infer<typeof AudioClipSchema>;\nexport type SubtitleClip = z.infer<typeof SubtitleClipSchema>;\nexport type GifClip = z.infer<typeof GifClipSchema>;\nexport type LottieClip = z.infer<typeof LottieClipSchema>;\nexport type Clip = z.infer<typeof ClipSchema>;\n\n// Composition types derived from Zod schemas\nexport type Caption = z.infer<typeof CaptionSchema>;\nexport type CaptionConfig = z.infer<typeof CaptionConfigSchema>;\nexport type FontConfig = z.infer<typeof FontConfigSchema>;\nexport type MainCompositionProps = z.infer<typeof MainCompositionSchema>;\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { CaptionService } from '../services/CaptionService.js';\nimport type { Caption, Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const GenerateCaptionsToolInputSchema = z.object({\n captions: z\n .array(\n z.object({\n text: z.string(),\n startMs: z.number(),\n endMs: z.number(),\n }),\n )\n .describe('Array of caption objects with text, startMs, and endMs'),\n combineMs: z.number().optional().describe('Milliseconds threshold for combining captions into pages (default: 100)'),\n});\n\ntype GenerateCaptionsToolInput = z.infer<typeof GenerateCaptionsToolInputSchema>;\n\n@injectable()\nexport class GenerateCaptionsTool extends BaseTool implements Tool<GenerateCaptionsToolInput> {\n static readonly TOOL_NAME = 'generate_captions';\n\n constructor(@inject(TYPES.CaptionService) private captionService: CaptionService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return GenerateCaptionsToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: GenerateCaptionsTool.TOOL_NAME,\n description: 'Create TikTok-style caption pages from a caption array for video overlay',\n inputSchema: z.toJSONSchema(GenerateCaptionsToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = GenerateCaptionsToolInputSchema.parse(rawInput);\n const { captions, combineMs = 100 } = input;\n const captionArray: Caption[] = captions.map((c) => ({\n text: c.text,\n startMs: c.startMs,\n endMs: c.endMs,\n }));\n const result = this.captionService.createTikTokCaptions(captionArray, combineMs);\n return this.successJson(result);\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { VoiceoverService } from '../services/VoiceoverService.js';\nimport type { Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const GenerateVoiceoverToolInputSchema = z.object({\n text: z.string().describe('Text to convert to speech'),\n voice: z\n .string()\n .optional()\n .describe(\n 'Voice ID (default: af_sarah). Options: af_sarah, af_nicole, af_bella, af_sky, am_adam, am_michael, bf_emma, bf_isabella, bm_george, bm_lewis',\n ),\n speed: z.number().optional().describe('Speech speed multiplier (default: 1.0)'),\n outputPath: z.string().describe('Output file path for the generated audio'),\n});\n\ntype GenerateVoiceoverToolInput = z.infer<typeof GenerateVoiceoverToolInputSchema>;\n\n@injectable()\nexport class GenerateVoiceoverTool extends BaseTool implements Tool<GenerateVoiceoverToolInput> {\n static readonly TOOL_NAME = 'generate_voiceover';\n\n constructor(@inject(TYPES.VoiceoverService) private voiceoverService: VoiceoverService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return GenerateVoiceoverToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: GenerateVoiceoverTool.TOOL_NAME,\n description: 'Generate voiceover audio from text using Kokoro TTS local engine',\n inputSchema: z.toJSONSchema(GenerateVoiceoverToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = GenerateVoiceoverToolInputSchema.parse(rawInput);\n const isAvailable = await this.voiceoverService.isAvailable();\n if (!isAvailable) {\n return this.error('Kokoro TTS is not installed. Install it with: pip install kokoro-tts');\n }\n\n const result = await this.voiceoverService.generateVoiceover({\n text: input.text,\n voice: input.voice,\n speed: input.speed,\n outputPath: input.outputPath,\n });\n\n return this.successJson(result);\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { MediaInfoService } from '../services/MediaInfoService.js';\nimport type { Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const GetMediaInfoToolInputSchema = z.object({\n src: z.string().describe('Path to the media file'),\n type: z.enum(['video', 'audio']).optional().describe('Media type (default: video)'),\n});\n\ntype GetMediaInfoToolInput = z.infer<typeof GetMediaInfoToolInputSchema>;\n\n@injectable()\nexport class GetMediaInfoTool extends BaseTool implements Tool<GetMediaInfoToolInput> {\n static readonly TOOL_NAME = 'get_media_info';\n\n constructor(@inject(TYPES.MediaInfoService) private mediaInfoService: MediaInfoService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return GetMediaInfoToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: GetMediaInfoTool.TOOL_NAME,\n description: 'Get duration, dimensions, and decodability info for video/audio files',\n inputSchema: z.toJSONSchema(GetMediaInfoToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = GetMediaInfoToolInputSchema.parse(rawInput);\n const { src, type = 'video' } = input;\n const canDecode = await this.mediaInfoService.canDecode(src);\n\n if (type === 'audio') {\n const duration = await this.mediaInfoService.getAudioDuration(src);\n return this.successJson({ src, type: 'audio', duration, canDecode });\n }\n\n const [duration, dimensions] = await Promise.all([\n this.mediaInfoService.getVideoDuration(src),\n this.mediaInfoService.getVideoDimensions(src),\n ]);\n\n return this.successJson({\n src,\n type: 'video',\n duration,\n width: dimensions.width,\n height: dimensions.height,\n canDecode,\n });\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { CaptionService } from '../services/CaptionService.js';\nimport type { Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const ImportSrtToolInputSchema = z.object({\n srtContent: z.string().describe('SRT file content as a string'),\n});\n\ntype ImportSrtToolInput = z.infer<typeof ImportSrtToolInputSchema>;\n\n@injectable()\nexport class ImportSrtTool extends BaseTool implements Tool<ImportSrtToolInput> {\n static readonly TOOL_NAME = 'import_srt';\n\n constructor(@inject(TYPES.CaptionService) private captionService: CaptionService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return ImportSrtToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: ImportSrtTool.TOOL_NAME,\n description: 'Parse SRT subtitle file content into structured Caption format for video overlay',\n inputSchema: z.toJSONSchema(ImportSrtToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = ImportSrtToolInputSchema.parse(rawInput);\n const captions = this.captionService.parseSrt(input.srtContent);\n return this.successJson({ captionCount: captions.length, captions });\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { injectable } from 'inversify';\nimport { z } from 'zod';\nimport type { Tool, ToolDefinition } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const ListCompositionsToolInputSchema = z.object({});\n\ntype ListCompositionsToolInput = z.infer<typeof ListCompositionsToolInputSchema>;\n\n@injectable()\nexport class ListCompositionsTool extends BaseTool implements Tool<ListCompositionsToolInput> {\n static readonly TOOL_NAME = 'list_compositions';\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return ListCompositionsToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: ListCompositionsTool.TOOL_NAME,\n description: 'List all available Remotion compositions with their schemas, dimensions, and format info',\n inputSchema: z.toJSONSchema(ListCompositionsToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(): Promise<CallToolResult> {\n const compositions = [\n {\n id: 'Main',\n description: 'Main 16:9 landscape composition',\n width: 1920,\n height: 1080,\n fps: 30,\n durationInFrames: null,\n defaultProps: { clips: [], audioVolume: 1, backgroundColor: '#000000' },\n },\n {\n id: 'Vertical',\n description: 'Vertical 9:16 portrait composition (TikTok, Instagram Stories)',\n width: 1080,\n height: 1920,\n fps: 30,\n durationInFrames: null,\n defaultProps: { clips: [], audioVolume: 1, backgroundColor: '#000000' },\n },\n {\n id: 'Square',\n description: 'Square 1:1 composition (Instagram posts)',\n width: 1080,\n height: 1080,\n fps: 30,\n durationInFrames: null,\n defaultProps: { clips: [], audioVolume: 1, backgroundColor: '#000000' },\n },\n ];\n\n return this.successJson({ compositionCount: compositions.length, compositions });\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { RenderService } from '../services/RenderService.js';\nimport type { RenderStillOptions, Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const PreviewFrameToolInputSchema = z.object({\n compositionId: z.string().describe('Remotion composition ID (e.g., \"Main\", \"Vertical\", \"Square\")'),\n inputProps: z.record(z.string(), z.unknown()).describe('JSON props to pass to the composition'),\n outputPath: z.string().describe('Output file path for the rendered image'),\n frame: z.number().optional().describe('Frame number to render (default: 0)'),\n imageFormat: z.enum(['png', 'jpeg']).optional().describe('Image format (default: png)'),\n});\n\ntype PreviewFrameToolInput = z.infer<typeof PreviewFrameToolInputSchema>;\n\n@injectable()\nexport class PreviewFrameTool extends BaseTool implements Tool<PreviewFrameToolInput> {\n static readonly TOOL_NAME = 'preview_frame';\n\n constructor(@inject(TYPES.RenderService) private renderService: RenderService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return PreviewFrameToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: PreviewFrameTool.TOOL_NAME,\n description: 'Render a single frame from a composition as a PNG or JPEG image for preview',\n inputSchema: z.toJSONSchema(PreviewFrameToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = PreviewFrameToolInputSchema.parse(rawInput);\n const options: RenderStillOptions = {\n compositionId: input.compositionId,\n inputProps: input.inputProps,\n outputPath: input.outputPath,\n frame: input.frame,\n imageFormat: input.imageFormat,\n };\n const result = await this.renderService.renderStill(options);\n return this.successJson(result);\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { RenderService } from '../services/RenderService.js';\nimport type { RenderOptions, Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const RenderVideoToolInputSchema = z.object({\n compositionId: z.string().describe('Remotion composition ID (e.g., \"Main\", \"Vertical\", \"Square\")'),\n inputProps: z.record(z.string(), z.unknown()).describe('JSON props to pass to the composition'),\n outputPath: z.string().describe('Output file path for the rendered video'),\n codec: z.enum(['h264', 'h265', 'vp8', 'vp9']).optional().describe('Video codec (default: h264)'),\n});\n\ntype RenderVideoToolInput = z.infer<typeof RenderVideoToolInputSchema>;\n\n@injectable()\nexport class RenderVideoTool extends BaseTool implements Tool<RenderVideoToolInput> {\n static readonly TOOL_NAME = 'render_video';\n\n constructor(@inject(TYPES.RenderService) private renderService: RenderService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return RenderVideoToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: RenderVideoTool.TOOL_NAME,\n description: 'Render a video from JSON props using Remotion',\n inputSchema: z.toJSONSchema(RenderVideoToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = RenderVideoToolInputSchema.parse(rawInput);\n const options: RenderOptions = {\n compositionId: input.compositionId,\n inputProps: input.inputProps,\n outputPath: input.outputPath,\n codec: input.codec,\n };\n const result = await this.renderService.render(options);\n return this.successJson(result);\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","/**\n * DI Container Setup\n *\n * DESIGN PATTERNS:\n * - ContainerModule pattern for modular DI configuration\n * - Separation of concerns: services vs tools\n * - Singleton scope for stateful services\n *\n * CODING STANDARDS:\n * - Use ContainerModule for grouping related bindings\n * - Import reflect-metadata at top\n * - Bind services to their interface symbols from TYPES\n */\n\nimport 'reflect-metadata';\nimport { Container, ContainerModule, type ContainerModuleLoadOptions } from 'inversify';\nimport { CaptionService, FontService, MediaInfoService, RenderService, VoiceoverService } from '../services/index.js';\nimport {\n GenerateCaptionsTool,\n GenerateVoiceoverTool,\n GetMediaInfoTool,\n ImportSrtTool,\n ListCompositionsTool,\n PreviewFrameTool,\n RenderVideoTool,\n} from '../tools/index.js';\nimport { TYPES } from '../types/index.js';\n\n/**\n * Services module - binds all core services\n */\nexport const servicesModule = new ContainerModule((options: ContainerModuleLoadOptions) => {\n options.bind(TYPES.RenderService).to(RenderService).inSingletonScope();\n options.bind(TYPES.MediaInfoService).to(MediaInfoService).inSingletonScope();\n options.bind(TYPES.CaptionService).to(CaptionService).inSingletonScope();\n options.bind(TYPES.FontService).to(FontService).inSingletonScope();\n options.bind(TYPES.VoiceoverService).to(VoiceoverService).inSingletonScope();\n});\n\n/**\n * Tools module - binds MCP tools\n */\nexport const toolsModule = new ContainerModule((options: ContainerModuleLoadOptions) => {\n options.bind(TYPES.Tool).to(RenderVideoTool).inSingletonScope();\n options.bind(TYPES.Tool).to(ListCompositionsTool).inSingletonScope();\n options.bind(TYPES.Tool).to(GetMediaInfoTool).inSingletonScope();\n options.bind(TYPES.Tool).to(PreviewFrameTool).inSingletonScope();\n options.bind(TYPES.Tool).to(GenerateCaptionsTool).inSingletonScope();\n options.bind(TYPES.Tool).to(ImportSrtTool).inSingletonScope();\n options.bind(TYPES.Tool).to(GenerateVoiceoverTool).inSingletonScope();\n});\n\n/**\n * Creates and configures the DI container\n */\nexport function createContainer(): Container {\n const container = new Container();\n container.load(servicesModule, toolsModule);\n return container;\n}\n","import { coerceArgs, formatZodError } from '@agimon-ai/foundation-validator';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport type { Container } from 'inversify';\nimport { z } from 'zod';\nimport type { Tool } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\n\nexport function createServer(container: Container): Server {\n const server = new Server({ name: 'video-editor-mcp', version: '0.1.0' }, { capabilities: { tools: {} } });\n\n const tools = container.getAll<Tool>(TYPES.Tool);\n const toolMap = new Map<string, Tool>();\n for (const tool of tools) {\n toolMap.set(tool.getDefinition().name, tool);\n }\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: tools.map((t) => t.getDefinition()),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n const tool = toolMap.get(name);\n if (!tool) {\n return {\n content: [{ type: 'text' as const, text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n\n const coerced = coerceArgs(args ?? {}, tool.getInputSchema());\n\n try {\n return await tool.execute(coerced);\n } catch (error) {\n if (error instanceof z.ZodError) {\n return {\n content: [\n { type: 'text' as const, text: formatZodError(error, { schemaName: name, schema: tool.getInputSchema() }) },\n ],\n isError: true,\n };\n }\n throw error;\n }\n });\n\n return server;\n}\n","/**\n * STDIO Transport\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Standard I/O based communication for CLI usage\n *\n * CODING STANDARDS:\n * - Initialize server and transport properly\n * - Handle cleanup on shutdown with stop() method\n * - Use async/await for all operations\n *\n * AVOID:\n * - Forgetting to close transport on shutdown\n * - Missing error handling for connection failures\n */\n\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\n\n/**\n * Stdio transport handler for MCP server\n * Used for command-line and direct integrations\n */\nexport class StdioTransportHandler {\n private server: Server;\n private transport: StdioServerTransport | null = null;\n\n constructor(server: Server) {\n this.server = server;\n }\n\n async start(): Promise<void> {\n this.transport = new StdioServerTransport();\n await this.server.connect(this.transport);\n console.error('video-editor MCP server started on stdio');\n }\n\n async stop(): Promise<void> {\n if (this.transport) {\n await this.transport.close();\n this.transport = null;\n }\n }\n}\n"],"mappings":"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"}
@@ -1,4 +0,0 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));require(`reflect-metadata`);let c=require(`inversify`),l=require(`node:child_process`),u=require(`node:util`),d=require(`node:path`);d=s(d);let ee=require(`@remotion/bundler`),f=require(`@remotion/renderer`),p=require(`node:fs/promises`);p=s(p);let m=require(`node:os`);m=s(m);let h=require(`zod`),te=require(`@agimon-ai/foundation-validator`),ne=require(`@modelcontextprotocol/sdk/server/index.js`),re=require(`@modelcontextprotocol/sdk/types.js`),ie=require(`@modelcontextprotocol/sdk/server/stdio.js`);function g(e,t,n,r){var i=arguments.length,a=i<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,o;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)a=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}let _=class{parseSrt(e){let t=[],n=e.split(`
2
- `),r=0;for(;r<n.length;){if(!n[r]?.trim()){r++;continue}r++;let e=n[r];if(!e?.includes(`-->`)){r++;continue}let[i,a]=e.split(`-->`).map(e=>e.trim());if(!i||!a){r++;continue}let o=this.parseTimestamp(i),s=this.parseTimestamp(a);r++;let c=[];for(;r<n.length&&n[r]?.trim();)c.push(n[r]??``),r++;let l=c.join(`
3
- `);l&&t.push({text:l,startMs:o,endMs:s})}return t}parseTimestamp(e){let[t,n]=e.split(`,`);if(!t||!n)throw Error(`Invalid timestamp format: ${e}`);let[r,i,a]=t.split(`:`).map(Number);if(r===void 0||i===void 0||a===void 0)throw Error(`Invalid time format: ${t}`);return(r*3600+i*60+a)*1e3+Number(n)}createTikTokCaptions(e,t){let n=[];if(e.length===0)return{pages:n};let r={startMs:e[0]?.startMs??0,endMs:e[0]?.endMs??0,tokens:[e[0]]};for(let i=1;i<e.length;i++){let a=e[i];a&&(a.startMs-r.endMs<=t?(r.tokens.push(a),r.endMs=a.endMs):(n.push(r),r={startMs:a.startMs,endMs:a.endMs,tokens:[a]}))}return r.tokens.length>0&&n.push(r),{pages:n}}};_=g([(0,c.injectable)()],_);const ae=[`Inter`,`Roboto`,`Open Sans`,`Montserrat`,`Lato`,`Poppins`,`Oswald`,`Raleway`,`Playfair Display`,`Merriweather`,`Source Sans Pro`,`PT Sans`,`Ubuntu`,`Nunito`,`Rubik`,`Work Sans`,`Karla`,`Noto Sans`,`Fira Sans`,`DM Sans`];let v=class{getAvailableGoogleFonts(){return[...ae]}validateFontFamily(e){let t=e.toLowerCase();return ae.some(e=>e.toLowerCase()===t)}};v=g([(0,c.injectable)()],v);const y=(0,u.promisify)(l.execFile);let b=class{async getVideoDuration(e){try{let{stdout:t}=await y(`ffprobe`,[`-v`,`error`,`-show_entries`,`format=duration`,`-of`,`json`,e]),n=JSON.parse(t),r=Number.parseFloat(n.format?.duration??`0`);if(Number.isNaN(r)||r<=0)throw Error(`Invalid duration for video: ${e}`);return r}catch(t){throw Error(`Failed to get video duration for ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}}async getAudioDuration(e){try{let{stdout:t}=await y(`ffprobe`,[`-v`,`error`,`-show_entries`,`format=duration`,`-of`,`json`,e]),n=JSON.parse(t),r=Number.parseFloat(n.format?.duration??`0`);if(Number.isNaN(r)||r<=0)throw Error(`Invalid duration for audio: ${e}`);return r}catch(t){throw Error(`Failed to get audio duration for ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}}async getVideoDimensions(e){try{let{stdout:t}=await y(`ffprobe`,[`-v`,`error`,`-select_streams`,`v:0`,`-show_entries`,`stream=width,height`,`-of`,`json`,e]),n=JSON.parse(t).streams?.[0];if(!n?.width||!n?.height)throw Error(`No video stream found in: ${e}`);return{width:Number(n.width),height:Number(n.height)}}catch(t){throw Error(`Failed to get video dimensions for ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}}async canDecode(e){try{return await y(`ffprobe`,[`-v`,`error`,`-show_entries`,`format=duration`,`-of`,`json`,e]),!0}catch{return!1}}};b=g([(0,c.injectable)()],b);let x=class{bundlePromise=null;async getServeUrl(){if(!this.bundlePromise){let e=d.default.resolve(__dirname,`../..`);this.bundlePromise=(0,ee.bundle)({entryPoint:d.default.resolve(e,`src/remotion/index.ts`),publicDir:d.default.resolve(e,`public`),webpackOverride:e=>({...e,resolve:{...e.resolve,extensionAlias:{".js":[`.ts`,`.tsx`,`.js`,`.jsx`]}}})})}return this.bundlePromise}async render(e){let t=await this.getServeUrl();return await(0,f.renderMedia)({composition:await(0,f.selectComposition)({serveUrl:t,id:e.compositionId,inputProps:e.inputProps}),serveUrl:t,codec:e.codec??`h264`,outputLocation:e.outputPath,inputProps:e.inputProps}),{outputPath:e.outputPath}}async renderStill(e){let t=await this.getServeUrl();return await(0,f.renderStill)({composition:await(0,f.selectComposition)({serveUrl:t,id:e.compositionId,inputProps:e.inputProps}),serveUrl:t,output:e.outputPath,frame:e.frame??0,imageFormat:e.imageFormat??`png`,inputProps:e.inputProps}),{outputPath:e.outputPath}}};x=g([(0,c.injectable)()],x);const S=(0,u.promisify)(l.execFile),oe=[`af_sarah`,`af_nicole`,`af_bella`,`af_sky`,`am_adam`,`am_michael`,`bf_emma`,`bf_isabella`,`bm_george`,`bm_lewis`];let C=class{async isAvailable(){try{return await S(`kokoro-tts`,[`--version`]),!0}catch{return!1}}async generateVoiceover(e){let{text:t,voice:n=`af_sarah`,speed:r=1,outputPath:i}=e,a=await p.default.mkdtemp(d.default.join(m.default.tmpdir(),`kokoro-`)),o=d.default.join(a,`input.txt`);try{return await p.default.writeFile(o,t,`utf-8`),await S(`kokoro-tts`,[o,i,`--voice`,n,`--speed`,r.toString()]),{outputPath:i}}catch(e){throw Error(`Failed to generate voiceover: ${e instanceof Error?e.message:String(e)}`,{cause:e})}finally{try{await p.default.rm(a,{recursive:!0,force:!0})}catch{}}}listVoices(){return[...oe]}};C=g([(0,c.injectable)()],C);let w=class{success(e){return{content:[{type:`text`,text:e}]}}successJson(e){return{content:[{type:`text`,text:JSON.stringify(e,null,2)}]}}error(e){return{content:[{type:`text`,text:`Error: ${e}`}],isError:!0}}async safeExecute(e){try{return await e()}catch(e){return this.error(e instanceof Error?e.message:`Unknown error`)}}};w=g([(0,c.injectable)()],w);const T={RenderService:Symbol.for(`RenderService`),MediaInfoService:Symbol.for(`MediaInfoService`),CaptionService:Symbol.for(`CaptionService`),FontService:Symbol.for(`FontService`),VoiceoverService:Symbol.for(`VoiceoverService`),Tool:Symbol.for(`Tool`)};function E(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}function D(e,t){return function(n,r){t(n,r,e)}}var O,k;const A=h.z.object({captions:h.z.array(h.z.object({text:h.z.string(),startMs:h.z.number(),endMs:h.z.number()})).describe(`Array of caption objects with text, startMs, and endMs`),combineMs:h.z.number().optional().describe(`Milliseconds threshold for combining captions into pages (default: 100)`)});let j=class extends w{static{k=this}static TOOL_NAME=`generate_captions`;constructor(e){super(),this.captionService=e}getInputSchema(){return A}getDefinition(){return{name:k.TOOL_NAME,description:`Create TikTok-style caption pages from a caption array for video overlay`,inputSchema:h.z.toJSONSchema(A,{reused:`inline`})}}async execute(e){try{let{captions:t,combineMs:n=100}=A.parse(e),r=t.map(e=>({text:e.text,startMs:e.startMs,endMs:e.endMs})),i=this.captionService.createTikTokCaptions(r,n);return this.successJson(i)}catch(e){return this.error(e instanceof Error?e.message:`Unknown error`)}}};j=k=g([(0,c.injectable)(),D(0,(0,c.inject)(T.CaptionService)),E(`design:paramtypes`,[typeof(O=_!==void 0&&_)==`function`?O:Object])],j);var M,N;const P=h.z.object({text:h.z.string().describe(`Text to convert to speech`),voice:h.z.string().optional().describe(`Voice ID (default: af_sarah). Options: af_sarah, af_nicole, af_bella, af_sky, am_adam, am_michael, bf_emma, bf_isabella, bm_george, bm_lewis`),speed:h.z.number().optional().describe(`Speech speed multiplier (default: 1.0)`),outputPath:h.z.string().describe(`Output file path for the generated audio`)});let F=class extends w{static{N=this}static TOOL_NAME=`generate_voiceover`;constructor(e){super(),this.voiceoverService=e}getInputSchema(){return P}getDefinition(){return{name:N.TOOL_NAME,description:`Generate voiceover audio from text using Kokoro TTS local engine`,inputSchema:h.z.toJSONSchema(P,{reused:`inline`})}}async execute(e){try{let t=P.parse(e);if(!await this.voiceoverService.isAvailable())return this.error(`Kokoro TTS is not installed. Install it with: pip install kokoro-tts`);let n=await this.voiceoverService.generateVoiceover({text:t.text,voice:t.voice,speed:t.speed,outputPath:t.outputPath});return this.successJson(n)}catch(e){return this.error(e instanceof Error?e.message:`Unknown error`)}}};F=N=g([(0,c.injectable)(),D(0,(0,c.inject)(T.VoiceoverService)),E(`design:paramtypes`,[typeof(M=C!==void 0&&C)==`function`?M:Object])],F);var I,L;const R=h.z.object({src:h.z.string().describe(`Path to the media file`),type:h.z.enum([`video`,`audio`]).optional().describe(`Media type (default: video)`)});let z=class extends w{static{L=this}static TOOL_NAME=`get_media_info`;constructor(e){super(),this.mediaInfoService=e}getInputSchema(){return R}getDefinition(){return{name:L.TOOL_NAME,description:`Get duration, dimensions, and decodability info for video/audio files`,inputSchema:h.z.toJSONSchema(R,{reused:`inline`})}}async execute(e){try{let{src:t,type:n=`video`}=R.parse(e),r=await this.mediaInfoService.canDecode(t);if(n===`audio`){let e=await this.mediaInfoService.getAudioDuration(t);return this.successJson({src:t,type:`audio`,duration:e,canDecode:r})}let[i,a]=await Promise.all([this.mediaInfoService.getVideoDuration(t),this.mediaInfoService.getVideoDimensions(t)]);return this.successJson({src:t,type:`video`,duration:i,width:a.width,height:a.height,canDecode:r})}catch(e){return this.error(e instanceof Error?e.message:`Unknown error`)}}};z=L=g([(0,c.injectable)(),D(0,(0,c.inject)(T.MediaInfoService)),E(`design:paramtypes`,[typeof(I=b!==void 0&&b)==`function`?I:Object])],z);var B,V;const H=h.z.object({srtContent:h.z.string().describe(`SRT file content as a string`)});let U=class extends w{static{V=this}static TOOL_NAME=`import_srt`;constructor(e){super(),this.captionService=e}getInputSchema(){return H}getDefinition(){return{name:V.TOOL_NAME,description:`Parse SRT subtitle file content into structured Caption format for video overlay`,inputSchema:h.z.toJSONSchema(H,{reused:`inline`})}}async execute(e){try{let t=H.parse(e),n=this.captionService.parseSrt(t.srtContent);return this.successJson({captionCount:n.length,captions:n})}catch(e){return this.error(e instanceof Error?e.message:`Unknown error`)}}};U=V=g([(0,c.injectable)(),D(0,(0,c.inject)(T.CaptionService)),E(`design:paramtypes`,[typeof(B=_!==void 0&&_)==`function`?B:Object])],U);var W;const G=h.z.object({});let K=class extends w{static{W=this}static TOOL_NAME=`list_compositions`;getInputSchema(){return G}getDefinition(){return{name:W.TOOL_NAME,description:`List all available Remotion compositions with their schemas, dimensions, and format info`,inputSchema:h.z.toJSONSchema(G,{reused:`inline`})}}async execute(){let e=[{id:`Main`,description:`Main 16:9 landscape composition`,width:1920,height:1080,fps:30,durationInFrames:null,defaultProps:{clips:[],audioVolume:1,backgroundColor:`#000000`}},{id:`Vertical`,description:`Vertical 9:16 portrait composition (TikTok, Instagram Stories)`,width:1080,height:1920,fps:30,durationInFrames:null,defaultProps:{clips:[],audioVolume:1,backgroundColor:`#000000`}},{id:`Square`,description:`Square 1:1 composition (Instagram posts)`,width:1080,height:1080,fps:30,durationInFrames:null,defaultProps:{clips:[],audioVolume:1,backgroundColor:`#000000`}}];return this.successJson({compositionCount:e.length,compositions:e})}};K=W=g([(0,c.injectable)()],K);var q,J;const Y=h.z.object({compositionId:h.z.string().describe(`Remotion composition ID (e.g., "Main", "Vertical", "Square")`),inputProps:h.z.record(h.z.string(),h.z.unknown()).describe(`JSON props to pass to the composition`),outputPath:h.z.string().describe(`Output file path for the rendered image`),frame:h.z.number().optional().describe(`Frame number to render (default: 0)`),imageFormat:h.z.enum([`png`,`jpeg`]).optional().describe(`Image format (default: png)`)});let X=class extends w{static{J=this}static TOOL_NAME=`preview_frame`;constructor(e){super(),this.renderService=e}getInputSchema(){return Y}getDefinition(){return{name:J.TOOL_NAME,description:`Render a single frame from a composition as a PNG or JPEG image for preview`,inputSchema:h.z.toJSONSchema(Y,{reused:`inline`})}}async execute(e){try{let t=Y.parse(e),n={compositionId:t.compositionId,inputProps:t.inputProps,outputPath:t.outputPath,frame:t.frame,imageFormat:t.imageFormat},r=await this.renderService.renderStill(n);return this.successJson(r)}catch(e){return this.error(e instanceof Error?e.message:`Unknown error`)}}};X=J=g([(0,c.injectable)(),D(0,(0,c.inject)(T.RenderService)),E(`design:paramtypes`,[typeof(q=x!==void 0&&x)==`function`?q:Object])],X);var se,Z;const Q=h.z.object({compositionId:h.z.string().describe(`Remotion composition ID (e.g., "Main", "Vertical", "Square")`),inputProps:h.z.record(h.z.string(),h.z.unknown()).describe(`JSON props to pass to the composition`),outputPath:h.z.string().describe(`Output file path for the rendered video`),codec:h.z.enum([`h264`,`h265`,`vp8`,`vp9`]).optional().describe(`Video codec (default: h264)`)});let $=class extends w{static{Z=this}static TOOL_NAME=`render_video`;constructor(e){super(),this.renderService=e}getInputSchema(){return Q}getDefinition(){return{name:Z.TOOL_NAME,description:`Render a video from JSON props using Remotion`,inputSchema:h.z.toJSONSchema(Q,{reused:`inline`})}}async execute(e){try{let t=Q.parse(e),n={compositionId:t.compositionId,inputProps:t.inputProps,outputPath:t.outputPath,codec:t.codec},r=await this.renderService.render(n);return this.successJson(r)}catch(e){return this.error(e instanceof Error?e.message:`Unknown error`)}}};$=Z=g([(0,c.injectable)(),D(0,(0,c.inject)(T.RenderService)),E(`design:paramtypes`,[typeof(se=x!==void 0&&x)==`function`?se:Object])],$);const ce=new c.ContainerModule(e=>{e.bind(T.RenderService).to(x).inSingletonScope(),e.bind(T.MediaInfoService).to(b).inSingletonScope(),e.bind(T.CaptionService).to(_).inSingletonScope(),e.bind(T.FontService).to(v).inSingletonScope(),e.bind(T.VoiceoverService).to(C).inSingletonScope()}),le=new c.ContainerModule(e=>{e.bind(T.Tool).to($).inSingletonScope(),e.bind(T.Tool).to(K).inSingletonScope(),e.bind(T.Tool).to(z).inSingletonScope(),e.bind(T.Tool).to(X).inSingletonScope(),e.bind(T.Tool).to(j).inSingletonScope(),e.bind(T.Tool).to(U).inSingletonScope(),e.bind(T.Tool).to(F).inSingletonScope()});function ue(){let e=new c.Container;return e.load(ce,le),e}function de(e){let t=new ne.Server({name:`video-editor-mcp`,version:`0.1.0`},{capabilities:{tools:{}}}),n=e.getAll(T.Tool),r=new Map;for(let e of n)r.set(e.getDefinition().name,e);return t.setRequestHandler(re.ListToolsRequestSchema,async()=>({tools:n.map(e=>e.getDefinition())})),t.setRequestHandler(re.CallToolRequestSchema,async e=>{let{name:t,arguments:n}=e.params,i=r.get(t);if(!i)return{content:[{type:`text`,text:`Unknown tool: ${t}`}],isError:!0};let a=(0,te.coerceArgs)(n??{},i.getInputSchema());try{return await i.execute(a)}catch(e){if(e instanceof h.z.ZodError)return{content:[{type:`text`,text:(0,te.formatZodError)(e,{schemaName:t,schema:i.getInputSchema()})}],isError:!0};throw e}}),t}var fe=class{server;transport=null;constructor(e){this.server=e}async start(){this.transport=new ie.StdioServerTransport,await this.server.connect(this.transport),console.error(`video-editor MCP server started on stdio`)}async stop(){this.transport&&=(await this.transport.close(),null)}};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return X}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return z}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return $}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return de}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return K}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return ue}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return U}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return fe}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return j}});
4
- //# sourceMappingURL=stdio-Dwc6SWvA.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stdio-Dwc6SWvA.cjs","names":["execFileAsync","execFile","path","execFile","fs","path","os","z","z","z","z","z","z","z","ContainerModule","Container","Server","ListToolsRequestSchema","CallToolRequestSchema","z","StdioServerTransport"],"sources":["../src/services/CaptionService.ts","../src/services/FontService.ts","../src/services/MediaInfoService.ts","../src/services/RenderService.ts","../src/services/VoiceoverService.ts","../src/tools/BaseTool.ts","../src/types/index.ts","../src/tools/GenerateCaptionsTool.ts","../src/tools/GenerateVoiceoverTool.ts","../src/tools/GetMediaInfoTool.ts","../src/tools/ImportSrtTool.ts","../src/tools/ListCompositionsTool.ts","../src/tools/PreviewFrameTool.ts","../src/tools/RenderVideoTool.ts","../src/container/index.ts","../src/server/index.ts","../src/transports/stdio.ts"],"sourcesContent":["/**\n * CaptionService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Dependency injection via InversifyJS\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n */\n\nimport { injectable } from 'inversify';\nimport type { Caption } from '../types/index.js';\n\n/**\n * TikTok-style caption page with grouped tokens\n */\nexport interface CaptionPage {\n startMs: number;\n endMs: number;\n tokens: Caption[];\n}\n\n@injectable()\nexport class CaptionService {\n /**\n * Parse SRT format content into Caption array\n *\n * SRT format:\n * 1\n * 00:00:01,000 --> 00:00:04,000\n * Welcome to the video\n *\n * 2\n * 00:00:04,500 --> 00:00:08,000\n * This is the second caption\n */\n parseSrt(srtContent: string): Caption[] {\n const captions: Caption[] = [];\n const lines = srtContent.split('\\n');\n let i = 0;\n\n while (i < lines.length) {\n // Skip empty lines\n if (!lines[i]?.trim()) {\n i++;\n continue;\n }\n\n // Skip sequence number\n i++;\n\n // Parse timestamp line\n const timestampLine = lines[i];\n if (!timestampLine?.includes('-->')) {\n i++;\n continue;\n }\n\n const [startStr, endStr] = timestampLine.split('-->').map((s) => s.trim());\n if (!startStr || !endStr) {\n i++;\n continue;\n }\n\n const startMs = this.parseTimestamp(startStr);\n const endMs = this.parseTimestamp(endStr);\n\n // Collect text lines until empty line or end\n i++;\n const textLines: string[] = [];\n while (i < lines.length && lines[i]?.trim()) {\n textLines.push(lines[i] ?? '');\n i++;\n }\n\n const text = textLines.join('\\n');\n if (text) {\n captions.push({ text, startMs, endMs });\n }\n }\n\n return captions;\n }\n\n /**\n * Parse SRT timestamp to milliseconds\n * Format: HH:MM:SS,mmm\n */\n private parseTimestamp(timestamp: string): number {\n const [time, ms] = timestamp.split(',');\n if (!time || !ms) {\n throw new Error(`Invalid timestamp format: ${timestamp}`);\n }\n\n const [hours, minutes, seconds] = time.split(':').map(Number);\n if (hours === undefined || minutes === undefined || seconds === undefined) {\n throw new Error(`Invalid time format: ${time}`);\n }\n\n const totalSeconds = hours * 3600 + minutes * 60 + seconds;\n return totalSeconds * 1000 + Number(ms);\n }\n\n /**\n * Create TikTok-style caption pages by grouping captions\n * Combines tokens that appear within combineMs milliseconds of each other\n */\n createTikTokCaptions(captions: Caption[], combineMs: number): { pages: CaptionPage[] } {\n const pages: CaptionPage[] = [];\n\n if (captions.length === 0) {\n return { pages };\n }\n\n let currentPage: CaptionPage = {\n startMs: captions[0]?.startMs ?? 0,\n endMs: captions[0]?.endMs ?? 0,\n tokens: [captions[0] as Caption],\n };\n\n for (let i = 1; i < captions.length; i++) {\n const caption = captions[i];\n if (!caption) continue;\n\n const gap = caption.startMs - currentPage.endMs;\n\n if (gap <= combineMs) {\n // Combine into current page\n currentPage.tokens.push(caption);\n currentPage.endMs = caption.endMs;\n } else {\n // Start new page\n pages.push(currentPage);\n currentPage = {\n startMs: caption.startMs,\n endMs: caption.endMs,\n tokens: [caption],\n };\n }\n }\n\n // Add final page\n if (currentPage.tokens.length > 0) {\n pages.push(currentPage);\n }\n\n return { pages };\n }\n}\n","/**\n * FontService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Dependency injection via InversifyJS\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n */\n\nimport { injectable } from 'inversify';\n\n/**\n * Curated list of popular Google Fonts\n * These fonts are widely used and well-supported\n */\nconst AVAILABLE_GOOGLE_FONTS = [\n 'Inter',\n 'Roboto',\n 'Open Sans',\n 'Montserrat',\n 'Lato',\n 'Poppins',\n 'Oswald',\n 'Raleway',\n 'Playfair Display',\n 'Merriweather',\n 'Source Sans Pro',\n 'PT Sans',\n 'Ubuntu',\n 'Nunito',\n 'Rubik',\n 'Work Sans',\n 'Karla',\n 'Noto Sans',\n 'Fira Sans',\n 'DM Sans',\n] as const;\n\n@injectable()\nexport class FontService {\n /**\n * Get list of available Google Fonts\n */\n getAvailableGoogleFonts(): string[] {\n return [...AVAILABLE_GOOGLE_FONTS];\n }\n\n /**\n * Validate if a font family is in the available list\n * Case-insensitive comparison\n */\n validateFontFamily(family: string): boolean {\n const normalizedFamily = family.toLowerCase();\n return AVAILABLE_GOOGLE_FONTS.some((font) => font.toLowerCase() === normalizedFamily);\n }\n}\n","/**\n * MediaInfoService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Dependency injection via InversifyJS\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n */\n\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { injectable } from 'inversify';\n\nconst execFileAsync = promisify(execFile);\n\n@injectable()\nexport class MediaInfoService {\n /**\n * Get video duration in seconds using ffprobe\n */\n async getVideoDuration(src: string): Promise<number> {\n try {\n const { stdout } = await execFileAsync('ffprobe', [\n '-v',\n 'error',\n '-show_entries',\n 'format=duration',\n '-of',\n 'json',\n src,\n ]);\n\n const result = JSON.parse(stdout);\n const duration = Number.parseFloat(result.format?.duration ?? '0');\n\n if (Number.isNaN(duration) || duration <= 0) {\n throw new Error(`Invalid duration for video: ${src}`);\n }\n\n return duration;\n } catch (error) {\n throw new Error(\n `Failed to get video duration for ${src}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n }\n\n /**\n * Get audio duration in seconds using ffprobe\n */\n async getAudioDuration(src: string): Promise<number> {\n try {\n const { stdout } = await execFileAsync('ffprobe', [\n '-v',\n 'error',\n '-show_entries',\n 'format=duration',\n '-of',\n 'json',\n src,\n ]);\n\n const result = JSON.parse(stdout);\n const duration = Number.parseFloat(result.format?.duration ?? '0');\n\n if (Number.isNaN(duration) || duration <= 0) {\n throw new Error(`Invalid duration for audio: ${src}`);\n }\n\n return duration;\n } catch (error) {\n throw new Error(\n `Failed to get audio duration for ${src}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n }\n\n /**\n * Get video dimensions (width and height) using ffprobe\n */\n async getVideoDimensions(src: string): Promise<{ width: number; height: number }> {\n try {\n const { stdout } = await execFileAsync('ffprobe', [\n '-v',\n 'error',\n '-select_streams',\n 'v:0',\n '-show_entries',\n 'stream=width,height',\n '-of',\n 'json',\n src,\n ]);\n\n const result = JSON.parse(stdout);\n const stream = result.streams?.[0];\n\n if (!stream?.width || !stream?.height) {\n throw new Error(`No video stream found in: ${src}`);\n }\n\n return {\n width: Number(stream.width),\n height: Number(stream.height),\n };\n } catch (error) {\n throw new Error(\n `Failed to get video dimensions for ${src}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n /**\n * Check if a media file can be decoded using ffprobe\n */\n async canDecode(src: string): Promise<boolean> {\n try {\n await execFileAsync('ffprobe', ['-v', 'error', '-show_entries', 'format=duration', '-of', 'json', src]);\n return true;\n } catch {\n return false;\n }\n }\n}\n","/**\n * RenderService\n *\n * Uses @remotion/renderer to render videos from JSON props.\n */\n\nimport path from 'node:path';\nimport { bundle } from '@remotion/bundler';\nimport { renderMedia, renderStill, selectComposition } from '@remotion/renderer';\nimport { injectable } from 'inversify';\nimport type { RenderOptions, RenderResult, RenderStillOptions } from '../types/index.js';\n\n@injectable()\nexport class RenderService {\n private bundlePromise: Promise<string> | null = null;\n\n private async getServeUrl(): Promise<string> {\n if (!this.bundlePromise) {\n const projectRoot = path.resolve(import.meta.dirname, '../..');\n this.bundlePromise = bundle({\n entryPoint: path.resolve(projectRoot, 'src/remotion/index.ts'),\n publicDir: path.resolve(projectRoot, 'public'),\n webpackOverride: (config) => ({\n ...config,\n resolve: {\n ...config.resolve,\n extensionAlias: {\n '.js': ['.ts', '.tsx', '.js', '.jsx'],\n },\n },\n }),\n });\n }\n return this.bundlePromise;\n }\n\n async render(options: RenderOptions): Promise<RenderResult> {\n const serveUrl = await this.getServeUrl();\n\n const composition = await selectComposition({\n serveUrl,\n id: options.compositionId,\n inputProps: options.inputProps,\n });\n\n await renderMedia({\n composition,\n serveUrl,\n codec: options.codec ?? 'h264',\n outputLocation: options.outputPath,\n inputProps: options.inputProps,\n });\n\n return { outputPath: options.outputPath };\n }\n\n async renderStill(options: RenderStillOptions): Promise<RenderResult> {\n const serveUrl = await this.getServeUrl();\n\n const composition = await selectComposition({\n serveUrl,\n id: options.compositionId,\n inputProps: options.inputProps,\n });\n\n await renderStill({\n composition,\n serveUrl,\n output: options.outputPath,\n frame: options.frame ?? 0,\n imageFormat: options.imageFormat ?? 'png',\n inputProps: options.inputProps,\n });\n\n return { outputPath: options.outputPath };\n }\n}\n","/**\n * VoiceoverService\n *\n * DESIGN PATTERNS:\n * - Service pattern for business logic encapsulation\n * - Dependency injection via InversifyJS\n * - Single responsibility principle\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Keep methods focused and well-named\n * - Use execFile for command execution (prevents command injection)\n */\n\nimport { execFile } from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\nimport { injectable } from 'inversify';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Available Kokoro TTS voices\n */\nconst AVAILABLE_VOICES = [\n 'af_sarah',\n 'af_nicole',\n 'af_bella',\n 'af_sky',\n 'am_adam',\n 'am_michael',\n 'bf_emma',\n 'bf_isabella',\n 'bm_george',\n 'bm_lewis',\n] as const;\n\nexport interface VoiceoverOptions {\n text: string;\n voice?: string;\n speed?: number;\n outputPath: string;\n}\n\nexport interface VoiceoverResult {\n outputPath: string;\n duration?: number;\n}\n\n@injectable()\nexport class VoiceoverService {\n /**\n * Check if kokoro-tts CLI is installed\n */\n async isAvailable(): Promise<boolean> {\n try {\n await execFileAsync('kokoro-tts', ['--version']);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Generate voiceover using Kokoro TTS\n */\n async generateVoiceover(options: VoiceoverOptions): Promise<VoiceoverResult> {\n const { text, voice = 'af_sarah', speed = 1.0, outputPath } = options;\n\n // Create temp file for text input\n const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'kokoro-'));\n const tempTextFile = path.join(tempDir, 'input.txt');\n\n try {\n // Write text to temp file\n await fs.writeFile(tempTextFile, text, 'utf-8');\n\n // Run kokoro-tts CLI\n const args = [tempTextFile, outputPath, '--voice', voice, '--speed', speed.toString()];\n\n await execFileAsync('kokoro-tts', args);\n\n return { outputPath };\n } catch (error) {\n throw new Error(`Failed to generate voiceover: ${error instanceof Error ? error.message : String(error)}`, {\n cause: error,\n });\n } finally {\n // Cleanup temp file\n try {\n await fs.rm(tempDir, { recursive: true, force: true });\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n\n /**\n * Get list of available Kokoro voice IDs\n */\n listVoices(): string[] {\n return [...AVAILABLE_VOICES];\n }\n}\n","/**\n * BaseTool - Abstract base class for video editor MCP tools\n *\n * DESIGN PATTERNS:\n * - Template Method pattern for consistent tool interface\n * - Dependency Injection via InversifyJS for services\n * - Helper methods for response formatting\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Return CallToolResult with content array\n * - Handle errors gracefully with isError flag\n *\n * AVOID:\n * - Business logic in base class (delegate to services)\n * - Exposing internal errors to users\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { injectable } from 'inversify';\n\n@injectable()\nexport abstract class BaseTool {\n /**\n * Creates a success response with text content.\n * @param text - The success message or content.\n * @returns A CallToolResult with the content.\n */\n protected success(text: string): CallToolResult {\n return {\n content: [{ type: 'text', text }],\n };\n }\n\n /**\n * Creates a success response with JSON content.\n * @param data - The data to serialize as JSON.\n * @returns A CallToolResult with the JSON content.\n */\n protected successJson(data: unknown): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],\n };\n }\n\n /**\n * Creates an error response.\n * @param message - The error message.\n * @returns A CallToolResult with isError flag set.\n */\n protected error(message: string): CallToolResult {\n return {\n content: [{ type: 'text', text: `Error: ${message}` }],\n isError: true,\n };\n }\n\n /**\n * Wraps execution with standard error handling.\n * @param fn - The async function to execute.\n * @returns The result of the function or an error response.\n */\n protected async safeExecute<T>(fn: () => Promise<T>): Promise<T | CallToolResult> {\n try {\n return await fn();\n } catch (err) {\n return this.error(err instanceof Error ? err.message : 'Unknown error');\n }\n }\n}\n","/**\n * Shared TypeScript Types\n *\n * DESIGN PATTERNS:\n * - Type-first development with Zod schema inference\n * - Interface segregation\n *\n * CODING STANDARDS:\n * - Export all shared types from this file\n * - Derive types from Zod schemas via z.infer<>\n */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { z } from 'zod';\nimport type {\n AudioClipSchema,\n ClipSchema,\n GifClipSchema,\n ImageClipSchema,\n LottieClipSchema,\n SubtitleClipSchema,\n TextClipSchema,\n VideoClipSchema,\n} from '../schemas/clips.js';\nimport type {\n CaptionConfigSchema,\n CaptionSchema,\n FontConfigSchema,\n MainCompositionSchema,\n} from '../schemas/compositions.js';\n\n/**\n * DI Container Symbols\n */\nexport const TYPES = {\n RenderService: Symbol.for('RenderService'),\n MediaInfoService: Symbol.for('MediaInfoService'),\n CaptionService: Symbol.for('CaptionService'),\n FontService: Symbol.for('FontService'),\n VoiceoverService: Symbol.for('VoiceoverService'),\n Tool: Symbol.for('Tool'),\n} as const;\n\n/**\n * Tool definition for MCP\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\n/**\n * Base tool interface following MCP SDK patterns\n */\nexport interface Tool<TInput = unknown> {\n getDefinition(): ToolDefinition;\n getInputSchema(): z.ZodObject<z.ZodRawShape>;\n execute(input: TInput): Promise<CallToolResult>;\n}\n\n/**\n * Render options for Remotion\n */\nexport interface RenderOptions {\n compositionId: string;\n inputProps: Record<string, unknown>;\n outputPath: string;\n codec?: 'h264' | 'h265' | 'vp8' | 'vp9';\n}\n\n/**\n * Render still options\n */\nexport interface RenderStillOptions {\n compositionId: string;\n inputProps: Record<string, unknown>;\n outputPath: string;\n frame?: number;\n imageFormat?: 'png' | 'jpeg';\n}\n\n/**\n * Render result from Remotion\n */\nexport interface RenderResult {\n outputPath: string;\n}\n\n// Clip types derived from Zod schemas\nexport type VideoClip = z.infer<typeof VideoClipSchema>;\nexport type ImageClip = z.infer<typeof ImageClipSchema>;\nexport type TextClip = z.infer<typeof TextClipSchema>;\nexport type AudioClip = z.infer<typeof AudioClipSchema>;\nexport type SubtitleClip = z.infer<typeof SubtitleClipSchema>;\nexport type GifClip = z.infer<typeof GifClipSchema>;\nexport type LottieClip = z.infer<typeof LottieClipSchema>;\nexport type Clip = z.infer<typeof ClipSchema>;\n\n// Composition types derived from Zod schemas\nexport type Caption = z.infer<typeof CaptionSchema>;\nexport type CaptionConfig = z.infer<typeof CaptionConfigSchema>;\nexport type FontConfig = z.infer<typeof FontConfigSchema>;\nexport type MainCompositionProps = z.infer<typeof MainCompositionSchema>;\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { CaptionService } from '../services/CaptionService.js';\nimport type { Caption, Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const GenerateCaptionsToolInputSchema = z.object({\n captions: z\n .array(\n z.object({\n text: z.string(),\n startMs: z.number(),\n endMs: z.number(),\n }),\n )\n .describe('Array of caption objects with text, startMs, and endMs'),\n combineMs: z.number().optional().describe('Milliseconds threshold for combining captions into pages (default: 100)'),\n});\n\ntype GenerateCaptionsToolInput = z.infer<typeof GenerateCaptionsToolInputSchema>;\n\n@injectable()\nexport class GenerateCaptionsTool extends BaseTool implements Tool<GenerateCaptionsToolInput> {\n static readonly TOOL_NAME = 'generate_captions';\n\n constructor(@inject(TYPES.CaptionService) private captionService: CaptionService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return GenerateCaptionsToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: GenerateCaptionsTool.TOOL_NAME,\n description: 'Create TikTok-style caption pages from a caption array for video overlay',\n inputSchema: z.toJSONSchema(GenerateCaptionsToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = GenerateCaptionsToolInputSchema.parse(rawInput);\n const { captions, combineMs = 100 } = input;\n const captionArray: Caption[] = captions.map((c) => ({\n text: c.text,\n startMs: c.startMs,\n endMs: c.endMs,\n }));\n const result = this.captionService.createTikTokCaptions(captionArray, combineMs);\n return this.successJson(result);\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { VoiceoverService } from '../services/VoiceoverService.js';\nimport type { Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const GenerateVoiceoverToolInputSchema = z.object({\n text: z.string().describe('Text to convert to speech'),\n voice: z\n .string()\n .optional()\n .describe(\n 'Voice ID (default: af_sarah). Options: af_sarah, af_nicole, af_bella, af_sky, am_adam, am_michael, bf_emma, bf_isabella, bm_george, bm_lewis',\n ),\n speed: z.number().optional().describe('Speech speed multiplier (default: 1.0)'),\n outputPath: z.string().describe('Output file path for the generated audio'),\n});\n\ntype GenerateVoiceoverToolInput = z.infer<typeof GenerateVoiceoverToolInputSchema>;\n\n@injectable()\nexport class GenerateVoiceoverTool extends BaseTool implements Tool<GenerateVoiceoverToolInput> {\n static readonly TOOL_NAME = 'generate_voiceover';\n\n constructor(@inject(TYPES.VoiceoverService) private voiceoverService: VoiceoverService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return GenerateVoiceoverToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: GenerateVoiceoverTool.TOOL_NAME,\n description: 'Generate voiceover audio from text using Kokoro TTS local engine',\n inputSchema: z.toJSONSchema(GenerateVoiceoverToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = GenerateVoiceoverToolInputSchema.parse(rawInput);\n const isAvailable = await this.voiceoverService.isAvailable();\n if (!isAvailable) {\n return this.error('Kokoro TTS is not installed. Install it with: pip install kokoro-tts');\n }\n\n const result = await this.voiceoverService.generateVoiceover({\n text: input.text,\n voice: input.voice,\n speed: input.speed,\n outputPath: input.outputPath,\n });\n\n return this.successJson(result);\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { MediaInfoService } from '../services/MediaInfoService.js';\nimport type { Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const GetMediaInfoToolInputSchema = z.object({\n src: z.string().describe('Path to the media file'),\n type: z.enum(['video', 'audio']).optional().describe('Media type (default: video)'),\n});\n\ntype GetMediaInfoToolInput = z.infer<typeof GetMediaInfoToolInputSchema>;\n\n@injectable()\nexport class GetMediaInfoTool extends BaseTool implements Tool<GetMediaInfoToolInput> {\n static readonly TOOL_NAME = 'get_media_info';\n\n constructor(@inject(TYPES.MediaInfoService) private mediaInfoService: MediaInfoService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return GetMediaInfoToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: GetMediaInfoTool.TOOL_NAME,\n description: 'Get duration, dimensions, and decodability info for video/audio files',\n inputSchema: z.toJSONSchema(GetMediaInfoToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = GetMediaInfoToolInputSchema.parse(rawInput);\n const { src, type = 'video' } = input;\n const canDecode = await this.mediaInfoService.canDecode(src);\n\n if (type === 'audio') {\n const duration = await this.mediaInfoService.getAudioDuration(src);\n return this.successJson({ src, type: 'audio', duration, canDecode });\n }\n\n const [duration, dimensions] = await Promise.all([\n this.mediaInfoService.getVideoDuration(src),\n this.mediaInfoService.getVideoDimensions(src),\n ]);\n\n return this.successJson({\n src,\n type: 'video',\n duration,\n width: dimensions.width,\n height: dimensions.height,\n canDecode,\n });\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { CaptionService } from '../services/CaptionService.js';\nimport type { Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const ImportSrtToolInputSchema = z.object({\n srtContent: z.string().describe('SRT file content as a string'),\n});\n\ntype ImportSrtToolInput = z.infer<typeof ImportSrtToolInputSchema>;\n\n@injectable()\nexport class ImportSrtTool extends BaseTool implements Tool<ImportSrtToolInput> {\n static readonly TOOL_NAME = 'import_srt';\n\n constructor(@inject(TYPES.CaptionService) private captionService: CaptionService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return ImportSrtToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: ImportSrtTool.TOOL_NAME,\n description: 'Parse SRT subtitle file content into structured Caption format for video overlay',\n inputSchema: z.toJSONSchema(ImportSrtToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = ImportSrtToolInputSchema.parse(rawInput);\n const captions = this.captionService.parseSrt(input.srtContent);\n return this.successJson({ captionCount: captions.length, captions });\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { injectable } from 'inversify';\nimport { z } from 'zod';\nimport type { Tool, ToolDefinition } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const ListCompositionsToolInputSchema = z.object({});\n\ntype ListCompositionsToolInput = z.infer<typeof ListCompositionsToolInputSchema>;\n\n@injectable()\nexport class ListCompositionsTool extends BaseTool implements Tool<ListCompositionsToolInput> {\n static readonly TOOL_NAME = 'list_compositions';\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return ListCompositionsToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: ListCompositionsTool.TOOL_NAME,\n description: 'List all available Remotion compositions with their schemas, dimensions, and format info',\n inputSchema: z.toJSONSchema(ListCompositionsToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(): Promise<CallToolResult> {\n const compositions = [\n {\n id: 'Main',\n description: 'Main 16:9 landscape composition',\n width: 1920,\n height: 1080,\n fps: 30,\n durationInFrames: null,\n defaultProps: { clips: [], audioVolume: 1, backgroundColor: '#000000' },\n },\n {\n id: 'Vertical',\n description: 'Vertical 9:16 portrait composition (TikTok, Instagram Stories)',\n width: 1080,\n height: 1920,\n fps: 30,\n durationInFrames: null,\n defaultProps: { clips: [], audioVolume: 1, backgroundColor: '#000000' },\n },\n {\n id: 'Square',\n description: 'Square 1:1 composition (Instagram posts)',\n width: 1080,\n height: 1080,\n fps: 30,\n durationInFrames: null,\n defaultProps: { clips: [], audioVolume: 1, backgroundColor: '#000000' },\n },\n ];\n\n return this.successJson({ compositionCount: compositions.length, compositions });\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { RenderService } from '../services/RenderService.js';\nimport type { RenderStillOptions, Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const PreviewFrameToolInputSchema = z.object({\n compositionId: z.string().describe('Remotion composition ID (e.g., \"Main\", \"Vertical\", \"Square\")'),\n inputProps: z.record(z.string(), z.unknown()).describe('JSON props to pass to the composition'),\n outputPath: z.string().describe('Output file path for the rendered image'),\n frame: z.number().optional().describe('Frame number to render (default: 0)'),\n imageFormat: z.enum(['png', 'jpeg']).optional().describe('Image format (default: png)'),\n});\n\ntype PreviewFrameToolInput = z.infer<typeof PreviewFrameToolInputSchema>;\n\n@injectable()\nexport class PreviewFrameTool extends BaseTool implements Tool<PreviewFrameToolInput> {\n static readonly TOOL_NAME = 'preview_frame';\n\n constructor(@inject(TYPES.RenderService) private renderService: RenderService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return PreviewFrameToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: PreviewFrameTool.TOOL_NAME,\n description: 'Render a single frame from a composition as a PNG or JPEG image for preview',\n inputSchema: z.toJSONSchema(PreviewFrameToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = PreviewFrameToolInputSchema.parse(rawInput);\n const options: RenderStillOptions = {\n compositionId: input.compositionId,\n inputProps: input.inputProps,\n outputPath: input.outputPath,\n frame: input.frame,\n imageFormat: input.imageFormat,\n };\n const result = await this.renderService.renderStill(options);\n return this.successJson(result);\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { inject, injectable } from 'inversify';\nimport { z } from 'zod';\nimport { RenderService } from '../services/RenderService.js';\nimport type { RenderOptions, Tool, ToolDefinition } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\nimport { BaseTool } from './BaseTool.js';\n\nexport const RenderVideoToolInputSchema = z.object({\n compositionId: z.string().describe('Remotion composition ID (e.g., \"Main\", \"Vertical\", \"Square\")'),\n inputProps: z.record(z.string(), z.unknown()).describe('JSON props to pass to the composition'),\n outputPath: z.string().describe('Output file path for the rendered video'),\n codec: z.enum(['h264', 'h265', 'vp8', 'vp9']).optional().describe('Video codec (default: h264)'),\n});\n\ntype RenderVideoToolInput = z.infer<typeof RenderVideoToolInputSchema>;\n\n@injectable()\nexport class RenderVideoTool extends BaseTool implements Tool<RenderVideoToolInput> {\n static readonly TOOL_NAME = 'render_video';\n\n constructor(@inject(TYPES.RenderService) private renderService: RenderService) {\n super();\n }\n\n getInputSchema(): z.ZodObject<z.ZodRawShape> {\n return RenderVideoToolInputSchema;\n }\n\n getDefinition(): ToolDefinition {\n return {\n name: RenderVideoTool.TOOL_NAME,\n description: 'Render a video from JSON props using Remotion',\n inputSchema: z.toJSONSchema(RenderVideoToolInputSchema, { reused: 'inline' }),\n };\n }\n\n async execute(rawInput: Record<string, unknown>): Promise<CallToolResult> {\n try {\n const input = RenderVideoToolInputSchema.parse(rawInput);\n const options: RenderOptions = {\n compositionId: input.compositionId,\n inputProps: input.inputProps,\n outputPath: input.outputPath,\n codec: input.codec,\n };\n const result = await this.renderService.render(options);\n return this.successJson(result);\n } catch (error) {\n return this.error(error instanceof Error ? error.message : 'Unknown error');\n }\n }\n}\n","/**\n * DI Container Setup\n *\n * DESIGN PATTERNS:\n * - ContainerModule pattern for modular DI configuration\n * - Separation of concerns: services vs tools\n * - Singleton scope for stateful services\n *\n * CODING STANDARDS:\n * - Use ContainerModule for grouping related bindings\n * - Import reflect-metadata at top\n * - Bind services to their interface symbols from TYPES\n */\n\nimport 'reflect-metadata';\nimport { Container, ContainerModule, type ContainerModuleLoadOptions } from 'inversify';\nimport { CaptionService, FontService, MediaInfoService, RenderService, VoiceoverService } from '../services/index.js';\nimport {\n GenerateCaptionsTool,\n GenerateVoiceoverTool,\n GetMediaInfoTool,\n ImportSrtTool,\n ListCompositionsTool,\n PreviewFrameTool,\n RenderVideoTool,\n} from '../tools/index.js';\nimport { TYPES } from '../types/index.js';\n\n/**\n * Services module - binds all core services\n */\nexport const servicesModule = new ContainerModule((options: ContainerModuleLoadOptions) => {\n options.bind(TYPES.RenderService).to(RenderService).inSingletonScope();\n options.bind(TYPES.MediaInfoService).to(MediaInfoService).inSingletonScope();\n options.bind(TYPES.CaptionService).to(CaptionService).inSingletonScope();\n options.bind(TYPES.FontService).to(FontService).inSingletonScope();\n options.bind(TYPES.VoiceoverService).to(VoiceoverService).inSingletonScope();\n});\n\n/**\n * Tools module - binds MCP tools\n */\nexport const toolsModule = new ContainerModule((options: ContainerModuleLoadOptions) => {\n options.bind(TYPES.Tool).to(RenderVideoTool).inSingletonScope();\n options.bind(TYPES.Tool).to(ListCompositionsTool).inSingletonScope();\n options.bind(TYPES.Tool).to(GetMediaInfoTool).inSingletonScope();\n options.bind(TYPES.Tool).to(PreviewFrameTool).inSingletonScope();\n options.bind(TYPES.Tool).to(GenerateCaptionsTool).inSingletonScope();\n options.bind(TYPES.Tool).to(ImportSrtTool).inSingletonScope();\n options.bind(TYPES.Tool).to(GenerateVoiceoverTool).inSingletonScope();\n});\n\n/**\n * Creates and configures the DI container\n */\nexport function createContainer(): Container {\n const container = new Container();\n container.load(servicesModule, toolsModule);\n return container;\n}\n","import { coerceArgs, formatZodError } from '@agimon-ai/foundation-validator';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport type { Container } from 'inversify';\nimport { z } from 'zod';\nimport type { Tool } from '../types/index.js';\nimport { TYPES } from '../types/index.js';\n\nexport function createServer(container: Container): Server {\n const server = new Server({ name: 'video-editor-mcp', version: '0.1.0' }, { capabilities: { tools: {} } });\n\n const tools = container.getAll<Tool>(TYPES.Tool);\n const toolMap = new Map<string, Tool>();\n for (const tool of tools) {\n toolMap.set(tool.getDefinition().name, tool);\n }\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: tools.map((t) => t.getDefinition()),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n const tool = toolMap.get(name);\n if (!tool) {\n return {\n content: [{ type: 'text' as const, text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n\n const coerced = coerceArgs(args ?? {}, tool.getInputSchema());\n\n try {\n return await tool.execute(coerced);\n } catch (error) {\n if (error instanceof z.ZodError) {\n return {\n content: [\n { type: 'text' as const, text: formatZodError(error, { schemaName: name, schema: tool.getInputSchema() }) },\n ],\n isError: true,\n };\n }\n throw error;\n }\n });\n\n return server;\n}\n","/**\n * STDIO Transport\n *\n * DESIGN PATTERNS:\n * - Transport handler pattern implementing TransportHandler interface\n * - Standard I/O based communication for CLI usage\n *\n * CODING STANDARDS:\n * - Initialize server and transport properly\n * - Handle cleanup on shutdown with stop() method\n * - Use async/await for all operations\n *\n * AVOID:\n * - Forgetting to close transport on shutdown\n * - Missing error handling for connection failures\n */\n\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\n\n/**\n * Stdio transport handler for MCP server\n * Used for command-line and direct integrations\n */\nexport class StdioTransportHandler {\n private server: Server;\n private transport: StdioServerTransport | null = null;\n\n constructor(server: Server) {\n this.server = server;\n }\n\n async start(): Promise<void> {\n this.transport = new StdioServerTransport();\n await this.server.connect(this.transport);\n console.error('video-editor MCP server started on stdio');\n }\n\n async stop(): Promise<void> {\n if (this.transport) {\n await this.transport.close();\n this.transport = null;\n }\n }\n}\n"],"mappings":"4xCA2BO,IAAA,EAAA,KAAqB,CAa1B,SAAS,EAA+B,CACtC,IAAM,EAAsB,EAAE,CACxB,EAAQ,EAAW,MAAM;EAAK,CAChC,EAAI,EAER,KAAO,EAAI,EAAM,QAAQ,CAEvB,GAAI,CAAC,EAAM,IAAI,MAAM,CAAE,CACrB,IACA,SAIF,IAGA,IAAM,EAAgB,EAAM,GAC5B,GAAI,CAAC,GAAe,SAAS,MAAM,CAAE,CACnC,IACA,SAGF,GAAM,CAAC,EAAU,GAAU,EAAc,MAAM,MAAM,CAAC,IAAK,GAAM,EAAE,MAAM,CAAC,CAC1E,GAAI,CAAC,GAAY,CAAC,EAAQ,CACxB,IACA,SAGF,IAAM,EAAU,KAAK,eAAe,EAAS,CACvC,EAAQ,KAAK,eAAe,EAAO,CAGzC,IACA,IAAM,EAAsB,EAAE,CAC9B,KAAO,EAAI,EAAM,QAAU,EAAM,IAAI,MAAM,EACzC,EAAU,KAAK,EAAM,IAAM,GAAG,CAC9B,IAGF,IAAM,EAAO,EAAU,KAAK;EAAK,CAC7B,GACF,EAAS,KAAK,CAAE,OAAM,UAAS,QAAO,CAAC,CAI3C,OAAO,EAOT,eAAuB,EAA2B,CAChD,GAAM,CAAC,EAAM,GAAM,EAAU,MAAM,IAAI,CACvC,GAAI,CAAC,GAAQ,CAAC,EACZ,MAAU,MAAM,6BAA6B,IAAY,CAG3D,GAAM,CAAC,EAAO,EAAS,GAAW,EAAK,MAAM,IAAI,CAAC,IAAI,OAAO,CAC7D,GAAI,IAAU,IAAA,IAAa,IAAY,IAAA,IAAa,IAAY,IAAA,GAC9D,MAAU,MAAM,wBAAwB,IAAO,CAIjD,OADqB,EAAQ,KAAO,EAAU,GAAK,GAC7B,IAAO,OAAO,EAAG,CAOzC,qBAAqB,EAAqB,EAA6C,CACrF,IAAM,EAAuB,EAAE,CAE/B,GAAI,EAAS,SAAW,EACtB,MAAO,CAAE,QAAO,CAGlB,IAAI,EAA2B,CAC7B,QAAS,EAAS,IAAI,SAAW,EACjC,MAAO,EAAS,IAAI,OAAS,EAC7B,OAAQ,CAAC,EAAS,GAAc,CACjC,CAED,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,IAAK,CACxC,IAAM,EAAU,EAAS,GACpB,IAEO,EAAQ,QAAU,EAAY,OAE/B,GAET,EAAY,OAAO,KAAK,EAAQ,CAChC,EAAY,MAAQ,EAAQ,QAG5B,EAAM,KAAK,EAAY,CACvB,EAAc,CACZ,QAAS,EAAQ,QACjB,MAAO,EAAQ,MACf,OAAQ,CAAC,EAAQ,CAClB,GASL,OAJI,EAAY,OAAO,OAAS,GAC9B,EAAM,KAAK,EAAY,CAGlB,CAAE,QAAO,0BA5HP,CAAA,CAAA,EAAA,CCNb,MAAM,GAAyB,CAC7B,QACA,SACA,YACA,aACA,OACA,UACA,SACA,UACA,mBACA,eACA,kBACA,UACA,SACA,SACA,QACA,YACA,QACA,YACA,YACA,UACD,CAGM,IAAA,EAAA,KAAkB,CAIvB,yBAAoC,CAClC,MAAO,CAAC,GAAG,GAAuB,CAOpC,mBAAmB,EAAyB,CAC1C,IAAM,EAAmB,EAAO,aAAa,CAC7C,OAAO,GAAuB,KAAM,GAAS,EAAK,aAAa,GAAK,EAAiB,0BAf5E,CAAA,CAAA,EAAA,CCzBb,MAAMA,GAAAA,EAAAA,EAAAA,WAA0BC,EAAAA,SAAS,CAGlC,IAAA,EAAA,KAAuB,CAI5B,MAAM,iBAAiB,EAA8B,CACnD,GAAI,CACF,GAAM,CAAE,UAAW,MAAMD,EAAc,UAAW,CAChD,KACA,QACA,gBACA,kBACA,MACA,OACA,EACD,CAAC,CAEI,EAAS,KAAK,MAAM,EAAO,CAC3B,EAAW,OAAO,WAAW,EAAO,QAAQ,UAAY,IAAI,CAElE,GAAI,OAAO,MAAM,EAAS,EAAI,GAAY,EACxC,MAAU,MAAM,+BAA+B,IAAM,CAGvD,OAAO,QACA,EAAO,CACd,MAAU,MACR,oCAAoC,EAAI,IAAI,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAClG,CACE,MAAO,EACR,CACF,EAOL,MAAM,iBAAiB,EAA8B,CACnD,GAAI,CACF,GAAM,CAAE,UAAW,MAAMA,EAAc,UAAW,CAChD,KACA,QACA,gBACA,kBACA,MACA,OACA,EACD,CAAC,CAEI,EAAS,KAAK,MAAM,EAAO,CAC3B,EAAW,OAAO,WAAW,EAAO,QAAQ,UAAY,IAAI,CAElE,GAAI,OAAO,MAAM,EAAS,EAAI,GAAY,EACxC,MAAU,MAAM,+BAA+B,IAAM,CAGvD,OAAO,QACA,EAAO,CACd,MAAU,MACR,oCAAoC,EAAI,IAAI,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAClG,CACE,MAAO,EACR,CACF,EAOL,MAAM,mBAAmB,EAAyD,CAChF,GAAI,CACF,GAAM,CAAE,UAAW,MAAMA,EAAc,UAAW,CAChD,KACA,QACA,kBACA,MACA,gBACA,sBACA,MACA,OACA,EACD,CAAC,CAGI,EADS,KAAK,MAAM,EAAO,CACX,UAAU,GAEhC,GAAI,CAAC,GAAQ,OAAS,CAAC,GAAQ,OAC7B,MAAU,MAAM,6BAA6B,IAAM,CAGrD,MAAO,CACL,MAAO,OAAO,EAAO,MAAM,CAC3B,OAAQ,OAAO,EAAO,OAAO,CAC9B,OACM,EAAO,CACd,MAAU,MACR,sCAAsC,EAAI,IAAI,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GACpG,CAAE,MAAO,EAAO,CACjB,EAOL,MAAM,UAAU,EAA+B,CAC7C,GAAI,CAEF,OADA,MAAMA,EAAc,UAAW,CAAC,KAAM,QAAS,gBAAiB,kBAAmB,MAAO,OAAQ,EAAI,CAAC,CAChG,QACD,CACN,MAAO,6BAhHA,CAAA,CAAA,EAAA,CCPN,IAAA,EAAA,KAAoB,CACzB,cAAgD,KAEhD,MAAc,aAA+B,CAC3C,GAAI,CAAC,KAAK,cAAe,CACvB,IAAM,EAAcE,EAAAA,QAAK,QAAA,UAA6B,QAAQ,CAC9D,KAAK,eAAA,EAAA,GAAA,QAAuB,CAC1B,WAAYA,EAAAA,QAAK,QAAQ,EAAa,wBAAwB,CAC9D,UAAWA,EAAAA,QAAK,QAAQ,EAAa,SAAS,CAC9C,gBAAkB,IAAY,CAC5B,GAAG,EACH,QAAS,CACP,GAAG,EAAO,QACV,eAAgB,CACd,MAAO,CAAC,MAAO,OAAQ,MAAO,OAAO,CACtC,CACF,CACF,EACF,CAAC,CAEJ,OAAO,KAAK,cAGd,MAAM,OAAO,EAA+C,CAC1D,IAAM,EAAW,MAAM,KAAK,aAAa,CAgBzC,OARA,MAAA,EAAA,EAAA,aAAkB,CAChB,YAPkB,MAAA,EAAA,EAAA,mBAAwB,CAC1C,WACA,GAAI,EAAQ,cACZ,WAAY,EAAQ,WACrB,CAAC,CAIA,WACA,MAAO,EAAQ,OAAS,OACxB,eAAgB,EAAQ,WACxB,WAAY,EAAQ,WACrB,CAAC,CAEK,CAAE,WAAY,EAAQ,WAAY,CAG3C,MAAM,YAAY,EAAoD,CACpE,IAAM,EAAW,MAAM,KAAK,aAAa,CAiBzC,OATA,MAAA,EAAA,EAAA,aAAkB,CAChB,YAPkB,MAAA,EAAA,EAAA,mBAAwB,CAC1C,WACA,GAAI,EAAQ,cACZ,WAAY,EAAQ,WACrB,CAAC,CAIA,WACA,OAAQ,EAAQ,WAChB,MAAO,EAAQ,OAAS,EACxB,YAAa,EAAQ,aAAe,MACpC,WAAY,EAAQ,WACrB,CAAC,CAEK,CAAE,WAAY,EAAQ,WAAY,0BA9DhC,CAAA,CAAA,EAAA,CCUb,MAAM,GAAA,EAAA,EAAA,WAA0BC,EAAAA,SAAS,CAKnC,GAAmB,CACvB,WACA,YACA,WACA,SACA,UACA,aACA,UACA,cACA,YACA,WACD,CAeM,IAAA,EAAA,KAAuB,CAI5B,MAAM,aAAgC,CACpC,GAAI,CAEF,OADA,MAAM,EAAc,aAAc,CAAC,YAAY,CAAC,CACzC,QACD,CACN,MAAO,IAOX,MAAM,kBAAkB,EAAqD,CAC3E,GAAM,CAAE,OAAM,QAAQ,WAAY,QAAQ,EAAK,cAAe,EAGxD,EAAU,MAAMC,EAAAA,QAAG,QAAQC,EAAAA,QAAK,KAAKC,EAAAA,QAAG,QAAQ,CAAE,UAAU,CAAC,CAC7D,EAAeD,EAAAA,QAAK,KAAK,EAAS,YAAY,CAEpD,GAAI,CASF,OAPA,MAAMD,EAAAA,QAAG,UAAU,EAAc,EAAM,QAAQ,CAK/C,MAAM,EAAc,aAFP,CAAC,EAAc,EAAY,UAAW,EAAO,UAAW,EAAM,UAAU,CAAC,CAE/C,CAEhC,CAAE,aAAY,OACd,EAAO,CACd,MAAU,MAAM,iCAAiC,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAAI,CACzG,MAAO,EACR,CAAC,QACM,CAER,GAAI,CACF,MAAMA,EAAAA,QAAG,GAAG,EAAS,CAAE,UAAW,GAAM,MAAO,GAAM,CAAC,MAChD,IASZ,YAAuB,CACrB,MAAO,CAAC,GAAG,GAAiB,0BApDnB,CAAA,CAAA,EAAA,CC9BN,IAAA,EAAA,KAAwB,CAM7B,QAAkB,EAA8B,CAC9C,MAAO,CACL,QAAS,CAAC,CAAE,KAAM,OAAQ,OAAM,CAAC,CAClC,CAQH,YAAsB,EAA+B,CACnD,MAAO,CACL,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,KAAK,UAAU,EAAM,KAAM,EAAE,CAAE,CAAC,CACjE,CAQH,MAAgB,EAAiC,CAC/C,MAAO,CACL,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,UAAU,IAAW,CAAC,CACtD,QAAS,GACV,CAQH,MAAgB,YAAe,EAAmD,CAChF,GAAI,CACF,OAAO,MAAM,GAAI,OACV,EAAK,CACZ,OAAO,KAAK,MAAM,aAAe,MAAQ,EAAI,QAAU,gBAAgB,2BA7ChE,CAAA,CAAA,EAAA,CCab,MAAa,EAAQ,CACnB,cAAe,OAAO,IAAI,gBAAgB,CAC1C,iBAAkB,OAAO,IAAI,mBAAmB,CAChD,eAAgB,OAAO,IAAI,iBAAiB,CAC5C,YAAa,OAAO,IAAI,cAAc,CACtC,iBAAkB,OAAO,IAAI,mBAAmB,CAChD,KAAM,OAAO,IAAI,OAAO,CACzB,sKCjCD,MAAa,EAAkCG,EAAAA,EAAE,OAAO,CACtD,SAAUA,EAAAA,EACP,MACCA,EAAAA,EAAE,OAAO,CACP,KAAMA,EAAAA,EAAE,QAAQ,CAChB,QAASA,EAAAA,EAAE,QAAQ,CACnB,MAAOA,EAAAA,EAAE,QAAQ,CAClB,CAAC,CACH,CACA,SAAS,yDAAyD,CACrE,UAAWA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAAC,SAAS,0EAA0E,CACrH,CAAC,CAKK,IAAA,EAAA,cAAmC,CAAoD,eAC5F,OAAgB,UAAY,oBAE5B,YAAY,EAAsE,CAChF,OAAO,CADyC,KAAA,eAAA,EAIlD,gBAA6C,CAC3C,OAAO,EAGT,eAAgC,CAC9B,MAAO,CACL,KAAA,EAA2B,UAC3B,YAAa,2EACb,YAAaA,EAAAA,EAAE,aAAa,EAAiC,CAAE,OAAQ,SAAU,CAAC,CACnF,CAGH,MAAM,QAAQ,EAA4D,CACxE,GAAI,CAEF,GAAM,CAAE,WAAU,YAAY,KADhB,EAAgC,MAAM,EAAS,CAEvD,EAA0B,EAAS,IAAK,IAAO,CACnD,KAAM,EAAE,KACR,QAAS,EAAE,QACX,MAAO,EAAE,MACV,EAAE,CACG,EAAS,KAAK,eAAe,qBAAqB,EAAc,EAAU,CAChF,OAAO,KAAK,YAAY,EAAO,OACxB,EAAO,CACd,OAAO,KAAK,MAAM,aAAiB,MAAQ,EAAM,QAAU,gBAAgB,6BAhCpE,kBAIS,EAAM,eAAe,CAAA,mFCnB3C,MAAa,EAAmCC,EAAAA,EAAE,OAAO,CACvD,KAAMA,EAAAA,EAAE,QAAQ,CAAC,SAAS,4BAA4B,CACtD,MAAOA,EAAAA,EACJ,QAAQ,CACR,UAAU,CACV,SACC,+IACD,CACH,MAAOA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAAC,SAAS,yCAAyC,CAC/E,WAAYA,EAAAA,EAAE,QAAQ,CAAC,SAAS,2CAA2C,CAC5E,CAAC,CAKK,IAAA,EAAA,cAAoC,CAAqD,eAC9F,OAAgB,UAAY,qBAE5B,YAAY,EAA4E,CACtF,OAAO,CAD2C,KAAA,iBAAA,EAIpD,gBAA6C,CAC3C,OAAO,EAGT,eAAgC,CAC9B,MAAO,CACL,KAAA,EAA4B,UAC5B,YAAa,mEACb,YAAaA,EAAAA,EAAE,aAAa,EAAkC,CAAE,OAAQ,SAAU,CAAC,CACpF,CAGH,MAAM,QAAQ,EAA4D,CACxE,GAAI,CACF,IAAM,EAAQ,EAAiC,MAAM,EAAS,CAE9D,GAAI,CADgB,MAAM,KAAK,iBAAiB,aAAa,CAE3D,OAAO,KAAK,MAAM,uEAAuE,CAG3F,IAAM,EAAS,MAAM,KAAK,iBAAiB,kBAAkB,CAC3D,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,MAAO,EAAM,MACb,WAAY,EAAM,WACnB,CAAC,CAEF,OAAO,KAAK,YAAY,EAAO,OACxB,EAAO,CACd,OAAO,KAAK,MAAM,aAAiB,MAAQ,EAAM,QAAU,gBAAgB,6BArCpE,kBAIS,EAAM,iBAAiB,CAAA,mFClB7C,MAAa,EAA8BC,EAAAA,EAAE,OAAO,CAClD,IAAKA,EAAAA,EAAE,QAAQ,CAAC,SAAS,yBAAyB,CAClD,KAAMA,EAAAA,EAAE,KAAK,CAAC,QAAS,QAAQ,CAAC,CAAC,UAAU,CAAC,SAAS,8BAA8B,CACpF,CAAC,CAKK,IAAA,EAAA,cAA+B,CAAgD,eACpF,OAAgB,UAAY,iBAE5B,YAAY,EAA4E,CACtF,OAAO,CAD2C,KAAA,iBAAA,EAIpD,gBAA6C,CAC3C,OAAO,EAGT,eAAgC,CAC9B,MAAO,CACL,KAAA,EAAuB,UACvB,YAAa,wEACb,YAAaA,EAAAA,EAAE,aAAa,EAA6B,CAAE,OAAQ,SAAU,CAAC,CAC/E,CAGH,MAAM,QAAQ,EAA4D,CACxE,GAAI,CAEF,GAAM,CAAE,MAAK,OAAO,SADN,EAA4B,MAAM,EAAS,CAEnD,EAAY,MAAM,KAAK,iBAAiB,UAAU,EAAI,CAE5D,GAAI,IAAS,QAAS,CACpB,IAAM,EAAW,MAAM,KAAK,iBAAiB,iBAAiB,EAAI,CAClE,OAAO,KAAK,YAAY,CAAE,MAAK,KAAM,QAAS,WAAU,YAAW,CAAC,CAGtE,GAAM,CAAC,EAAU,GAAc,MAAM,QAAQ,IAAI,CAC/C,KAAK,iBAAiB,iBAAiB,EAAI,CAC3C,KAAK,iBAAiB,mBAAmB,EAAI,CAC9C,CAAC,CAEF,OAAO,KAAK,YAAY,CACtB,MACA,KAAM,QACN,WACA,MAAO,EAAW,MAClB,OAAQ,EAAW,OACnB,YACD,CAAC,OACK,EAAO,CACd,OAAO,KAAK,MAAM,aAAiB,MAAQ,EAAM,QAAU,gBAAgB,6BA7CpE,kBAIS,EAAM,iBAAiB,CAAA,mFCX7C,MAAa,EAA2BC,EAAAA,EAAE,OAAO,CAC/C,WAAYA,EAAAA,EAAE,QAAQ,CAAC,SAAS,+BAA+B,CAChE,CAAC,CAKK,IAAA,EAAA,cAA4B,CAA6C,eAC9E,OAAgB,UAAY,aAE5B,YAAY,EAAsE,CAChF,OAAO,CADyC,KAAA,eAAA,EAIlD,gBAA6C,CAC3C,OAAO,EAGT,eAAgC,CAC9B,MAAO,CACL,KAAA,EAAoB,UACpB,YAAa,mFACb,YAAaA,EAAAA,EAAE,aAAa,EAA0B,CAAE,OAAQ,SAAU,CAAC,CAC5E,CAGH,MAAM,QAAQ,EAA4D,CACxE,GAAI,CACF,IAAM,EAAQ,EAAyB,MAAM,EAAS,CAChD,EAAW,KAAK,eAAe,SAAS,EAAM,WAAW,CAC/D,OAAO,KAAK,YAAY,CAAE,aAAc,EAAS,OAAQ,WAAU,CAAC,OAC7D,EAAO,CACd,OAAO,KAAK,MAAM,aAAiB,MAAQ,EAAM,QAAU,gBAAgB,6BA1BpE,kBAIS,EAAM,eAAe,CAAA,iFCZ3C,MAAa,EAAkCC,EAAAA,EAAE,OAAO,EAAE,CAAC,CAKpD,IAAA,EAAA,cAAmC,CAAoD,eAC5F,OAAgB,UAAY,oBAE5B,gBAA6C,CAC3C,OAAO,EAGT,eAAgC,CAC9B,MAAO,CACL,KAAA,EAA2B,UAC3B,YAAa,2FACb,YAAaA,EAAAA,EAAE,aAAa,EAAiC,CAAE,OAAQ,SAAU,CAAC,CACnF,CAGH,MAAM,SAAmC,CACvC,IAAM,EAAe,CACnB,CACE,GAAI,OACJ,YAAa,kCACb,MAAO,KACP,OAAQ,KACR,IAAK,GACL,iBAAkB,KAClB,aAAc,CAAE,MAAO,EAAE,CAAE,YAAa,EAAG,gBAAiB,UAAW,CACxE,CACD,CACE,GAAI,WACJ,YAAa,iEACb,MAAO,KACP,OAAQ,KACR,IAAK,GACL,iBAAkB,KAClB,aAAc,CAAE,MAAO,EAAE,CAAE,YAAa,EAAG,gBAAiB,UAAW,CACxE,CACD,CACE,GAAI,SACJ,YAAa,2CACb,MAAO,KACP,OAAQ,KACR,IAAK,GACL,iBAAkB,KAClB,aAAc,CAAE,MAAO,EAAE,CAAE,YAAa,EAAG,gBAAiB,UAAW,CACxE,CACF,CAED,OAAO,KAAK,YAAY,CAAE,iBAAkB,EAAa,OAAQ,eAAc,CAAC,4BA/CvE,CAAA,CAAA,EAAA,SCFb,MAAa,EAA8BC,EAAAA,EAAE,OAAO,CAClD,cAAeA,EAAAA,EAAE,QAAQ,CAAC,SAAS,+DAA+D,CAClG,WAAYA,EAAAA,EAAE,OAAOA,EAAAA,EAAE,QAAQ,CAAEA,EAAAA,EAAE,SAAS,CAAC,CAAC,SAAS,wCAAwC,CAC/F,WAAYA,EAAAA,EAAE,QAAQ,CAAC,SAAS,0CAA0C,CAC1E,MAAOA,EAAAA,EAAE,QAAQ,CAAC,UAAU,CAAC,SAAS,sCAAsC,CAC5E,YAAaA,EAAAA,EAAE,KAAK,CAAC,MAAO,OAAO,CAAC,CAAC,UAAU,CAAC,SAAS,8BAA8B,CACxF,CAAC,CAKK,IAAA,EAAA,cAA+B,CAAgD,eACpF,OAAgB,UAAY,gBAE5B,YAAY,EAAmE,CAC7E,OAAO,CADwC,KAAA,cAAA,EAIjD,gBAA6C,CAC3C,OAAO,EAGT,eAAgC,CAC9B,MAAO,CACL,KAAA,EAAuB,UACvB,YAAa,8EACb,YAAaA,EAAAA,EAAE,aAAa,EAA6B,CAAE,OAAQ,SAAU,CAAC,CAC/E,CAGH,MAAM,QAAQ,EAA4D,CACxE,GAAI,CACF,IAAM,EAAQ,EAA4B,MAAM,EAAS,CACnD,EAA8B,CAClC,cAAe,EAAM,cACrB,WAAY,EAAM,WAClB,WAAY,EAAM,WAClB,MAAO,EAAM,MACb,YAAa,EAAM,YACpB,CACK,EAAS,MAAM,KAAK,cAAc,YAAY,EAAQ,CAC5D,OAAO,KAAK,YAAY,EAAO,OACxB,EAAO,CACd,OAAO,KAAK,MAAM,aAAiB,MAAQ,EAAM,QAAU,gBAAgB,6BAjCpE,kBAIS,EAAM,cAAc,CAAA,oFCd1C,MAAa,EAA6BC,EAAAA,EAAE,OAAO,CACjD,cAAeA,EAAAA,EAAE,QAAQ,CAAC,SAAS,+DAA+D,CAClG,WAAYA,EAAAA,EAAE,OAAOA,EAAAA,EAAE,QAAQ,CAAEA,EAAAA,EAAE,SAAS,CAAC,CAAC,SAAS,wCAAwC,CAC/F,WAAYA,EAAAA,EAAE,QAAQ,CAAC,SAAS,0CAA0C,CAC1E,MAAOA,EAAAA,EAAE,KAAK,CAAC,OAAQ,OAAQ,MAAO,MAAM,CAAC,CAAC,UAAU,CAAC,SAAS,8BAA8B,CACjG,CAAC,CAKK,IAAA,EAAA,cAA8B,CAA+C,eAClF,OAAgB,UAAY,eAE5B,YAAY,EAAmE,CAC7E,OAAO,CADwC,KAAA,cAAA,EAIjD,gBAA6C,CAC3C,OAAO,EAGT,eAAgC,CAC9B,MAAO,CACL,KAAA,EAAsB,UACtB,YAAa,gDACb,YAAaA,EAAAA,EAAE,aAAa,EAA4B,CAAE,OAAQ,SAAU,CAAC,CAC9E,CAGH,MAAM,QAAQ,EAA4D,CACxE,GAAI,CACF,IAAM,EAAQ,EAA2B,MAAM,EAAS,CAClD,EAAyB,CAC7B,cAAe,EAAM,cACrB,WAAY,EAAM,WAClB,WAAY,EAAM,WAClB,MAAO,EAAM,MACd,CACK,EAAS,MAAM,KAAK,cAAc,OAAO,EAAQ,CACvD,OAAO,KAAK,YAAY,EAAO,OACxB,EAAO,CACd,OAAO,KAAK,MAAM,aAAiB,MAAQ,EAAM,QAAU,gBAAgB,6BAhCpE,kBAIS,EAAM,cAAc,CAAA,6ECU1C,MAAa,GAAiB,IAAIC,EAAAA,gBAAiB,GAAwC,CACzF,EAAQ,KAAK,EAAM,cAAc,CAAC,GAAG,EAAc,CAAC,kBAAkB,CACtE,EAAQ,KAAK,EAAM,iBAAiB,CAAC,GAAG,EAAiB,CAAC,kBAAkB,CAC5E,EAAQ,KAAK,EAAM,eAAe,CAAC,GAAG,EAAe,CAAC,kBAAkB,CACxE,EAAQ,KAAK,EAAM,YAAY,CAAC,GAAG,EAAY,CAAC,kBAAkB,CAClE,EAAQ,KAAK,EAAM,iBAAiB,CAAC,GAAG,EAAiB,CAAC,kBAAkB,EAC5E,CAKW,GAAc,IAAIA,EAAAA,gBAAiB,GAAwC,CACtF,EAAQ,KAAK,EAAM,KAAK,CAAC,GAAG,EAAgB,CAAC,kBAAkB,CAC/D,EAAQ,KAAK,EAAM,KAAK,CAAC,GAAG,EAAqB,CAAC,kBAAkB,CACpE,EAAQ,KAAK,EAAM,KAAK,CAAC,GAAG,EAAiB,CAAC,kBAAkB,CAChE,EAAQ,KAAK,EAAM,KAAK,CAAC,GAAG,EAAiB,CAAC,kBAAkB,CAChE,EAAQ,KAAK,EAAM,KAAK,CAAC,GAAG,EAAqB,CAAC,kBAAkB,CACpE,EAAQ,KAAK,EAAM,KAAK,CAAC,GAAG,EAAc,CAAC,kBAAkB,CAC7D,EAAQ,KAAK,EAAM,KAAK,CAAC,GAAG,EAAsB,CAAC,kBAAkB,EACrE,CAKF,SAAgB,IAA6B,CAC3C,IAAM,EAAY,IAAIC,EAAAA,UAEtB,OADA,EAAU,KAAK,GAAgB,GAAY,CACpC,EClDT,SAAgB,GAAa,EAA8B,CACzD,IAAM,EAAS,IAAIC,GAAAA,OAAO,CAAE,KAAM,mBAAoB,QAAS,QAAS,CAAE,CAAE,aAAc,CAAE,MAAO,EAAE,CAAE,CAAE,CAAC,CAEpG,EAAQ,EAAU,OAAa,EAAM,KAAK,CAC1C,EAAU,IAAI,IACpB,IAAK,IAAM,KAAQ,EACjB,EAAQ,IAAI,EAAK,eAAe,CAAC,KAAM,EAAK,CAkC9C,OA/BA,EAAO,kBAAkBC,GAAAA,uBAAwB,UAAa,CAC5D,MAAO,EAAM,IAAK,GAAM,EAAE,eAAe,CAAC,CAC3C,EAAE,CAEH,EAAO,kBAAkBC,GAAAA,sBAAuB,KAAO,IAAY,CACjE,GAAM,CAAE,OAAM,UAAW,GAAS,EAAQ,OACpC,EAAO,EAAQ,IAAI,EAAK,CAC9B,GAAI,CAAC,EACH,MAAO,CACL,QAAS,CAAC,CAAE,KAAM,OAAiB,KAAM,iBAAiB,IAAQ,CAAC,CACnE,QAAS,GACV,CAGH,IAAM,GAAA,EAAA,GAAA,YAAqB,GAAQ,EAAE,CAAE,EAAK,gBAAgB,CAAC,CAE7D,GAAI,CACF,OAAO,MAAM,EAAK,QAAQ,EAAQ,OAC3B,EAAO,CACd,GAAI,aAAiBC,EAAAA,EAAE,SACrB,MAAO,CACL,QAAS,CACP,CAAE,KAAM,OAAiB,MAAA,EAAA,GAAA,gBAAqB,EAAO,CAAE,WAAY,EAAM,OAAQ,EAAK,gBAAgB,CAAE,CAAC,CAAE,CAC5G,CACD,QAAS,GACV,CAEH,MAAM,IAER,CAEK,ECxBT,IAAa,GAAb,KAAmC,CACjC,OACA,UAAiD,KAEjD,YAAY,EAAgB,CAC1B,KAAK,OAAS,EAGhB,MAAM,OAAuB,CAC3B,KAAK,UAAY,IAAIC,GAAAA,qBACrB,MAAM,KAAK,OAAO,QAAQ,KAAK,UAAU,CACzC,QAAQ,MAAM,2CAA2C,CAG3D,MAAM,MAAsB,CAC1B,AAEE,KAAK,aADL,MAAM,KAAK,UAAU,OAAO,CACX"}