@studiomeyer/mcp-video 1.0.0 → 1.0.1

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 (121) hide show
  1. package/.github/FUNDING.yml +2 -0
  2. package/.github/PULL_REQUEST_TEMPLATE.md +9 -0
  3. package/.github/dependabot.yml +46 -0
  4. package/.github/workflows/ci.yml +2 -2
  5. package/CHANGELOG.md +157 -0
  6. package/CODE_OF_CONDUCT.md +7 -0
  7. package/ECOSYSTEM.md +35 -0
  8. package/README.md +26 -2
  9. package/SECURITY.md +11 -0
  10. package/dist/handlers/smart-screenshot.js +10 -3
  11. package/dist/handlers/smart-screenshot.js.map +1 -1
  12. package/dist/handlers/tts.js +6 -2
  13. package/dist/handlers/tts.js.map +1 -1
  14. package/dist/handlers/video.js +17 -7
  15. package/dist/handlers/video.js.map +1 -1
  16. package/dist/lib/error-sanitizer.d.ts +26 -0
  17. package/dist/lib/error-sanitizer.js +58 -0
  18. package/dist/lib/error-sanitizer.js.map +1 -0
  19. package/dist/lib/error-sanitizer.test.d.ts +1 -0
  20. package/dist/lib/error-sanitizer.test.js +73 -0
  21. package/dist/lib/error-sanitizer.test.js.map +1 -0
  22. package/dist/lib/ffmpeg-bin.d.ts +64 -0
  23. package/dist/lib/ffmpeg-bin.js +96 -0
  24. package/dist/lib/ffmpeg-bin.js.map +1 -0
  25. package/dist/lib/ffmpeg-bin.test.d.ts +18 -0
  26. package/dist/lib/ffmpeg-bin.test.js +169 -0
  27. package/dist/lib/ffmpeg-bin.test.js.map +1 -0
  28. package/dist/lib/ffmpeg-run.d.ts +43 -0
  29. package/dist/lib/ffmpeg-run.js +67 -0
  30. package/dist/lib/ffmpeg-run.js.map +1 -0
  31. package/dist/lib/ffmpeg-run.test.d.ts +1 -0
  32. package/dist/lib/ffmpeg-run.test.js +66 -0
  33. package/dist/lib/ffmpeg-run.test.js.map +1 -0
  34. package/dist/lib/ffmpeg-safety.d.ts +37 -0
  35. package/dist/lib/ffmpeg-safety.js +67 -0
  36. package/dist/lib/ffmpeg-safety.js.map +1 -0
  37. package/dist/lib/ffmpeg-safety.test.d.ts +1 -0
  38. package/dist/lib/ffmpeg-safety.test.js +72 -0
  39. package/dist/lib/ffmpeg-safety.test.js.map +1 -0
  40. package/dist/lib/temp-dir.d.ts +24 -0
  41. package/dist/lib/temp-dir.js +53 -0
  42. package/dist/lib/temp-dir.js.map +1 -0
  43. package/dist/lib/temp-dir.test.d.ts +1 -0
  44. package/dist/lib/temp-dir.test.js +68 -0
  45. package/dist/lib/temp-dir.test.js.map +1 -0
  46. package/dist/lib/url-guard.d.ts +41 -0
  47. package/dist/lib/url-guard.js +134 -0
  48. package/dist/lib/url-guard.js.map +1 -0
  49. package/dist/lib/url-guard.test.d.ts +10 -0
  50. package/dist/lib/url-guard.test.js +231 -0
  51. package/dist/lib/url-guard.test.js.map +1 -0
  52. package/dist/server.js +9 -4
  53. package/dist/server.js.map +1 -1
  54. package/dist/tools/engine/audio-mixer.js +5 -20
  55. package/dist/tools/engine/audio-mixer.js.map +1 -1
  56. package/dist/tools/engine/audio.js +3 -19
  57. package/dist/tools/engine/audio.js.map +1 -1
  58. package/dist/tools/engine/beat-sync.js +7 -30
  59. package/dist/tools/engine/beat-sync.js.map +1 -1
  60. package/dist/tools/engine/capture.js +7 -0
  61. package/dist/tools/engine/capture.js.map +1 -1
  62. package/dist/tools/engine/chroma-key.js +2 -11
  63. package/dist/tools/engine/chroma-key.js.map +1 -1
  64. package/dist/tools/engine/concat.js +2 -11
  65. package/dist/tools/engine/concat.js.map +1 -1
  66. package/dist/tools/engine/editing.js +12 -35
  67. package/dist/tools/engine/editing.js.map +1 -1
  68. package/dist/tools/engine/encoder.js +2 -12
  69. package/dist/tools/engine/encoder.js.map +1 -1
  70. package/dist/tools/engine/lut-presets.js +2 -11
  71. package/dist/tools/engine/lut-presets.js.map +1 -1
  72. package/dist/tools/engine/narrated-video.js +30 -39
  73. package/dist/tools/engine/narrated-video.js.map +1 -1
  74. package/dist/tools/engine/smart-screenshot.js +7 -0
  75. package/dist/tools/engine/smart-screenshot.js.map +1 -1
  76. package/dist/tools/engine/social-format.js +2 -11
  77. package/dist/tools/engine/social-format.js.map +1 -1
  78. package/dist/tools/engine/template-renderer.js +2 -11
  79. package/dist/tools/engine/template-renderer.js.map +1 -1
  80. package/dist/tools/engine/text-animations.js +2 -11
  81. package/dist/tools/engine/text-animations.js.map +1 -1
  82. package/dist/tools/engine/text-overlay.js +2 -11
  83. package/dist/tools/engine/text-overlay.js.map +1 -1
  84. package/dist/tools/engine/tts.js +11 -6
  85. package/dist/tools/engine/tts.js.map +1 -1
  86. package/dist/tools/engine/voice-effects.js +3 -20
  87. package/dist/tools/engine/voice-effects.js.map +1 -1
  88. package/package.json +6 -6
  89. package/src/handlers/smart-screenshot.ts +8 -3
  90. package/src/handlers/tts.ts +6 -2
  91. package/src/handlers/video.ts +14 -7
  92. package/src/lib/error-sanitizer.test.ts +88 -0
  93. package/src/lib/error-sanitizer.ts +66 -0
  94. package/src/lib/ffmpeg-bin.test.ts +192 -0
  95. package/src/lib/ffmpeg-bin.ts +111 -0
  96. package/src/lib/ffmpeg-run.test.ts +76 -0
  97. package/src/lib/ffmpeg-run.ts +110 -0
  98. package/src/lib/ffmpeg-safety.test.ts +88 -0
  99. package/src/lib/ffmpeg-safety.ts +79 -0
  100. package/src/lib/temp-dir.test.ts +75 -0
  101. package/src/lib/temp-dir.ts +58 -0
  102. package/src/lib/url-guard.test.ts +261 -0
  103. package/src/lib/url-guard.ts +143 -0
  104. package/src/server.ts +10 -5
  105. package/src/tools/engine/audio-mixer.ts +8 -21
  106. package/src/tools/engine/audio.ts +6 -21
  107. package/src/tools/engine/beat-sync.ts +10 -31
  108. package/src/tools/engine/capture.ts +8 -0
  109. package/src/tools/engine/chroma-key.ts +2 -11
  110. package/src/tools/engine/concat.ts +2 -11
  111. package/src/tools/engine/editing.ts +17 -34
  112. package/src/tools/engine/encoder.ts +2 -12
  113. package/src/tools/engine/lut-presets.ts +2 -11
  114. package/src/tools/engine/narrated-video.ts +26 -38
  115. package/src/tools/engine/smart-screenshot.ts +8 -0
  116. package/src/tools/engine/social-format.ts +2 -11
  117. package/src/tools/engine/template-renderer.ts +2 -11
  118. package/src/tools/engine/text-animations.ts +2 -11
  119. package/src/tools/engine/text-overlay.ts +2 -11
  120. package/src/tools/engine/tts.ts +15 -6
  121. package/src/tools/engine/voice-effects.ts +3 -17
