@kolbo/mcp 1.76.4 → 1.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -110,7 +110,6 @@ These elevate rich cinematic / reference-anchored sequences. For a short, tight,
|
|
|
110
110
|
## Dialogue & expression
|
|
111
111
|
|
|
112
112
|
- Dialogue goes in quotes and may be in ANY language (Hebrew included). For silent tension, deliver it as expression, not speech: `He does not speak. His expression clearly says: "…"`.
|
|
113
|
-
- **Seedance PERFORMS quoted dialogue natively** — synced voices, lip movement, and room tone come out of the video model itself. Never route scene dialogue through TTS (`generate_speech`) or `generate_lipsync`; write each line in quotes inside its shot beat (`DANIEL says: "…"`) and generate once.
|
|
114
113
|
|
|
115
114
|
## Content tone
|
|
116
115
|
|
|
@@ -9,7 +9,7 @@ Load this file when the user wants a **Seedance 2.5** video (they said "2.5" / "
|
|
|
9
9
|
|
|
10
10
|
**Kolbo MCP routing:** `generate_video` or `generate_elements` (refs / Visual DNA / first-last). Run `list_models({ type: "text_to_video" })` and pick the Seedance 2.5 variant by name.
|
|
11
11
|
|
|
12
|
-
**Audio:** Seedance 2.5 still emits real synced audio. `list_models` may show `sound_generation_type: none` because there is no in-app toggle (`sound_baked_in: true`). Do not tell the user the model is silent.
|
|
12
|
+
**Audio:** Seedance 2.5 still emits real synced audio. `list_models` may show `sound_generation_type: none` because there is no in-app toggle (`sound_baked_in: true`). Do not tell the user the model is silent.
|
|
13
13
|
|
|
14
14
|
## What's NEW in 2.5 (verified — never hedge)
|
|
15
15
|
|
package/src/apps/theme.js
CHANGED
|
@@ -144,31 +144,20 @@ body {
|
|
|
144
144
|
.k-skel.portrait { aspect-ratio: 3 / 4; }
|
|
145
145
|
.k-skel::after {
|
|
146
146
|
content: ''; position: absolute; inset: 0;
|
|
147
|
-
/* The shimmer covers the WHOLE cell and outlives the skeleton — .k-skel.done
|
|
148
|
-
only clears its paint, not its box. A pseudo-element hit-tests as its
|
|
149
|
-
originating element, so every click on a finished cell landed on the cell
|
|
150
|
-
instead of the media inside it: image cells still worked (their handler is
|
|
151
|
-
ON the cell), but a <video>'s native controls never saw a single event —
|
|
152
|
-
play, scrub, volume and fullscreen were all dead. It is decoration; it must
|
|
153
|
-
never take a click. */
|
|
154
|
-
pointer-events: none;
|
|
155
147
|
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.06) 40%, rgba(255,255,255,0.10) 50%, rgba(255,255,255,0.06) 60%, transparent 100%);
|
|
156
148
|
background-size: 200% 100%;
|
|
157
149
|
animation: k-sweep 1.6s ease-in-out infinite;
|
|
158
150
|
}
|
|
159
151
|
@keyframes k-sweep { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
|
160
152
|
/* Batch grid: per-cell prompt caption + a cell that already finished */
|
|
161
|
-
|
|
162
|
-
to be click-through too — the caption is a label, not a target. The full text
|
|
163
|
-
stays reachable: the title tooltip moved onto the cell. */
|
|
164
|
-
.k-skel-cap { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; pointer-events: none;
|
|
153
|
+
.k-skel-cap { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
|
|
165
154
|
padding: 12px 8px 6px; font-size: 10.5px; color: #fff;
|
|
166
155
|
background: linear-gradient(transparent, rgba(0, 0, 0, 0.65));
|
|
167
156
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
168
157
|
.k-skel.done::after { animation: none; background: none; }
|
|
169
158
|
.k-cell-fill { width: 100%; height: 100%; object-fit: contain; display: block; background: #000; }
|
|
170
159
|
.k-gen-badge {
|
|
171
|
-
position: absolute; top: 10px; left: 10px; z-index: 2;
|
|
160
|
+
position: absolute; top: 10px; left: 10px; z-index: 2;
|
|
172
161
|
display: inline-flex; align-items: center; gap: 6px;
|
|
173
162
|
padding: 4px 10px; border-radius: 999px;
|
|
174
163
|
background: rgba(0, 0, 0, 0.65); /* no backdrop-filter: nested blur over the
|
|
@@ -586,12 +586,8 @@ function fillBatchCell(sc, i, g) {
|
|
|
586
586
|
cell.classList.add('done');
|
|
587
587
|
var cap = (sc.prompts && sc.prompts[i])
|
|
588
588
|
? '<span class="k-skel-cap" title="' + esc(sc.prompts[i]) + '">' + esc(sc.prompts[i]) + '</span>' : '';
|
|
589
|
-
if (sc.prompts && sc.prompts[i]) cell.title = sc.prompts[i];
|
|
590
|
-
// The controls attribute is not optional here: this cell is a FINISHED result
|
|
591
|
-
// the user is meant to watch, and without it the tile was a muted poster with
|
|
592
|
-
// no way to play, seek or unmute until the whole batch finished and repainted.
|
|
593
589
|
cell.innerHTML = (sc.kind === 'video'
|
|
594
|
-
? '<video class="k-cell-fill" src="' + esc(u) + '"' + (r.thumbnail_url ? ' poster="' + esc(r.thumbnail_url) + '"' : '') + '
|
|
590
|
+
? '<video class="k-cell-fill" src="' + esc(u) + '"' + (r.thumbnail_url ? ' poster="' + esc(r.thumbnail_url) + '"' : '') + ' muted playsinline preload="metadata"></video>'
|
|
595
591
|
: '<img class="k-cell-fill" src="' + esc(u) + '" alt="">') + cap;
|
|
596
592
|
window.kolbo.notifySize();
|
|
597
593
|
}
|
|
@@ -771,8 +767,7 @@ function renderBatchGrid(sc) {
|
|
|
771
767
|
var shape = items[0].type === 'video' ? 'video' : 'square';
|
|
772
768
|
el('stage').innerHTML = '<div class="k-gen-grid n' + Math.min(items.length, 4) + '">' +
|
|
773
769
|
items.map(function (it, i) {
|
|
774
|
-
return '<div class="k-skel done ' + shape + '" data-focus="' + i + '"' +
|
|
775
|
-
(it.label ? ' title="' + esc(it.label) + '"' : '') + '>' +
|
|
770
|
+
return '<div class="k-skel done ' + shape + '" data-focus="' + i + '">' +
|
|
776
771
|
(it.type === 'video'
|
|
777
772
|
? '<video class="k-cell-fill" src="' + esc(it.url) + '" controls playsinline preload="metadata"></video>'
|
|
778
773
|
: '<img class="k-cell-fill" src="' + esc(it.url) + '" alt="" loading="lazy" style="cursor:zoom-in">') +
|
|
@@ -808,8 +803,7 @@ function renderStatusGrid(sc) {
|
|
|
808
803
|
// file — the tool only knows a url came back, not what kind it is.
|
|
809
804
|
it.kind = refKind(it.url, 'image');
|
|
810
805
|
var shape = it.kind === 'video' ? 'video' : 'square';
|
|
811
|
-
return '<div class="k-skel done ' + shape + '" data-focus="' + i + '"' +
|
|
812
|
-
(it.title ? ' title="' + esc(it.title) + '"' : '') + '>' +
|
|
806
|
+
return '<div class="k-skel done ' + shape + '" data-focus="' + i + '">' +
|
|
813
807
|
(it.kind === 'video'
|
|
814
808
|
? '<video class="k-cell-fill" src="' + esc(it.url) + '" controls playsinline preload="metadata"></video>'
|
|
815
809
|
: it.kind === 'audio'
|
package/src/tools/generate.js
CHANGED
|
@@ -1181,10 +1181,10 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1181
1181
|
{
|
|
1182
1182
|
prompt: z.string().describe('Locked Intro prompt (Seedance/Elements): Total line, [GLOBAL LOOK], [CAST] with @ExactDNAName for every visual_dna_ids entry, [LOCATION], then SHOT N. Not SCENE CONTEXT/OPTICS/ACTION packs. Never substitute "the left man" or "Zohar\'s" for @Name.'),
|
|
1183
1183
|
model: z.string().optional().describe('Model identifier. If the user already named a family (Grok / Kling / Veo / Seedance / …), pass THAT family — never default to Seedance because Elements often uses it. Use list_models type="elements" for exact ids and elements_max_* caps. Do NOT omit (omitting = Smart Select).'),
|
|
1184
|
-
reference_images: z.array(z.string()).optional().describe('Array of image references (product shots, character references, etc.). Accepts a public URL (forwarded as-is
|
|
1185
|
-
reference_videos: z.array(z.string()).optional().describe('Array of reference videos for models that accept video inputs. Accepts a public URL (forwarded as-is
|
|
1186
|
-
reference_audio_urls: z.array(z.string()).optional().describe('Array of reference audio tracks for models that accept audio inputs. Accepts a public URL (forwarded as-is
|
|
1187
|
-
audio_url: z.string().optional().describe('A single reference audio track — legacy form of reference_audio_urls. Accepts a public URL (forwarded as-is
|
|
1184
|
+
reference_images: z.array(z.string()).optional().describe('Array of image references (product shots, character references, etc.). Accepts a public URL (forwarded as-is, never re-uploaded) OR an absolute local path, which is uploaded for you. **Cap: pass at most `elements_max_images` URLs from list_models for the chosen model — exceeding it is a deterministic 400.**'),
|
|
1185
|
+
reference_videos: z.array(z.string()).optional().describe('Array of reference videos for models that accept video inputs. Accepts a public URL (forwarded as-is, never re-uploaded) OR an absolute local path, which is uploaded for you. **Cap: pass at most `elements_max_videos` URLs from list_models — if the cap is 0 the model rejects videos.**'),
|
|
1186
|
+
reference_audio_urls: z.array(z.string()).optional().describe('Array of reference audio tracks for models that accept audio inputs. Accepts a public URL (forwarded as-is, never re-uploaded) OR an absolute local path, which is uploaded for you. **Cap: pass at most `elements_max_audio` URLs from list_models.** `audio_url` remains supported as the legacy single-track form.'),
|
|
1187
|
+
audio_url: z.string().optional().describe('A single reference audio track — legacy form of reference_audio_urls. Accepts a public URL (forwarded as-is, never re-uploaded) OR an absolute local path, which is uploaded for you. **Audio constraints: `elements_max_audio` from list_models gates whether audio is accepted at all; audio duration must fall within `min_audio_duration`-`max_audio_duration`; format must be in `supported_audio_formats` (if specified).**'),
|
|
1188
1188
|
files: z.array(z.string()).optional().describe('Untyped catch-all for mixed media — images, videos AND audio, each a URL or an absolute local path. The kind is detected from the file extension and the item is routed to the matching reference list, so a local .mp4 is sent as a video and a local .mp3 as audio. Prefer the typed lists (reference_images / reference_videos / reference_audio_urls) when you already know the kind; they accept local paths too. URLs given here are forwarded as URLs, never re-uploaded. **Caps still apply per kind: `elements_max_images` / `elements_max_videos` / `elements_max_audio` from list_models. Local uploads are capped at 200MB each.**'),
|
|
1189
1189
|
duration: z.number().optional().describe('Output duration in seconds. Must be in `supported_durations` from list_models, OR within `min_output_duration`-`max_output_duration`. Default: 5'),
|
|
1190
1190
|
aspect_ratio: z.string().optional().describe('Aspect ratio (e.g., "16:9", "9:16", "1:1"). Must be in `supported_aspect_ratios` from list_models. Default: "16:9"'),
|
|
@@ -1250,10 +1250,10 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1250
1250
|
session_name, project_id, session_id
|
|
1251
1251
|
};
|
|
1252
1252
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1253
|
+
let startResponse;
|
|
1254
|
+
if (!locals.length) {
|
|
1255
|
+
startResponse = await client.post('/v1/generate/elements', body);
|
|
1256
|
+
} else {
|
|
1257
1257
|
const resolved = await Promise.all(locals.map(({ src, kind }) =>
|
|
1258
1258
|
resolveToBuffer(src, kind, { maxBytes: ELEMENTS_MAX_UPLOAD_BYTES })));
|
|
1259
1259
|
const form = new FormData();
|
|
@@ -1277,36 +1277,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1277
1277
|
for (const f of resolved) {
|
|
1278
1278
|
form.append('files', f.buffer, { filename: f.filename, contentType: f.contentType });
|
|
1279
1279
|
}
|
|
1280
|
-
|
|
1281
|
-
};
|
|
1282
|
-
|
|
1283
|
-
let startResponse;
|
|
1284
|
-
try {
|
|
1285
|
-
startResponse = await startElements();
|
|
1286
|
-
} catch (err) {
|
|
1287
|
-
// The elements trust gate only accepts remote references that are BOTH
|
|
1288
|
-
// Kolbo-hosted AND registered to this caller's library/project — any
|
|
1289
|
-
// other public URL 400s with this code, even though every schema here
|
|
1290
|
-
// says "public URL, forwarded as-is". Agents used to recover by hand
|
|
1291
|
-
// (upload_media, then resend). Do that detour for them: rehost every
|
|
1292
|
-
// remote reference into the caller's library and retry ONCE. The gate
|
|
1293
|
-
// fires before any charge, so the failed first attempt costs nothing.
|
|
1294
|
-
if (err?.code !== 'UNTRUSTED_REFERENCE_MEDIA_URL') throw err;
|
|
1295
|
-
for (const kind of ['image', 'video', 'audio']) {
|
|
1296
|
-
media[kind] = await Promise.all(media[kind].map(async (src) => {
|
|
1297
|
-
if (!isUrlSource(src)) return src;
|
|
1298
|
-
const file = await resolveToBuffer(src, kind, { maxBytes: ELEMENTS_MAX_UPLOAD_BYTES });
|
|
1299
|
-
const form = new FormData();
|
|
1300
|
-
form.append('file', file.buffer, { filename: file.filename, contentType: file.contentType });
|
|
1301
|
-
if (project_id) form.append('project_id', project_id);
|
|
1302
|
-
const uploaded = await client.postMultipart('/v1/media/upload', form);
|
|
1303
|
-
return uploaded?.media?.url || uploaded?.url || src;
|
|
1304
|
-
}));
|
|
1305
|
-
}
|
|
1306
|
-
body.reference_images = some(urlsOf('image'));
|
|
1307
|
-
body.reference_videos = some(urlsOf('video'));
|
|
1308
|
-
body.reference_audio_urls = some(urlsOf('audio'));
|
|
1309
|
-
startResponse = await startElements();
|
|
1280
|
+
startResponse = await client.postMultipart('/v1/generate/elements', form);
|
|
1310
1281
|
}
|
|
1311
1282
|
|
|
1312
1283
|
if (ui()) return uiGenerating({
|
|
@@ -1605,18 +1576,10 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1605
1576
|
highlighted: z.object({ font: z.string().optional(), weight: z.number().int().min(100).max(900).optional(), color: z.string().optional() }).optional().describe('Highlighted word tier styling.'),
|
|
1606
1577
|
}).optional(),
|
|
1607
1578
|
}).optional().describe('VEED Subtitles only: style overrides. Any omitted field keeps the preset default. Best supported by Basic presets.'),
|
|
1608
|
-
enhancement_model: z.string().optional()
|
|
1609
|
-
.describe('Topaz Slow Motion only (model "topaz/interpolate/video"): retiming engine — "Apollo" (smooth motion, default), "Chronos" (complex motion and occlusion), or "Aion" (highest quality for extreme slow motion, and the most expensive).'),
|
|
1610
|
-
target_fps: z.number().optional()
|
|
1611
|
-
.describe('Topaz Slow Motion only: frames per second of the OUTPUT (16-120, default 60). Higher rates generate more frames and cost proportionally more.'),
|
|
1612
|
-
slowdown_factor: z.number().optional()
|
|
1613
|
-
.describe('Topaz Slow Motion only: how many times longer the output runs (1-8, default 1). 4 turns a 5s clip into 20s of slow motion. Billing is on the OUTPUT length, so an 8x pass costs 8x a 1x pass.'),
|
|
1614
|
-
output_format: z.string().optional()
|
|
1615
|
-
.describe('Topaz HDR only (model "topaz/sdr-to-hdr/video"): "mp4" for 10-bit H.265 HDR10 (default) or "prores" for 10-bit ProRes 422 HQ. Both are HDR masters — the in-app player shows a tone-mapped SDR preview and the HDR file is the download.'),
|
|
1616
1579
|
project_id: projectIdField,
|
|
1617
1580
|
session_id: sessionIdField
|
|
1618
1581
|
},
|
|
1619
|
-
async ({ source_video, prompt, model, aspect_ratio, duration, enhance_prompt = false, visual_dna_ids, resolution, sound_enabled, reference_images, reference_videos, elements, preset, source_language, translation_language, srt_content, srt_file_url, vocabulary, customization,
|
|
1582
|
+
async ({ source_video, prompt, model, aspect_ratio, duration, enhance_prompt = false, visual_dna_ids, resolution, sound_enabled, reference_images, reference_videos, elements, preset, source_language, translation_language, srt_content, srt_file_url, vocabulary, customization, project_id, session_id }) => {
|
|
1620
1583
|
model = await canonicalModelId(client, model, 'video_to_video'); // lenient id resolution ("z-image" → "z-image/turbo")
|
|
1621
1584
|
if (!source_video) throw new Error('source_video is required');
|
|
1622
1585
|
|
|
@@ -1626,9 +1589,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1626
1589
|
startResponse = await client.post('/v1/generate/video-from-video', {
|
|
1627
1590
|
video_url: source_video, prompt, model, aspect_ratio, duration, enhance_prompt, visual_dna_ids, resolution, sound_enabled,
|
|
1628
1591
|
reference_images, reference_videos, elements, preset, source_language, translation_language,
|
|
1629
|
-
srt_content, srt_file_url, vocabulary, customization,
|
|
1630
|
-
enhancement_model, target_fps, slowdown_factor, output_format,
|
|
1631
|
-
project_id, session_id
|
|
1592
|
+
srt_content, srt_file_url, vocabulary, customization, project_id, session_id
|
|
1632
1593
|
});
|
|
1633
1594
|
} else {
|
|
1634
1595
|
const resolved = await resolveToBuffer(source_video, 'video');
|
|
@@ -1652,10 +1613,6 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1652
1613
|
if (reference_images) form.append('reference_images', JSON.stringify(reference_images));
|
|
1653
1614
|
if (reference_videos) form.append('reference_videos', JSON.stringify(reference_videos));
|
|
1654
1615
|
if (elements) form.append('elements', JSON.stringify(elements));
|
|
1655
|
-
if (enhancement_model) form.append('enhancement_model', enhancement_model);
|
|
1656
|
-
if (target_fps !== undefined) form.append('target_fps', String(target_fps));
|
|
1657
|
-
if (slowdown_factor !== undefined) form.append('slowdown_factor', String(slowdown_factor));
|
|
1658
|
-
if (output_format) form.append('output_format', output_format);
|
|
1659
1616
|
if (project_id) form.append('project_id', project_id);
|
|
1660
1617
|
if (session_id) form.append('session_id', session_id);
|
|
1661
1618
|
startResponse = await client.postMultipart('/v1/generate/video-from-video', form);
|
|
@@ -1865,8 +1822,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1865
1822
|
'camera_angle',
|
|
1866
1823
|
'split', 'split_upscale',
|
|
1867
1824
|
'multi_shot',
|
|
1868
|
-
'magic_edit'
|
|
1869
|
-
'enhance'
|
|
1825
|
+
'magic_edit'
|
|
1870
1826
|
]).describe([
|
|
1871
1827
|
'Edit operation:',
|
|
1872
1828
|
'"upscale" — increase resolution by 2×, 3×, or 4× (use `scale`). "clarity_upscale" — AI-powered clarity upscale with detail enhancement (use `resolution`).',
|
|
@@ -1875,7 +1831,6 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1875
1831
|
'"removebg" — remove the image background, output is transparent PNG.',
|
|
1876
1832
|
'"background_replace" — remove background and replace it with AI-generated content from `prompt`.',
|
|
1877
1833
|
'"enhance_skin" — portrait skin retouching (use `skin_strength`: "subtle" | "realistic" | "pimple" | "freckle").',
|
|
1878
|
-
'"enhance" — Topaz photo correction at the SOURCE resolution (no resizing). Pick the tool with `model`: "topaz/adjust/image" (exposure, white balance, or colorizing a black-and-white photo), "topaz/sharpen/image" (lens / motion / portrait / wildlife blur, or Super Focus for severely blurred shots), "topaz/denoise/image" (high-ISO and night noise), "topaz/restore/image" (old or damaged photos, dust and scratches). Choose the specific engine with `enhancement_model` — call list_models type="graphics_enhance" to see each model\'s engines. To make an image BIGGER use "upscale" instead.',
|
|
1879
1834
|
'"inpaint" — paint over a masked area using `mask_image_url` (B&W mask, white = fill area) and optional `prompt`. Add reference images via `additional_images`.',
|
|
1880
1835
|
'"erase" — erase an object defined by `mask_image_url` (white = erase area).',
|
|
1881
1836
|
'"face_swap" — swap the face in `image_url` with the face from `mask_image_url` (required).',
|
|
@@ -1890,13 +1845,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1890
1845
|
|
|
1891
1846
|
// ── upscale ────────────────────────────────────────────
|
|
1892
1847
|
scale: z.number().optional()
|
|
1893
|
-
.describe('Upscale factor:
|
|
1894
|
-
|
|
1895
|
-
enhancement_model: z.string().optional()
|
|
1896
|
-
.describe('Topaz engine. With operation="upscale" on model "topaz/upscale/image" it selects the engine family: "Standard V2" / "High Fidelity V3" / "CGI" / "Text Refine" (faithful), "Wonder 3.5" (rebuilds natural detail), "Bloom 2" (reinvents detail — most expensive), "Transparent" (keeps the alpha channel). With operation="enhance" it selects the correction engine for the chosen model. Omit for the model default. Call list_models to see the engines a model offers.'),
|
|
1897
|
-
|
|
1898
|
-
output_format: z.string().optional()
|
|
1899
|
-
.describe('Output image format: "jpeg" or "png". Used with Topaz "upscale" and "enhance". Defaults to jpeg (the transparent upscaler always returns png).'),
|
|
1848
|
+
.describe('Upscale factor: 2, 3, or 4. Used with operation="upscale". Default: 2.'),
|
|
1900
1849
|
|
|
1901
1850
|
resolution: z.string().optional()
|
|
1902
1851
|
.describe('Target output resolution (e.g. "4k", "2k", "1080p"). Used with "clarity_upscale", "split_upscale", "multi_shot".'),
|
|
@@ -1953,7 +1902,6 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1953
1902
|
image_url, operation, model, scale, aspect_ratio, skin_strength, prompt,
|
|
1954
1903
|
mask_image_url, additional_images, generate_all_angles, resolution, quality, ai_optimize = false,
|
|
1955
1904
|
zoom_out_percentage, expand_left, expand_right, expand_top, expand_bottom,
|
|
1956
|
-
enhancement_model, output_format,
|
|
1957
1905
|
project_id, session_id
|
|
1958
1906
|
}) => {
|
|
1959
1907
|
// No `type` argument: these are operation-routed tools (upscale / reframe /
|
|
@@ -1963,9 +1911,6 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1963
1911
|
|
|
1964
1912
|
// Basic validation
|
|
1965
1913
|
if (operation === 'reframe' && !aspect_ratio) throw new Error('aspect_ratio is required for reframe');
|
|
1966
|
-
if (operation === 'enhance' && !model) {
|
|
1967
|
-
throw new Error('model is required for enhance — pick one of: topaz/adjust/image, topaz/sharpen/image, topaz/denoise/image, topaz/restore/image');
|
|
1968
|
-
}
|
|
1969
1914
|
if (operation === 'background_replace' && !prompt) throw new Error('prompt is required for background_replace');
|
|
1970
1915
|
if (operation === 'face_swap' && !mask_image_url && !(additional_images && additional_images.length > 0)) {
|
|
1971
1916
|
throw new Error('mask_image_url (face reference) is required for face_swap');
|
|
@@ -1975,7 +1920,6 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1975
1920
|
image_url, operation, model, scale, aspect_ratio, skin_strength, prompt,
|
|
1976
1921
|
mask_image_url, additional_images, generate_all_angles, resolution, quality, ai_optimize,
|
|
1977
1922
|
zoom_out_percentage, expand_left, expand_right, expand_top, expand_bottom,
|
|
1978
|
-
enhancement_model, output_format,
|
|
1979
1923
|
project_id, session_id
|
|
1980
1924
|
});
|
|
1981
1925
|
|
|
@@ -2048,8 +1992,6 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
2048
1992
|
.describe('Target resolution (e.g. "4k", "2k", "1080p"). Used with "upscale" and "reframe".'),
|
|
2049
1993
|
target_fps: z.number().optional()
|
|
2050
1994
|
.describe('Target frame rate (e.g. 24, 30, 60). Used with operation="upscale".'),
|
|
2051
|
-
enhancement_model: z.string().optional()
|
|
2052
|
-
.describe('Topaz enhancement engine for operation="upscale" on model "topaz/upscale/video". Families: Precision (faithful — "Proteus", "Artemis High Quality", "Iris", "Dione TV", "Gaia CG", "Gaia 2"), Denoise ("Nyx", "Nyx Fast"), Generative (rebuilds detail that is not in the source — "Starlight Fast 2", "Starlight HQ", "Starlight Precise 2.6"), Creative ("Astra 2", always renders 4K). Generative and Creative engines cost several times the Precision rate. Omit for the default.'),
|
|
2053
1995
|
|
|
2054
1996
|
// ── reframe ────────────────────────────────────────────
|
|
2055
1997
|
aspect_ratio: z.string().optional()
|
|
@@ -2113,7 +2055,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
2113
2055
|
async ({
|
|
2114
2056
|
video_url, operation, model, aspect_ratio, scale, prompt,
|
|
2115
2057
|
image_url, audio_url, duration, mode,
|
|
2116
|
-
target_fps, resolution,
|
|
2058
|
+
target_fps, resolution,
|
|
2117
2059
|
grid_position_x, grid_position_y,
|
|
2118
2060
|
sound_effect_prompt, background_music_prompt, original_sound, cfg_strength,
|
|
2119
2061
|
refine_edges, subject_is_person,
|
|
@@ -2136,7 +2078,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
2136
2078
|
if (operation === 'extend' && !duration) throw new Error('duration is required for extend');
|
|
2137
2079
|
|
|
2138
2080
|
const gen = await client.post('/v1/edit/video', {
|
|
2139
|
-
video_url, operation, model, aspect_ratio, scale, prompt,
|
|
2081
|
+
video_url, operation, model, aspect_ratio, scale, prompt,
|
|
2140
2082
|
image_url, audio_url, duration, mode,
|
|
2141
2083
|
target_fps, resolution,
|
|
2142
2084
|
grid_position_x, grid_position_y,
|