@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.
- package/.github/FUNDING.yml +2 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +9 -0
- package/.github/dependabot.yml +46 -0
- package/.github/workflows/ci.yml +2 -2
- package/CHANGELOG.md +157 -0
- package/CODE_OF_CONDUCT.md +7 -0
- package/ECOSYSTEM.md +35 -0
- package/README.md +26 -2
- package/SECURITY.md +11 -0
- package/dist/handlers/smart-screenshot.js +10 -3
- package/dist/handlers/smart-screenshot.js.map +1 -1
- package/dist/handlers/tts.js +6 -2
- package/dist/handlers/tts.js.map +1 -1
- package/dist/handlers/video.js +17 -7
- package/dist/handlers/video.js.map +1 -1
- package/dist/lib/error-sanitizer.d.ts +26 -0
- package/dist/lib/error-sanitizer.js +58 -0
- package/dist/lib/error-sanitizer.js.map +1 -0
- package/dist/lib/error-sanitizer.test.d.ts +1 -0
- package/dist/lib/error-sanitizer.test.js +73 -0
- package/dist/lib/error-sanitizer.test.js.map +1 -0
- package/dist/lib/ffmpeg-bin.d.ts +64 -0
- package/dist/lib/ffmpeg-bin.js +96 -0
- package/dist/lib/ffmpeg-bin.js.map +1 -0
- package/dist/lib/ffmpeg-bin.test.d.ts +18 -0
- package/dist/lib/ffmpeg-bin.test.js +169 -0
- package/dist/lib/ffmpeg-bin.test.js.map +1 -0
- package/dist/lib/ffmpeg-run.d.ts +43 -0
- package/dist/lib/ffmpeg-run.js +67 -0
- package/dist/lib/ffmpeg-run.js.map +1 -0
- package/dist/lib/ffmpeg-run.test.d.ts +1 -0
- package/dist/lib/ffmpeg-run.test.js +66 -0
- package/dist/lib/ffmpeg-run.test.js.map +1 -0
- package/dist/lib/ffmpeg-safety.d.ts +37 -0
- package/dist/lib/ffmpeg-safety.js +67 -0
- package/dist/lib/ffmpeg-safety.js.map +1 -0
- package/dist/lib/ffmpeg-safety.test.d.ts +1 -0
- package/dist/lib/ffmpeg-safety.test.js +72 -0
- package/dist/lib/ffmpeg-safety.test.js.map +1 -0
- package/dist/lib/temp-dir.d.ts +24 -0
- package/dist/lib/temp-dir.js +53 -0
- package/dist/lib/temp-dir.js.map +1 -0
- package/dist/lib/temp-dir.test.d.ts +1 -0
- package/dist/lib/temp-dir.test.js +68 -0
- package/dist/lib/temp-dir.test.js.map +1 -0
- package/dist/lib/url-guard.d.ts +41 -0
- package/dist/lib/url-guard.js +134 -0
- package/dist/lib/url-guard.js.map +1 -0
- package/dist/lib/url-guard.test.d.ts +10 -0
- package/dist/lib/url-guard.test.js +231 -0
- package/dist/lib/url-guard.test.js.map +1 -0
- package/dist/server.js +9 -4
- package/dist/server.js.map +1 -1
- package/dist/tools/engine/audio-mixer.js +5 -20
- package/dist/tools/engine/audio-mixer.js.map +1 -1
- package/dist/tools/engine/audio.js +3 -19
- package/dist/tools/engine/audio.js.map +1 -1
- package/dist/tools/engine/beat-sync.js +7 -30
- package/dist/tools/engine/beat-sync.js.map +1 -1
- package/dist/tools/engine/capture.js +7 -0
- package/dist/tools/engine/capture.js.map +1 -1
- package/dist/tools/engine/chroma-key.js +2 -11
- package/dist/tools/engine/chroma-key.js.map +1 -1
- package/dist/tools/engine/concat.js +2 -11
- package/dist/tools/engine/concat.js.map +1 -1
- package/dist/tools/engine/editing.js +12 -35
- package/dist/tools/engine/editing.js.map +1 -1
- package/dist/tools/engine/encoder.js +2 -12
- package/dist/tools/engine/encoder.js.map +1 -1
- package/dist/tools/engine/lut-presets.js +2 -11
- package/dist/tools/engine/lut-presets.js.map +1 -1
- package/dist/tools/engine/narrated-video.js +30 -39
- package/dist/tools/engine/narrated-video.js.map +1 -1
- package/dist/tools/engine/smart-screenshot.js +7 -0
- package/dist/tools/engine/smart-screenshot.js.map +1 -1
- package/dist/tools/engine/social-format.js +2 -11
- package/dist/tools/engine/social-format.js.map +1 -1
- package/dist/tools/engine/template-renderer.js +2 -11
- package/dist/tools/engine/template-renderer.js.map +1 -1
- package/dist/tools/engine/text-animations.js +2 -11
- package/dist/tools/engine/text-animations.js.map +1 -1
- package/dist/tools/engine/text-overlay.js +2 -11
- package/dist/tools/engine/text-overlay.js.map +1 -1
- package/dist/tools/engine/tts.js +11 -6
- package/dist/tools/engine/tts.js.map +1 -1
- package/dist/tools/engine/voice-effects.js +3 -20
- package/dist/tools/engine/voice-effects.js.map +1 -1
- package/package.json +6 -6
- package/src/handlers/smart-screenshot.ts +8 -3
- package/src/handlers/tts.ts +6 -2
- package/src/handlers/video.ts +14 -7
- package/src/lib/error-sanitizer.test.ts +88 -0
- package/src/lib/error-sanitizer.ts +66 -0
- package/src/lib/ffmpeg-bin.test.ts +192 -0
- package/src/lib/ffmpeg-bin.ts +111 -0
- package/src/lib/ffmpeg-run.test.ts +76 -0
- package/src/lib/ffmpeg-run.ts +110 -0
- package/src/lib/ffmpeg-safety.test.ts +88 -0
- package/src/lib/ffmpeg-safety.ts +79 -0
- package/src/lib/temp-dir.test.ts +75 -0
- package/src/lib/temp-dir.ts +58 -0
- package/src/lib/url-guard.test.ts +261 -0
- package/src/lib/url-guard.ts +143 -0
- package/src/server.ts +10 -5
- package/src/tools/engine/audio-mixer.ts +8 -21
- package/src/tools/engine/audio.ts +6 -21
- package/src/tools/engine/beat-sync.ts +10 -31
- package/src/tools/engine/capture.ts +8 -0
- package/src/tools/engine/chroma-key.ts +2 -11
- package/src/tools/engine/concat.ts +2 -11
- package/src/tools/engine/editing.ts +17 -34
- package/src/tools/engine/encoder.ts +2 -12
- package/src/tools/engine/lut-presets.ts +2 -11
- package/src/tools/engine/narrated-video.ts +26 -38
- package/src/tools/engine/smart-screenshot.ts +8 -0
- package/src/tools/engine/social-format.ts +2 -11
- package/src/tools/engine/template-renderer.ts +2 -11
- package/src/tools/engine/text-animations.ts +2 -11
- package/src/tools/engine/text-overlay.ts +2 -11
- package/src/tools/engine/tts.ts +15 -6
- package/src/tools/engine/voice-effects.ts +3 -17
|
@@ -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
|
+
- "*"
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -14,10 +14,10 @@ jobs:
|
|
|
14
14
|
node-version: [18, 20, 22]
|
|
15
15
|
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
18
|
|
|
19
19
|
- name: Use Node.js ${{ matrix.node-version }}
|
|
20
|
-
uses: actions/setup-node@
|
|
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
|
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:
|
|
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:
|
|
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:
|
|
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":"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;
|
|
1
|
+
{"version":3,"file":"smart-screenshot.js","sourceRoot":"","sources":["../../src/handlers/smart-screenshot.ts"],"names":[],"mappings":"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"}
|
package/dist/handlers/tts.js
CHANGED
|
@@ -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(
|
|
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:
|
|
66
|
+
url: guard.url,
|
|
63
67
|
segments,
|
|
64
68
|
outputPath: args.outputPath ?? defaultOutput,
|
|
65
69
|
provider: args.provider,
|
package/dist/handlers/tts.js.map
CHANGED
|
@@ -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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"}
|
package/dist/handlers/video.js
CHANGED
|
@@ -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:
|
|
17
|
-
outputPath: args.outputPath ?? generateOutputPath(
|
|
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:
|
|
48
|
-
outputPath: args.outputPath ?? generateOutputPath(
|
|
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 ${
|
|
88
|
+
logger.info(`Recording ${device} viewport for ${guard.url}...`);
|
|
79
89
|
const config = {
|
|
80
|
-
url:
|
|
81
|
-
outputPath: path.join(outputDir, generateOutputName(
|
|
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":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;
|
|
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"}
|