@@ -0,0 +1,2 @@
1
+ github: studiomeyer-io
2
+ custom: ["https://studiomeyer.io"]
@@ -0,0 +1,9 @@
1
+ ## What does this PR do?
2
+
3
+ ## How to test
4
+
5
+ ## Checklist
6
+
7
+ - [ ] Tests pass
8
+ - [ ] Build succeeds
9
+ - [ ] No breaking changes
@@ -0,0 +1,46 @@
1
+ version: 2
2
+ # Conservative policy (S992):
3
+ # - Weekly Monday 06:00 CET, max 5 open PRs
4
+ # - Patches + minors auto-grouped into one PR
5
+ # - Major bumps IGNORED for routine updates (require manual review)
6
+ # - Security advisories still auto-applied (security fixes bypass `ignore`)
7
+ # - GitHub Actions monthly, all bumps grouped (Action versions usually safe)
8
+ updates:
9
+ - package-ecosystem: "npm"
10
+ directory: "/"
11
+ schedule:
12
+ interval: "weekly"
13
+ day: "monday"
14
+ time: "06:00"
15
+ timezone: "Europe/Berlin"
16
+ open-pull-requests-limit: 5
17
+ commit-message:
18
+ prefix: "deps"
19
+ prefix-development: "deps-dev"
20
+ include: "scope"
21
+ labels:
22
+ - "dependencies"
23
+ groups:
24
+ patch-and-minor:
25
+ update-types:
26
+ - "patch"
27
+ - "minor"
28
+ ignore:
29
+ - dependency-name: "*"
30
+ update-types:
31
+ - "version-update:semver-major"
32
+
33
+ - package-ecosystem: "github-actions"
34
+ directory: "/"
35
+ schedule:
36
+ interval: "monthly"
37
+ open-pull-requests-limit: 3
38
+ commit-message:
39
+ prefix: "ci"
40
+ include: "scope"
41
+ labels:
42
+ - "ci"
43
+ groups:
44
+ actions:
45
+ patterns:
46
+ - "*"
@@ -14,10 +14,10 @@ jobs:
14
14
  node-version: [18, 20, 22]
15
15
 
16
16
  steps:
17
- - uses: actions/checkout@v4
17
+ - uses: actions/checkout@v6
18
18
 
19
19
  - name: Use Node.js ${{ matrix.node-version }}
20
- uses: actions/setup-node@v4
20
+ uses: actions/setup-node@v6
21
21
  with:
22
22
  node-version: ${{ matrix.node-version }}
23
23
 
package/CHANGELOG.md CHANGED
@@ -5,6 +5,163 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## Unreleased
9
+
10
+ ### Fixed — Cross-platform ffmpeg detection (issue #11, 2026-05-07)
11
+
12
+ - **Windows startup failure**: `checkDependencies()` previously called
13
+ `execFileSync('which', [bin])` to verify ffmpeg/ffprobe presence. `which`
14
+ is a Unix-only command, so the check failed on Windows even when ffmpeg
15
+ was on PATH and runnable. Reported by [@Firelods](https://github.com/Firelods)
16
+ in [#11](https://github.com/studiomeyer-io/mcp-video/issues/11).
17
+ - **`FFMPEG_PATH` / `FFPROBE_PATH` env overrides now actually work at
18
+ runtime**, not only in error-message text. The previous implementation
19
+ spawned `execFile('ffmpeg', ...)` directly inside `ffmpeg-run.ts`, so
20
+ setting the env var was ignored at every spawn site. The original
21
+ reporter confirmed this on the issue thread.
22
+
23
+ **New module:** `src/lib/ffmpeg-bin.ts` exposes `resolveFfmpegBin()` (env
24
+ override → bare-name fallback) and `assertFfmpegBinAvailable()` (probe via
25
+ `<bin> -version`, no shell, `windowsHide: true`). Both `server.ts`
26
+ startup-check and `ffmpeg-run.ts` runtime spawn now go through the same
27
+ helper, so the override stays consistent.
28
+
29
+ 15 new regression tests + the original test suite (104/104 green); typecheck
30
+ clean. Thanks to [@Firelods](https://github.com/Firelods) for the detailed
31
+ environment + reproduction.
32
+
33
+ ### Security — Round-4 OSS-Sweep (2026-04-24)
34
+
35
+ - **`server.ts` dependency check**: replaced `execSync(\`which ${bin}\`)`
36
+ shell-interpolation with `execFileSync('which', [bin], ...)`. The input
37
+ was already a hardcoded literal array, so no real exposure today — this
38
+ is defense-in-depth so a future refactor that makes the list
39
+ config-driven can't turn into a command-injection sink. 89/89 tests
40
+ still green, tsc clean.
41
+
42
+ ### Security — hardening sweep (Session 840, 2026-04-21)
43
+
44
+ Four of the five Session 837 Must-Fix items plus the three Session 839
45
+ follow-up findings (DNS-rebinding, ffmpeg hop-2 SSRF, redirect following)
46
+ are now addressed in one coherent patch. 42 new tests; 89/89 green.
47
+
48
+ **Post-review follow-through (Session 840 Agent Critic):**
49
+
50
+ A full triple-agent review (Analyst + Critic + Research) caught one
51
+ remaining bypass: every engine still had a LOCAL `runFfmpeg` function that
52
+ delegated to the safe runner, but five engines (`audio.ts`,
53
+ `audio-mixer.ts`, `beat-sync.ts`, `editing.ts`, `voice-effects.ts`) ALSO
54
+ called `ffprobe` directly via `execFile('ffprobe', ...)` — and `ffprobe`
55
+ follows HLS/DASH playlists just like `ffmpeg` does. The "just probe it
56
+ first" path was a complete SSRF bypass.
57
+
58
+ - `src/lib/ffmpeg-run.ts` gains `runFfprobe(args, opts, resolver)` —
59
+ same discipline as `runFfmpeg`: prepend `-protocol_whitelist`, sanitize
60
+ stderr, label for rejection messages. Every `ffprobe` call across the
61
+ five engines now routes through it.
62
+ - `narrated-video.ts` internal `runFfmpeg` helper now delegates to the
63
+ central safe runner (was still using its own `execFile` wrapper from
64
+ the first pass of the sweep).
65
+ - All thirteen engines drop their `import { execFile } from 'child_process'`
66
+ now that the last consumer is gone.
67
+
68
+ **`src/lib/url-guard.ts` — DNS resolution + IPv6-mapped IPv4 + final URL check**
69
+
70
+ - New async `resolveAndGuardUrl(raw)` uses `dns.lookup(family:0)` and
71
+ rejects any hostname that resolves to a loopback / RFC1918 / link-local
72
+ IP. Catches the naive DNS-rebinding case where a public hostname
73
+ resolves to `127.0.0.1` at lookup time.
74
+ - New `guardFinalUrl(raw)` is called AFTER `page.goto()` on the URL the
75
+ browser actually landed on — 302 redirects to internal metadata
76
+ endpoints are now blocked at the post-navigation checkpoint.
77
+ - `BLOCKED_HOST_PATTERNS` now matches the full `fc00::/7` ULA range
78
+ (previously only `fc00:`, missing `fd00:`-`fdff:`).
79
+ - IPv6-mapped IPv4 (`::ffff:127.0.0.1`, `::ffff:7f00:1`, and
80
+ `0:0:0:0:0:ffff:7f00:1` — all three forms the WHATWG URL parser
81
+ can produce) are now explicitly blocked.
82
+
83
+ **`src/lib/ffmpeg-safety.ts` — protocol whitelist + flag-injection guard (NEW)**
84
+
85
+ - `buildFfmpegArgs(args, set)` prepends `-protocol_whitelist` to every
86
+ ffmpeg invocation. Three protocol sets cover all internal use:
87
+ `local-only` (file+pipe+crypto+cache+fd), `https-input` (adds
88
+ https+tls+tcp for one-top-level-URL), `https-and-hls` (adds hls for
89
+ master+segment playback). `http://` is in none of them — forces TLS
90
+ for any network-bound ffmpeg read.
91
+ - `validateFfmpegPath(p, label)` rejects values starting with `-`
92
+ (turns `-i /etc/passwd ...` smuggled as "filename" into an error) and
93
+ null-byte injections. `validateFfmpegPaths(args, indices)` applies
94
+ the check to specific user-controlled positions in an args array.
95
+
96
+ **`src/lib/ffmpeg-run.ts` — central ffmpeg runner (NEW)**
97
+
98
+ - Every engine (audio, audio-mixer, beat-sync, chroma-key, concat,
99
+ editing, encoder, lut-presets, narrated-video, social-format,
100
+ template-renderer, text-animations, text-overlay, voice-effects —
101
+ 14 call sites across 13 files) now routes through `runFfmpeg()`,
102
+ which injects `-protocol_whitelist` + sanitizes stderr before it
103
+ reaches logs or thrown errors. `resolver: 'stderr'` lets beat-sync
104
+ still pick up its filter-info output.
105
+
106
+ **`src/lib/temp-dir.ts` — safe mkdtemp helper (NEW)**
107
+
108
+ - `withTempDir(prefix, fn)` wraps `fs.mkdtemp` + try/finally cleanup so
109
+ a crash or throw inside `fn` still removes the directory. Replaces
110
+ the predictable `/tmp/narrated-video-${Date.now()}` pattern in
111
+ `tools/engine/narrated-video.ts` that raced when two invocations
112
+ hit the same millisecond and leaked on SIGTERM.
113
+
114
+ **`src/lib/error-sanitizer.ts` — upstream-API secret scrub (NEW)**
115
+
116
+ - `sanitizeErrorMessage(raw, opts)` strips seven secret shapes before
117
+ ffmpeg stderr or TTS-provider error bodies land in logs or thrown
118
+ errors: Bearer tokens, `xi-api-key` / `x-api-key` header values,
119
+ OpenAI `sk-` keys, AWS `AKIA...`, generic `"api_key"` JSON fields,
120
+ Authorization headers (with a negative lookahead so already-redacted
121
+ Bearer markers survive), and signed-URL `X-Amz-Signature` params.
122
+ Length-capped at 300 chars by default.
123
+
124
+ **`src/handlers/tts.ts` — missing SSRF guard on `create_narrated_video`**
125
+
126
+ - Session 837 wrapped three video handlers in `guardUrl` but skipped
127
+ `create_narrated_video`. That handler now guards `args.url` the same
128
+ way as the other three — Playwright + ffmpeg no longer see
129
+ unvalidated URLs.
130
+
131
+ **`src/tools/engine/capture.ts` + `smart-screenshot.ts` — post-redirect guard**
132
+
133
+ - Each `page.goto()` is now followed by `guardFinalUrl(page.url())`.
134
+ If the browser followed a 302 to an internal host (classic SSRF
135
+ escalation path via open redirect on a public domain), the engine
136
+ throws before rendering frames or saving screenshots.
137
+
138
+ **`src/tools/engine/narrated-video.ts` — withTempDir + safe ffmpeg**
139
+
140
+ - `createNarratedVideo` now uses `withTempDir` instead of the
141
+ predictable timestamp-based path. Its inner `runFfmpeg` helper routes
142
+ through the central safe runner. Concat list generation escapes
143
+ single quotes in file names before writing the concat list file.
144
+
145
+ ### Added — url-guard test suite
146
+
147
+ - **`src/lib/url-guard.test.ts`** (38 tests) locks in every branch of
148
+ `guardUrl()`, including the classic SSRF-filter evasion encodings. All
149
+ pass because Node's WHATWG URL parser normalises them before the regex
150
+ check sees the hostname; the tests exist so a future parser change,
151
+ regex tweak, or refactor can't silently re-open the hole.
152
+
153
+ Covered bypass vectors: decimal IPv4 (`http://2130706433/`), hex IPv4
154
+ (`0x7f000001`), octal IPv4 (`0177.0.0.1`), short-form IPv4 (`127.1`),
155
+ bare zero (`http://0/`), IPv6-mapped IPv4 (`[::ffff:127.0.0.1]` plus
156
+ its compact `[::ffff:7f00:1]` and expanded forms).
157
+
158
+ Plus full coverage of the scheme allow-list (rejects `file://`, `ftp://`,
159
+ `gopher://`, `data:`, `javascript:`), private ranges, link-local
160
+ (including the AWS/GCP/Azure metadata endpoint at `169.254.169.254`),
161
+ IPv6 `::1` / fc00::/7 ULA / fe80::/10 link-local, and the
162
+ `MCP_VIDEO_ALLOW_INTERNAL` escape hatch (strict equality on `"1"` —
163
+ `"true"` / `"yes"` / `"0"` do not open the door).
164
+
8
165
  ## [1.0.0] - 2026-03-14
9
166
 
10
167
  ### Added
@@ -0,0 +1,7 @@
1
+ # Code of Conduct
2
+
3
+ Be kind. Be respectful. Help each other.
4
+
5
+ This project follows the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) v2.1.
6
+
7
+ Report issues to hello@studiomeyer.io.
package/ECOSYSTEM.md ADDED
@@ -0,0 +1,35 @@
1
+ # StudioMeyer Ecosystem
2
+
3
+ MCP Video is part of the StudioMeyer open source toolkit. Here is everything we build and maintain.
4
+
5
+ ## MCP Server Products (Hosted)
6
+
7
+ | Product | Tools | What it does | Link |
8
+ |---------|-------|-------------|------|
9
+ | **StudioMeyer Memory** | 53 | Persistent AI memory with knowledge graph, semantic search, multi-agent support | [memory.studiomeyer.io](https://memory.studiomeyer.io) |
10
+ | **StudioMeyer CRM** | 33 | Headless CRM — contacts, companies, deals, pipeline, health scores, Stripe sync | [crm.studiomeyer.io](https://crm.studiomeyer.io) |
11
+ | **StudioMeyer GEO** | 24 | AI visibility monitoring across 8 LLM platforms | [geo.studiomeyer.io](https://geo.studiomeyer.io) |
12
+ | **MCP Crew** | 10 | 8 expert personas with domain frameworks | [crew.studiomeyer.io](https://crew.studiomeyer.io) |
13
+
14
+ All MCP products use OAuth 2.1 + Magic Link authentication. Free tiers available. EU Frankfurt hosting.
15
+
16
+ ## Open Source Tools
17
+
18
+ | Project | Description | Install |
19
+ |---------|-------------|---------|
20
+ | **[AI Shield](https://github.com/studiomeyer-io/ai-shield)** | LLM security — prompt injection, PII, cost tracking, tool policies | `npm install ai-shield-core` |
21
+ | **[Darwin Agents](https://github.com/studiomeyer-io/darwin-agents)** | Self-evolving AI agents with A/B testing and safety gates | `npm install darwin-agents` |
22
+ | **[Agent Fleet](https://github.com/studiomeyer-io/agent-fleet)** | Multi-agent orchestration for Claude Code CLI | `git clone` + `npm install` |
23
+ | **[MCP Video](https://github.com/studiomeyer-io/mcp-video)** | Cinema-grade video production via MCP | `npx mcp-video` |
24
+
25
+ ## How MCP Video fits in
26
+
27
+ MCP Video handles the visual content layer. Record product demos, create social media clips, add AI voiceovers, and export for every platform. Pairs well with MCP Crew (Creative Director persona for direction) and Agent Fleet (automated content pipelines).
28
+
29
+ ## License
30
+
31
+ All open source projects are MIT licensed.
32
+
33
+ ---
34
+
35
+ Built by [StudioMeyer](https://studiomeyer.io) — AI agency from Mallorca, Spain.
package/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <!-- studiomeyer-mcp-stack-banner:start -->
2
+ > **Part of the [StudioMeyer MCP Stack](https://studiomeyer.io)** — Built in Mallorca 🌴 · ⭐ if you use it
3
+ <!-- studiomeyer-mcp-stack-banner:end -->
4
+
1
5
  <div align="center">
2
6
 
3
7
  # mcp-video
@@ -18,6 +22,16 @@ Built on [ffmpeg](https://ffmpeg.org/) and [Playwright](https://playwright.dev/)
18
22
 
19
23
  </div>
20
24
 
25
+ ## A note from us
26
+
27
+ We have been building tools and systems for ourselves for the past two years. The fact that this repo is small and has few stars is not because it is new. It is because we only just decided to share what we have built. It is not a fresh experiment, it is a long story with a recent commit.
28
+
29
+ We love building things and sharing them. We do not love social media tactics, growth hacks, or chasing stars and followers. So this repo is small. The code is real, it gets used, issues get answered. Judge for yourself.
30
+
31
+ If it helps you, sharing, testing, and feedback help us. If it could be better, an issue is more useful. If you build something with it, tell us at hello@studiomeyer.io. That genuinely makes our day.
32
+
33
+ From a small studio in Palma de Mallorca.
34
+
21
35
  ## Features
22
36
 
23
37
  | Tool | Operations | Description |
@@ -44,11 +58,15 @@ Built on [ffmpeg](https://ffmpeg.org/) and [Playwright](https://playwright.dev/)
44
58
  ## Prerequisites
45
59
 
46
60
  - **Node.js** >= 18
47
- - **ffmpeg** and **ffprobe** (validated on startup)
61
+ - **ffmpeg** and **ffprobe** (validated on startup, cross-platform)
48
62
  - **Playwright** browsers (`npx playwright install chromium`)
49
63
  - Optional: `ELEVENLABS_API_KEY` for ElevenLabs TTS
50
64
  - Optional: `OPENAI_API_KEY` for Whisper captions and OpenAI TTS
51
65
 
66
+ If ffmpeg lives outside `PATH`, set `FFMPEG_PATH` and `FFPROBE_PATH` to the
67
+ absolute binary paths. Both env vars are honoured at startup AND at every
68
+ runtime spawn site.
69
+
52
70
  ## Quick Start
53
71
 
54
72
  ### With Claude Code (stdio)
@@ -96,6 +114,8 @@ MCP_HTTP=1 MCP_PORT=9847 npx mcp-video
96
114
  | Environment Variable | Default | Description |
97
115
  |---------------------|---------|-------------|
98
116
  | `VIDEO_OUTPUT_DIR` | `./output` | Directory for generated files |
117
+ | `FFMPEG_PATH` | — | Absolute path to `ffmpeg` binary if not on `PATH` |
118
+ | `FFPROBE_PATH` | — | Absolute path to `ffprobe` binary if not on `PATH` |
99
119
  | `ELEVENLABS_API_KEY` | — | ElevenLabs TTS API key |
100
120
  | `OPENAI_API_KEY` | — | OpenAI API key (Whisper + TTS) |
101
121
  | `MCP_HTTP` | `false` | Enable HTTP transport |
@@ -185,6 +205,10 @@ npm test # Run tests
185
205
  npm run check # Verify ffmpeg/ffprobe installed
186
206
  ```
187
207
 
208
+ ## About StudioMeyer
209
+
210
+ [StudioMeyer](https://studiomeyer.io) is an AI and design studio based in Palma de Mallorca, working with clients worldwide. We build custom websites and AI infrastructure for small and medium businesses. Production stack on Claude Agent SDK, MCP and n8n, with Sentry, Langfuse and LangGraph for observability and an in-house guard layer.
211
+
188
212
  ## License
189
213
 
190
214
  MIT
@@ -195,4 +219,4 @@ Built by [StudioMeyer](https://studiomeyer.io). Part of our open-source toolkit
195
219
 
196
220
  - [ai-shield](https://github.com/studiomeyer-io/ai-shield) — LLM security middleware for TypeScript
197
221
  - [agent-fleet](https://github.com/studiomeyer-io/agent-fleet) — Multi-agent orchestration for Claude Code
198
- - [darwin-agents](https://github.com/studiomeyer-io/darwin-agents) — Self-evolving agent framework
222
+ - [darwin-agents](https://github.com/studiomeyer-io/darwin-agents) — Self-evolving agent framework
package/SECURITY.md ADDED
@@ -0,0 +1,11 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a Vulnerability
4
+
5
+ If you find a security vulnerability, please report it privately.
6
+
7
+ **Email:** hello@studiomeyer.io
8
+
9
+ Please do not open a public GitHub issue for security vulnerabilities.
10
+
11
+ We will acknowledge your report within 48 hours and provide a fix timeline within 7 days.
@@ -4,14 +4,18 @@
4
4
  import { jsonResponse } from '../lib/types.js';
5
5
  import { logger } from '../lib/logger.js';
6
6
  import { smartScreenshot } from '../tools/engine/smart-screenshot.js';
7
+ import { guardUrl } from '../lib/url-guard.js';
7
8
  export const smartScreenshotHandlers = {
8
9
  /**
9
10
  * Take element-aware screenshots of specific page features
10
11
  */
11
12
  screenshot_element: async (args) => {
12
13
  try {
14
+ const guard = guardUrl(args.url);
15
+ if (!guard.ok)
16
+ return jsonResponse({ success: false, error: guard.reason }, true);
13
17
  const config = {
14
- url: args.url,
18
+ url: guard.url,
15
19
  targets: normalizeTargetArgs(args.targets),
16
20
  outputDir: args.outputDir,
17
21
  viewport: args.viewport ?? { width: 1920, height: 1080 },
@@ -33,8 +37,11 @@ export const smartScreenshotHandlers = {
33
37
  */
34
38
  detect_page_features: async (args) => {
35
39
  try {
40
+ const guard = guardUrl(args.url);
41
+ if (!guard.ok)
42
+ return jsonResponse({ success: false, error: guard.reason }, true);
36
43
  const config = {
37
- url: args.url,
44
+ url: guard.url,
38
45
  targets: ['all'],
39
46
  viewport: args.viewport ?? { width: 1920, height: 1080 },
40
47
  includeFullPage: false,
@@ -44,7 +51,7 @@ export const smartScreenshotHandlers = {
44
51
  const result = await smartScreenshot(config);
45
52
  return jsonResponse({
46
53
  success: true,
47
- url: args.url,
54
+ url: guard.url,
48
55
  features: result.detected.map(f => ({
49
56
  name: f.name,
50
57
  selector: f.selector,
@@ -1 +1 @@
1
- {"version":3,"file":"smart-screenshot.js","sourceRoot":"","sources":["../../src/handlers/smart-screenshot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"smart-screenshot.js","sourceRoot":"","sources":["../../src/handlers/smart-screenshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAEtE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,MAAM,CAAC,MAAM,uBAAuB,GAAgC;IAClE;;OAEG;IACH,kBAAkB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACjC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,EAAE;gBAAE,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;YAClF,MAAM,MAAM,GAA0B;gBACpC,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,OAAO,EAAE,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC;gBAC1C,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;gBACxD,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,CAAC;gBAC9C,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;gBAChC,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,KAAK;aAC/C,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;YAC7C,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAC;YACtD,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED;;OAEG;IACH,oBAAoB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACnC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,EAAE;gBAAE,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;YAClF,MAAM,MAAM,GAA0B;gBACpC,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,OAAO,EAAE,CAAC,KAAK,CAAC;gBAChB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;gBACxD,eAAe,EAAE,KAAK;aACvB,CAAC;YAEF,gEAAgE;YAChE,oFAAoF;YACpF,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;YAC7C,OAAO,YAAY,CAAC;gBAClB,OAAO,EAAE,IAAI;gBACb,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBAClC,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;oBACpB,IAAI,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE;oBAC5C,QAAQ,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG;oBAClE,WAAW,EAAE,CAAC,CAAC,WAAW;oBAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;iBACzB,CAAC,CAAC;gBACH,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM;gBAC7B,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBACxC,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,IAAI,EAAE,CAAC,CAAC,IAAI;iBACb,CAAC,CAAC;aACJ,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,gCAAgC,OAAO,EAAE,CAAC,CAAC;YACxD,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;CACF,CAAC;AAEF,SAAS,mBAAmB,CAAC,OAAgB;IAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;QACrB,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC;QACpC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;YAC1D,OAAO,CAAgB,CAAC;QAC1B,CAAC;QACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { jsonResponse } from '../lib/types.js';
5
5
  import { logger } from '../lib/logger.js';
6
+ import { guardUrl } from '../lib/url-guard.js';
6
7
  import { generateSpeech, listElevenLabsVoices, createNarratedVideo, } from '../tools/index.js';
7
8
  const OUTPUT_DIR = process.env.VIDEO_OUTPUT_DIR || './output';
8
9
  export const ttsHandlers = {
@@ -51,15 +52,18 @@ export const ttsHandlers = {
51
52
  },
52
53
  create_narrated_video: async (args) => {
53
54
  try {
55
+ const guard = guardUrl(args.url);
56
+ if (!guard.ok)
57
+ return jsonResponse({ success: false, error: guard.reason }, true);
54
58
  const segments = args.segments.map((s) => ({
55
59
  text: s.text,
56
60
  scene: s.scene,
57
61
  paddingAfter: s.paddingAfter,
58
62
  }));
59
- const hostname = new URL(args.url).hostname.replace(/^www\./, '').replace(/\./g, '-');
63
+ const hostname = new URL(guard.url).hostname.replace(/^www\./, '').replace(/\./g, '-');
60
64
  const defaultOutput = `${OUTPUT_DIR}/narrated-${hostname}-${new Date().toISOString().slice(0, 10)}`;
61
65
  const result = await createNarratedVideo({
62
- url: args.url,
66
+ url: guard.url,
63
67
  segments,
64
68
  outputPath: args.outputPath ?? defaultOutput,
65
69
  provider: args.provider,
@@ -1 +1 @@
1
- {"version":3,"file":"tts.js","sourceRoot":"","sources":["../../src/handlers/tts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tts.js","sourceRoot":"","sources":["../../src/handlers/tts.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import { jsonResponse } from '../lib/types.js';
5
5
  import { logger } from '../lib/logger.js';
6
6
  import { recordWebsite } from '../tools/index.js';
7
7
  import * as path from 'path';
8
+ import { guardUrl } from '../lib/url-guard.js';
8
9
  const OUTPUT_DIR = process.env.VIDEO_OUTPUT_DIR || './output';
9
10
  export const videoHandlers = {
10
11
  /**
@@ -12,9 +13,12 @@ export const videoHandlers = {
12
13
  */
13
14
  record_website_video: async (args) => {
14
15
  try {
16
+ const guard = guardUrl(args.url);
17
+ if (!guard.ok)
18
+ return jsonResponse({ success: false, error: guard.reason }, true);
15
19
  const config = {
16
- url: args.url,
17
- outputPath: args.outputPath ?? generateOutputPath(args.url, 'video'),
20
+ url: guard.url,
21
+ outputPath: args.outputPath ?? generateOutputPath(guard.url, 'video'),
18
22
  viewport: args.viewport ?? 'desktop',
19
23
  fps: args.fps ?? 60,
20
24
  scenes: args.scenes,
@@ -41,11 +45,14 @@ export const videoHandlers = {
41
45
  */
42
46
  record_website_scroll: async (args) => {
43
47
  try {
48
+ const guard = guardUrl(args.url);
49
+ if (!guard.ok)
50
+ return jsonResponse({ success: false, error: guard.reason }, true);
44
51
  const duration = args.duration ?? 12;
45
52
  const easing = args.easing ?? 'showcase';
46
53
  const config = {
47
- url: args.url,
48
- outputPath: args.outputPath ?? generateOutputPath(args.url, 'scroll'),
54
+ url: guard.url,
55
+ outputPath: args.outputPath ?? generateOutputPath(guard.url, 'scroll'),
49
56
  viewport: args.viewport ?? 'desktop',
50
57
  fps: 60,
51
58
  scenes: [
@@ -70,15 +77,18 @@ export const videoHandlers = {
70
77
  */
71
78
  record_multi_device: async (args) => {
72
79
  try {
80
+ const guard = guardUrl(args.url);
81
+ if (!guard.ok)
82
+ return jsonResponse({ success: false, error: guard.reason }, true);
73
83
  const devices = args.devices ?? ['desktop', 'tablet', 'mobile'];
74
84
  const duration = args.duration ?? 10;
75
85
  const outputDir = args.outputDir ?? OUTPUT_DIR;
76
86
  const results = {};
77
87
  for (const device of devices) {
78
- logger.info(`Recording ${device} viewport for ${args.url}...`);
88
+ logger.info(`Recording ${device} viewport for ${guard.url}...`);
79
89
  const config = {
80
- url: args.url,
81
- outputPath: path.join(outputDir, generateOutputName(args.url, device)),
90
+ url: guard.url,
91
+ outputPath: path.join(outputDir, generateOutputName(guard.url, device)),
82
92
  viewport: device,
83
93
  fps: 60,
84
94
  scenes: [
@@ -1 +1 @@
1
- {"version":3,"file":"video.js","sourceRoot":"","sources":["../../src/handlers/video.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"video.js","sourceRoot":"","sources":["../../src/handlers/video.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Error-message sanitizer for upstream API responses.
3
+ *
4
+ * Motivation: TTS, cloud-media and similar APIs echo request bodies or
5
+ * auth headers back in error responses. Our code previously did
6
+ * throw new Error(`ElevenLabs API ${status}: ${errorText}`);
7
+ * which happily embedded Bearer tokens, full xi-api-key values, signed
8
+ * URLs and sometimes the caller's text payload into stack traces that
9
+ * the MCP client then logged or surfaced to the human.
10
+ *
11
+ * `sanitizeErrorMessage` strips the common secret-looking patterns and
12
+ * truncates to a fixed cap so a 2 MB HTML error page doesn't turn into
13
+ * a 2 MB thrown string.
14
+ */
15
+ export interface SanitizeOptions {
16
+ /** Max length of the returned string (default: 300). */
17
+ limit?: number;
18
+ /** Optional prefix added before the sanitized body. */
19
+ prefix?: string;
20
+ }
21
+ export declare function sanitizeErrorMessage(raw: unknown, opts?: SanitizeOptions): string;
22
+ /**
23
+ * Wrap a thrown non-Error value and return a sanitized Error.
24
+ * Use inside catch-blocks that re-throw upstream API failures.
25
+ */
26
+ export declare function sanitizedError(raw: unknown, prefix?: string): Error;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Error-message sanitizer for upstream API responses.
3
+ *
4
+ * Motivation: TTS, cloud-media and similar APIs echo request bodies or
5
+ * auth headers back in error responses. Our code previously did
6
+ * throw new Error(`ElevenLabs API ${status}: ${errorText}`);
7
+ * which happily embedded Bearer tokens, full xi-api-key values, signed
8
+ * URLs and sometimes the caller's text payload into stack traces that
9
+ * the MCP client then logged or surfaced to the human.
10
+ *
11
+ * `sanitizeErrorMessage` strips the common secret-looking patterns and
12
+ * truncates to a fixed cap so a 2 MB HTML error page doesn't turn into
13
+ * a 2 MB thrown string.
14
+ */
15
+ // Order matters: specific-form patterns (Bearer / sk- / AKIA) run first and
16
+ // leave `[REDACTED]` markers behind. The generic Authorization pattern has
17
+ // a negative lookahead for `Bearer` / `[REDACTED]` so it does not re-consume
18
+ // an already-tokenised value (which would lose the `Bearer` marker that
19
+ // log-readers rely on to see *what kind* of token leaked).
20
+ const PATTERNS = [
21
+ // Bearer tokens — `Bearer sk-abc...`
22
+ { re: /\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi, replacement: 'Bearer [REDACTED]' },
23
+ // ElevenLabs `xi-api-key`, standard `x-api-key` (AWS / Anthropic / many APIs)
24
+ { re: /(xi?-api-key["':\s]+)[^"'\s,}]+/gi, replacement: '$1[REDACTED]' },
25
+ // OpenAI-style `sk-` and `sk-proj-` keys
26
+ { re: /\bsk-[a-zA-Z0-9_-]{20,}/g, replacement: 'sk-[REDACTED]' },
27
+ // AWS access keys (AKIA + 16 chars)
28
+ { re: /\bAKIA[0-9A-Z]{16}\b/g, replacement: '[REDACTED-AWS-KEY]' },
29
+ // Generic `"api_key": "..."` or `"apiKey": "..."` inside JSON
30
+ { re: /("api[-_]?key"\s*:\s*")[^"]+/gi, replacement: '$1[REDACTED]' },
31
+ // Authorization header full line — skip values we already redacted.
32
+ {
33
+ re: /(authorization["':\s]+)(?!Bearer\s)(?!\[REDACTED\])[^"'\s,}]+/gi,
34
+ replacement: '$1[REDACTED]',
35
+ },
36
+ // Signed URLs with `X-Amz-Signature=` or `?signature=`
37
+ { re: /([?&](?:X-Amz-Signature|signature)=)[^&\s"']+/gi, replacement: '$1[REDACTED]' },
38
+ ];
39
+ export function sanitizeErrorMessage(raw, opts = {}) {
40
+ const { limit = 300, prefix = '' } = opts;
41
+ let str = typeof raw === 'string' ? raw : raw instanceof Error ? raw.message : String(raw);
42
+ for (const { re, replacement } of PATTERNS) {
43
+ str = str.replace(re, replacement);
44
+ }
45
+ // Collapse whitespace for readability.
46
+ str = str.replace(/\s+/g, ' ').trim();
47
+ if (str.length > limit)
48
+ str = `${str.slice(0, limit)}…`;
49
+ return prefix ? `${prefix}${str}` : str;
50
+ }
51
+ /**
52
+ * Wrap a thrown non-Error value and return a sanitized Error.
53
+ * Use inside catch-blocks that re-throw upstream API failures.
54
+ */
55
+ export function sanitizedError(raw, prefix = '') {
56
+ return new Error(sanitizeErrorMessage(raw, { prefix }));
57
+ }
58
+ //# sourceMappingURL=error-sanitizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-sanitizer.js","sourceRoot":"","sources":["../../src/lib/error-sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,4EAA4E;AAC5E,2EAA2E;AAC3E,6EAA6E;AAC7E,wEAAwE;AACxE,2DAA2D;AAC3D,MAAM,QAAQ,GAA+C;IAC3D,qCAAqC;IACrC,EAAE,EAAE,EAAE,mCAAmC,EAAE,WAAW,EAAE,mBAAmB,EAAE;IAC7E,8EAA8E;IAC9E,EAAE,EAAE,EAAE,mCAAmC,EAAE,WAAW,EAAE,cAAc,EAAE;IACxE,yCAAyC;IACzC,EAAE,EAAE,EAAE,0BAA0B,EAAE,WAAW,EAAE,eAAe,EAAE;IAChE,oCAAoC;IACpC,EAAE,EAAE,EAAE,uBAAuB,EAAE,WAAW,EAAE,oBAAoB,EAAE;IAClE,8DAA8D;IAC9D,EAAE,EAAE,EAAE,gCAAgC,EAAE,WAAW,EAAE,cAAc,EAAE;IACrE,oEAAoE;IACpE;QACE,EAAE,EAAE,iEAAiE;QACrE,WAAW,EAAE,cAAc;KAC5B;IACD,uDAAuD;IACvD,EAAE,EAAE,EAAE,iDAAiD,EAAE,WAAW,EAAE,cAAc,EAAE;CACvF,CAAC;AASF,MAAM,UAAU,oBAAoB,CAAC,GAAY,EAAE,OAAwB,EAAE;IAC3E,MAAM,EAAE,KAAK,GAAG,GAAG,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC;IAC1C,IAAI,GAAG,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3F,KAAK,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC3C,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IACrC,CAAC;IACD,uCAAuC;IACvC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACtC,IAAI,GAAG,CAAC,MAAM,GAAG,KAAK;QAAE,GAAG,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC;IACxD,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY,EAAE,MAAM,GAAG,EAAE;IACtD,OAAO,IAAI,KAAK,CAAC,oBAAoB,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC"}