hermoso 0.1.379 → 0.1.381
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/mcp/openai-extensions.mjs +1 -1
- package/mcp/tools.mjs +18 -12
- package/package.json +1 -1
|
@@ -1256,7 +1256,7 @@ export const neutralNote = (t) => {
|
|
|
1256
1256
|
// sweeps and drift watches queued for later (measured on a fresh local account: one `agentidle` "nudge", queued for
|
|
1257
1257
|
// tomorrow, would have shown as "1 render in progress"). Mirrors server.js JOB_TYPES_HIDDEN_FROM_V1, and the check
|
|
1258
1258
|
// fails if that list gains a type this one lacks. A job queued for LATER is not in progress either.
|
|
1259
|
-
export const PANEL_HIDDEN_JOB_TYPES = Object.freeze(['purge', 'agentidle', 'agentsetupmail', 'uploadretention', 'connectordrift', 'falwatch', 'faceassetsweep', 'searchvis', 'mcpcanary', 'welcomeads', 'lifecycleedu', 'connhealth']);
|
|
1259
|
+
export const PANEL_HIDDEN_JOB_TYPES = Object.freeze(['purge', 'agentidle', 'agentsetupmail', 'uploadretention', 'connectordrift', 'falwatch', 'faceassetsweep', 'searchvis', 'mcpcanary', 'welcomeads', 'lifecycleedu', 'connhealth', 'chargednothing', 'emailfeedback']);
|
|
1260
1260
|
const ageText = (h) => (h == null || !Number.isFinite(+h) ? '' : +h < 1 ? 'just now' : +h < 48 ? `${Math.round(+h)}h ago` : `${Math.round(+h / 24)}d ago`);
|
|
1261
1261
|
const VIDEO_RE = /\.(mp4|webm|mov|m4v)([?#]|$)/i;
|
|
1262
1262
|
const clip = (s, n) => { const t = String(s ?? '').replace(/\s+/g, ' ').trim(); return t.length > n ? t.slice(0, n - 1) + '…' : t; };
|
package/mcp/tools.mjs
CHANGED
|
@@ -4025,16 +4025,18 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
4025
4025
|
const _resLine = 'RESOLUTION: each model\'s `resolutions` list is its REAL enum (and `creditsByRes` prices every tier). Ask for a tier a model does not list and the render is delivered at that model\'s best available tier instead — the reply does not say so — so read `resolutions` here before promising anyone 1080p or 4k.';
|
|
4026
4026
|
// recast_motion's two tiers, priced off the same live quote the app's costs page reads (toolExamples), never a number here.
|
|
4027
4027
|
const _mq = (q) => (q ? Object.entries(q).map(([s, c]) => `${s}s=${c}cr`).join(' ') : null);
|
|
4028
|
-
const _motionLine = _mq(d.toolExamples?.motion) ? `\nRecast motion (recast_motion, billed per output second): tier pro (default) ${_mq(d.toolExamples.motion)}${_mq(d.toolExamples.motionStandard) ? `; tier standard ${_mq(d.toolExamples.motionStandard)}` : ''}` : '';
|
|
4028
|
+
const _motionLine = _mq(d.toolExamples?.motion) ? `\nRecast motion (recast_motion, billed per output second): tier pro (default) ${_mq(d.toolExamples.motion)}${_mq(d.toolExamples.motionStandard) ? `; tier standard ${_mq(d.toolExamples.motionStandard)}` : ''}${d.toolExamples.motionH3 ? `; engine h3 (a person clip recast with people) 720p ${_mq(d.toolExamples.motionH3['720p'])}, 1080p ${_mq(d.toolExamples.motionH3['1080p'])}` : ''}` : '';
|
|
4029
4029
|
// THE TALKING-AVATAR ENGINES (generate_avatar `engine`), priced live per second of speech — never a number here.
|
|
4030
4030
|
const _avLine = (d.avatarEngines || []).length ? `\nTalking-avatar engines (generate_avatar engine): ${(d.avatarEngines || []).map(e => `${e.id}${e.default ? ' (default)' : ''} = ${e.label}${e.id === 'natural' ? ` · ${e.model}` : ''}, ~${e.creditsPerSecond}cr per second of speech, ${(e.resolutions || []).join('/')}${e.oneAtATime ? ', renders one video at a time (about ' + e.renderSecondsPerAudioSecond + 'x the speech length plus any wait), speech under 60s' : ''}`).join('; ')}` : '';
|
|
4031
4031
|
// EXTEND + RESTYLE, read off the same status (2026-10-02): which models continue a clip and by how much per call,
|
|
4032
4032
|
// and the one-tap looks restyle_video takes. Derived, never a hand list.
|
|
4033
4033
|
const _extLine = (d.options?.video?.models || []).filter(m => m.extend).map(m => `${m.id} +${m.extend.minSeconds}${m.extend.maxSeconds !== m.extend.minSeconds ? `-${m.extend.maxSeconds}` : ''}s`).join(', ');
|
|
4034
4034
|
const _rsLine = (d.restyleStyles || []).map(x => x.id).join(', ');
|
|
4035
|
+
// MULTI-SHOT + its per-shot prompt cap (2026-10-05): the cap is a vendor rule the schema hides, so it is said here.
|
|
4036
|
+
const _shotLine = (d.options?.video?.models || []).filter(m => m.multiShot).map(m => `${m.id}${m.shotPromptMaxChars ? ` (each shot prompt at most ${m.shotPromptMaxChars} characters; longer is refused, never cut)` : ''}`).join(', ');
|
|
4035
4037
|
const _rm = d.remake; // the remake engines (recast_motion / restyle_video / edit_video engine), priced live
|
|
4036
4038
|
const _rmLine = _rm ? `\nRemake engines (recast_motion, restyle_video, edit_video engine): auto picks, for a clip with no real-looking person, ${Object.entries(_rm.defaults || {}).map(([k, v]) => `${k} → ${v}`).join(', ')}; with a person in the clip, ${Object.entries(_rm.faceDefaults || {}).map(([k, v]) => `${k} → ${v}`).join(', ')}${_rm.personClipLibraryDefaults ? ` (a person clip our face library can carry, i.e. one of our renders or faceRoute on a paid plan: ${Object.entries(_rm.personClipLibraryDefaults).map(([k, v]) => `${k} → ${v}`).join(', ')})` : ''}. seedance ${(_rm.seconds || []).join('-')}s, up to ${_rm.imagesMax} pictures, ${Object.entries(_rm.seedanceCredits || {}).map(([res, m]) => `${res} ${Object.entries(m).map(([s, c]) => `${s}s ${c}cr`).join(' / ')}`).join('; ')}${_rm.wanCredits ? `. wan clips up to ${_rm.wanMaxSourceSeconds}s, ${Object.entries(_rm.wanCredits).map(([res, m]) => `${res} ${Object.entries(m).map(([s, c]) => `${s}s ${c}cr`).join(' / ')}`).join('; ')}` : ''}` : '';
|
|
4037
|
-
const text = `Image: ${d.image ? img : 'unavailable'}\nVideo: ${d.video ? vid : 'unavailable'}\n${_lenLine}${_extLine ? `\nExtend a clip (generate_video extend:true + refVideo): ${_extLine}` : ''}${_rsLine ? `\nRestyle looks (restyle_video style): ${_rsLine}` : ''}${_rmLine}\n${_pickLine}\n${_resLine}${_motionLine}${_avLine}\nVoice engines (generate_voice): ${voice}\nWriting models (generate_text): ${llm}\ncanEdit:${d.canEdit} canAvatar:${d.canAvatar}\nRecipes (${(d.recipes || []).length}): ${(d.recipes || []).slice(0, 20).map(r => r.id).join(', ')}…\n\n${CAPABILITY_MAP}`;
|
|
4039
|
+
const text = `Image: ${d.image ? img : 'unavailable'}\nVideo: ${d.video ? vid : 'unavailable'}\n${_lenLine}${_extLine ? `\nExtend a clip (generate_video extend:true + refVideo): ${_extLine}` : ''}${_shotLine ? `\nMulti-shot clips (generate_video shots): ${_shotLine}` : ''}${_rsLine ? `\nRestyle looks (restyle_video style): ${_rsLine}` : ''}${_rmLine}\n${_pickLine}\n${_resLine}${_motionLine}${_avLine}\nVoice engines (generate_voice): ${voice}\nWriting models (generate_text): ${llm}\ncanEdit:${d.canEdit} canAvatar:${d.canAvatar}\nRecipes (${(d.recipes || []).length}): ${(d.recipes || []).slice(0, 20).map(r => r.id).join(', ')}…\n\n${CAPABILITY_MAP}`;
|
|
4038
4040
|
return ok(text + connLine, d);
|
|
4039
4041
|
}));
|
|
4040
4042
|
|
|
@@ -5746,14 +5748,14 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
5746
5748
|
const brief = Object.fromEntries(Object.entries({ goal, goalNote, pillars, formats, avoid }).filter(([, v]) => v !== undefined));
|
|
5747
5749
|
const d = await apiPut('/api/schedule/refill', { ...rest, ...(Object.keys(brief).length ? { brief } : {}) });
|
|
5748
5750
|
return ok(d.enabled
|
|
5749
|
-
? `Autoposting is ON in ${d.mode === 'auto' ? 'AUTO-PUBLISH mode — each run makes fresh posts and schedules them to go out on their own.' : 'REVIEW mode — each run makes fresh posts and keeps them as drafts; nothing is scheduled until you approve them with run_post_refill.'} get_post_refill lists the channels it posts to and what it costs a day. Run run_post_refill (a
|
|
5751
|
+
? `Autoposting is ON in ${d.mode === 'auto' ? 'AUTO-PUBLISH mode — each run makes fresh posts and schedules them to go out on their own.' : 'REVIEW mode — each run makes fresh posts and keeps them as drafts; nothing is scheduled until you approve them with run_post_refill.'} get_post_refill lists the channels it posts to and what it costs a day. Run run_post_refill (a preview by default: it renders and queues nothing, and writing its copy costs a few credits) to see a plan.`
|
|
5750
5752
|
: 'Autoposting is OFF. The recurring job is gone and nothing new will be made. Posts already in the calendar are untouched — cancel those with cancel_scheduled if you want them gone.', d);
|
|
5751
5753
|
}));
|
|
5752
5754
|
server.registerTool('run_post_refill', {
|
|
5753
5755
|
title: 'Run autoposting / review its drafts',
|
|
5754
|
-
description: 'Run autoposting NOW instead of waiting for its daily turn, or act on the drafts it made. PREVIEW BY DEFAULT: it returns the posts it WOULD make — each new picture’s scene, the caption, the channels and the price — rendering nothing and queueing nothing. dryRun:false actually renders a fresh image or video for each post within the budget (credits are spent) and then, by mode, schedules them ("auto") or keeps them as drafts ("review"). It never re-posts the Library. REVIEWING A BATCH: pass approve (draft ids, or ["all"]) to schedule drafts, discard to delete them, edit ([{id, message?, captions?, title?, at?, channels?}]) to change one first; get_post_refill lists them. A scheduled post can still be pulled before it goes out with cancel_scheduled. Every caption is screened against the voice rules and a failing one is dropped, so a plan can come back shorter than the cadence — the reason is in the notes.',
|
|
5756
|
+
description: 'Run autoposting NOW instead of waiting for its daily turn, or act on the drafts it made. PREVIEW BY DEFAULT: it returns the posts it WOULD make — each new picture’s scene, the caption, the channels and the price — rendering nothing and queueing nothing. The preview still WRITES the scenes and copy with a model, which bills a few credits (the reply says how many). dryRun:false actually renders a fresh image or video for each post within the budget (credits are spent) and then, by mode, schedules them ("auto") or keeps them as drafts ("review"). It never re-posts the Library. REVIEWING A BATCH: pass approve (draft ids, or ["all"]) to schedule drafts, discard to delete them, edit ([{id, message?, captions?, title?, at?, channels?}]) to change one first; get_post_refill lists them. A scheduled post can still be pulled before it goes out with cancel_scheduled. Every caption is screened against the voice rules and a failing one is dropped, so a plan can come back shorter than the cadence — the reason is in the notes.',
|
|
5755
5757
|
inputSchema: {
|
|
5756
|
-
dryRun: z.boolean().optional().describe('default TRUE (a preview: nothing rendered, nothing queued). false renders fresh creative and schedules or drafts it per the mode.'),
|
|
5758
|
+
dryRun: z.boolean().optional().describe('default TRUE (a preview: nothing rendered, nothing queued; the copy it writes bills a few credits). false renders fresh creative and schedules or drafts it per the mode.'),
|
|
5757
5759
|
force: z.boolean().optional().describe('plan a preview even while autoposting is switched off — useful for showing someone what it would do before they turn it on.'),
|
|
5758
5760
|
approve: z.array(z.string()).optional().describe('REVIEW: draft ids to schedule, or ["all"]. A draft whose time has passed moves to the next free posting slot.'),
|
|
5759
5761
|
discard: z.array(z.string()).optional().describe('REVIEW: draft ids to delete, or ["all"]. Nothing is posted for them.'),
|
|
@@ -5786,7 +5788,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
5786
5788
|
return ok([
|
|
5787
5789
|
d.summary || '',
|
|
5788
5790
|
lines.length ? `\nThe posts:\n${lines.join('\n')}` : '',
|
|
5789
|
-
(d.noChannels || d.zeroBudget || (d.enabled === false && !(d.posts || []).length)) ? '' : d.dryRun ?
|
|
5791
|
+
(d.noChannels || d.zeroBudget || (d.enabled === false && !(d.posts || []).length)) ? '' : d.dryRun ? `\nNOTHING WAS RENDERED OR QUEUED — this was a preview${d.copyCredits ? ` (writing its copy cost ${d.copyCredits} credit(s))` : ''}. Show it to the user; call again with dryRun:false only if they say yes.`
|
|
5790
5792
|
: (d.mode === 'auto' ? `\n${q} post(s) scheduled. Pull any of them before it goes out with cancel_scheduled.` : `\n${(d.drafts || []).length} draft(s) waiting for approval — approve, edit or discard them with run_post_refill.`),
|
|
5791
5793
|
qFail.length ? `\n${qFail.length} could not be queued: ${qFail.map(x => `${x.at} — ${x.error}`).join(' · ')}` : '',
|
|
5792
5794
|
(d.notes || []).length ? `\nNotes: ${d.notes.join(' · ')}` : '',
|
|
@@ -18671,7 +18673,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
18671
18673
|
title: 'Generate video',
|
|
18672
18674
|
description: 'Render a RAW video clip from your own prompt and return its served mp4 URL. For finished brand ADS prefer render_ad (it runs the Studio quality pipeline — composited text, clean speech, end card, music); use this for raw/experimental clips or precise manual control. ONE generation = one continuous clip up to the model’s longest listed duration — the longest-clip model in the catalog today renders a full multi-beat spot of up to 30 SECONDS in ONE unbroken take with native synchronized audio, so never assume a generic 8–10s cap and never stitch something that fits one clip; durationSeconds must be one of the model’s durations from hermoso_capabilities, which is the live list. TO GET A SPECIFIC MODEL, NAME IT in `model`: an unnamed render is routed by the server’s own auto-pool, which is narrower than the catalog, so the longest-clip and highest-resolution models are reached by naming them. Renders take 1–3 min. refImage anchors the opening frame; ttsScript adds a voiceover. AUDIO IS NOT FREE AND NOT OPTIONAL BY DEFAULT: a clip delivered with no audio of its own gets a music bed composed and CHARGED on top of the render (see musicMood and audio) — on a cheap short draft the bed can cost as much as the clip. Pass refVideo (a clip URL) to EDIT an existing video instead — the omni engine transforms that clip per your prompt, inheriting its canvas + length (aspectRatio/durationSeconds are ignored for an edit). RAW MODEL ACCESS: by default a few small guards are appended (packaging/label safety with no reference image, a negative prompt where the model takes one, reference-binding lines) and hex colour codes become colour names; ' + RAW_TOOL_NOTE + ' Spends credits (Starter plan is video-blocked server-side).',
|
|
18673
18675
|
inputSchema: {
|
|
18674
|
-
prompt: z.string().describe('the video prompt / shot description (for a refVideo edit, this is the transformation instruction)'),
|
|
18676
|
+
prompt: z.string().optional().describe('the video prompt / shot description (for a refVideo edit, this is the transformation instruction); optional with `shots`'),
|
|
18675
18677
|
raw: z.boolean().optional().describe('RAW MODEL ACCESS: dispatch this prompt to the model BYTE-IDENTICAL — no appended packaging/label guidance, no negative prompt, no reference-binding lines, no hex-to-colour-name rewrite. Use it when you want the model itself rather than Hermoso\'s render craft. Two vendor-required fixes still apply: extra @ImageN tokens are dropped and an over-long prompt is trimmed at a sentence. Billing, durable delivery and per-model validation are unchanged.'),
|
|
18676
18678
|
refImage: z.string().optional().describe('local path or URL to anchor the first frame. ' + LIKENESS_TERMS),
|
|
18677
18679
|
speak: z.string().optional().describe('A PERSON SAYING THESE EXACT WORDS TO CAMERA — the default for any "make my photo talk" / spokesperson / talk-to-camera ask. Pass with `creator` or a portrait as `refImage`: the video model films them saying it in their own voice, matched to who they are, and the length follows the words (leave durationSeconds out). `prompt` is then the staging (e.g. "natural", "walking in a park"). Real footage, not an animated photo; generate_avatar is the animated-photo look, only when asked for by name'),
|
|
@@ -18681,7 +18683,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
18681
18683
|
refVideo: z.string().optional().describe("URL of an existing video to EDIT rather than generate from scratch — the clip is transformed per your prompt, inheriting the SOURCE clip’s canvas (aspect ratio) and, on every model hermoso_capabilities marks `sourceLength`, its LENGTH too: those endpoints have no duration parameter, their listed `durations` are the per-second price ladder, and a durationSeconds you send is reported back as unused rather than silently dropped. Trim the source to change the length. Omit `model` for the default editor, or name a model whose videoEdit is true in hermoso_capabilities (a named model that cannot edit is refused, nothing charged); refImage rides along as the look of what the edit adds. With extend:true this is instead the clip to EXTEND. Omit to generate a fresh clip."),
|
|
18682
18684
|
endImage: z.string().optional().describe('local path or URL of the LAST frame: the clip travels from refImage (required with it) to this image. Only models with endFrame true in hermoso_capabilities take it; any other named model is refused by name, nothing charged.'),
|
|
18683
18685
|
loop: z.boolean().optional().describe('true = a seamless loop whose last frame flows back into its first. Only models with loop true in hermoso_capabilities; needs refImage and cannot be combined with endImage.'),
|
|
18684
|
-
shots: z.array(z.object({ prompt: z.string().describe('what happens in this shot'), seconds: z.number().int().min(1).max(15).describe('this shot’s length in whole seconds') })).optional().describe('MULTI-SHOT: one clip cut into these shots, in order. The seconds must add up to a length the model renders; replaces `prompt` (send either). Only models with multiShot true in hermoso_capabilities.'),
|
|
18686
|
+
shots: z.array(z.object({ prompt: z.string().describe('what happens in this shot'), seconds: z.number().int().min(1).max(15).describe('this shot’s length in whole seconds') })).optional().describe('MULTI-SHOT: one clip cut into these shots, in order. The seconds must add up to a length the model renders; replaces `prompt` (send either). Only models with multiShot true in hermoso_capabilities. Each shot prompt is capped per model (shotPromptMaxChars there; Kling 3.0: 512 characters); a longer one is refused free, never cut.'),
|
|
18685
18687
|
extend: z.boolean().optional().describe('true = EXTEND refVideo: the same clip continues per your prompt for durationSeconds more (each model’s extend.minSeconds..maxSeconds in hermoso_capabilities), delivered as ONE clip, source then continuation. Needs model named (a model with extend in hermoso_capabilities).'),
|
|
18686
18688
|
interactionId: z.string().optional().describe('with extend:true on an Omni model: the interactionId returned by an earlier render on that model — continues it from its own stored context instead of re-uploading refVideo.'),
|
|
18687
18689
|
durationSeconds: z.number().optional().describe('length of THIS ONE clip in seconds — pick one of the CHOSEN model’s listed durations from hermoso_capabilities (never a generic guess: the lists differ per model, from 4–8s on the short models up to 30s on the longest-clip one). This is a single continuous generation, so it CANNOT exceed that model’s longest clip: a longer ask is REFUSED with nothing rendered and nothing charged (it is never quietly truncated). Past ~15s only the long-clip models qualify, and an unnamed render is routed by the narrower auto-pool — so NAME the model in `model` when you are asking for a long single take. For a spot longer than any one clip, use plan_ad with durationSeconds then render_ad, which stitches acts of at most one model clip each (on a 15s-clip model, 40s = 15+15+10).'),
|
|
@@ -18702,7 +18704,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
18702
18704
|
audio: z.boolean().optional().describe('default true. false = render SILENT: no native model audio, no music bed, and no bed charge held or billed. This is the ONLY way to decline the automatic bed (see musicMood) — leave it alone for anything that should have sound, and do not combine it with ttsScript.'),
|
|
18703
18705
|
},
|
|
18704
18706
|
outputSchema: { ...JOB_OUT,
|
|
18705
|
-
refused: z.string().optional().describe("set when NOTHING was rendered and nothing charged — 'duration_exceeds_single_clip' or '
|
|
18707
|
+
refused: z.string().optional().describe("set when NOTHING was rendered and nothing charged — 'duration_exceeds_single_clip', 'aspect_unsupported' or 'missing_prompt'"),
|
|
18706
18708
|
maxSingleClipSeconds: z.number().optional().describe('the longest single clip any connected video model can render'),
|
|
18707
18709
|
askedSeconds: z.number().optional().describe('the durationSeconds that could not be honored'),
|
|
18708
18710
|
askedAspectRatio: z.string().optional().describe('the aspectRatio that could not be honored'),
|
|
@@ -18712,6 +18714,10 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
18712
18714
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
18713
18715
|
_meta: openaiMeta(AD_RESULT_URI, 'Rendering your video…', 'Video ready'),
|
|
18714
18716
|
}, wrap(async (a) => {
|
|
18717
|
+
// A PROMPT OR SHOTS, EITHER ONE (2026-10-05). `shots` replaces `prompt` (Kling sends multi_prompt and no prompt at all),
|
|
18718
|
+
// so the schema leaves prompt optional and the one refusal lives here: neither is nothing to render. Free, no network.
|
|
18719
|
+
const _hasShots = Array.isArray(a.shots) && a.shots.length > 0;
|
|
18720
|
+
if (!String(a.prompt || '').trim() && !_hasShots) return ok('NOTHING WAS RENDERED and nothing was charged. Say what the clip should show: send `prompt`, or `shots` (a list of {prompt, seconds}) for a multi-shot clip on a model with multiShot true in hermoso_capabilities.', { refused: 'missing_prompt' });
|
|
18715
18721
|
// NEVER SILENTLY TRUNCATE A LENGTH ASK. One generation = ONE continuous clip, and workVideo resolves an
|
|
18716
18722
|
// over-long ask by picking the longest clip the model has — so a 40s generate_video call used to render 15s and
|
|
18717
18723
|
// report success. A refusal that names the real ceiling and the real alternative is the honest answer, and it
|
|
@@ -21108,14 +21114,14 @@ function memoryNoteVerdict(text) {
|
|
|
21108
21114
|
|
|
21109
21115
|
server.registerTool('recast_motion', {
|
|
21110
21116
|
title: 'Recast motion',
|
|
21111
|
-
description: "Motion transfer: re-perform a clip's motion, camera and timing with YOUR character, product, clothes or place; the clip's sound is kept. image (+ images, named @Image1, @Image2… in prompt) supply who and what. engine auto (default): seedance (Seedance 2.5, ≤9 pictures, 4-30s, keeps the set and every beat; a person clip needs it to be our render or faceRoute, else wan ≤15s / kling). kling = Kling Motion Control (ONE picture, its background becomes the set, 3-30s); wan = Wan 3.0 Prime (≤9 pictures, keeps the set, ≤15s). Billed per output second; ~5 min for 5s; dryRun quotes it.",
|
|
21117
|
+
description: "Motion transfer: re-perform a clip's motion, camera and timing with YOUR character, product, clothes or place; the clip's sound is kept. image (+ images, named @Image1, @Image2… in prompt) supply who and what. engine auto (default): a person clip recast with photos of people -> h3 (H3 Max Recast: ≤4 photos, one per person, 5-30s, no shot over 15s, keeps the set, light, cuts and every beat); otherwise seedance (Seedance 2.5, ≤9 pictures, 4-30s, keeps the set and every beat; a person clip needs it to be our render or faceRoute, else wan ≤15s / kling). kling = Kling Motion Control (ONE picture, its background becomes the set, 3-30s); wan = Wan 3.0 Prime (≤9 pictures, keeps the set, ≤15s). Billed per output second; ~5 min for 5s; dryRun quotes it.",
|
|
21112
21118
|
inputSchema: {
|
|
21113
21119
|
image: z.string().optional().describe('who performs it: the actor/character image URL (@Image1). Required on kling. ' + LIKENESS_TERMS),
|
|
21114
21120
|
images: z.array(z.string()).max(8).optional().describe('seedance/wan: more pictures, @Image2…: a character, product, outfit or place'),
|
|
21115
21121
|
video: z.string().describe('the reference video whose motion to re-perform'),
|
|
21116
21122
|
prompt: z.string().optional().describe("optional, e.g. 'same moves, new location: Tokyo at night'"),
|
|
21117
|
-
engine: z.enum(['auto', 'seedance', 'wan', 'kling']).optional().describe('default auto'),
|
|
21118
|
-
resolution: z.enum(['480p', '720p', '1080p']).optional().describe('seedance and wan; default
|
|
21123
|
+
engine: z.enum(['auto', 'h3', 'seedance', 'wan', 'kling']).optional().describe('default auto'),
|
|
21124
|
+
resolution: z.enum(['480p', '720p', '1080p']).optional().describe('seedance and wan default 1080p; h3 takes 720p or 1080p, default the clip’s own'),
|
|
21119
21125
|
orientation: z.enum(['video', 'image']).optional().describe("kling only: which aspect to keep: the video's (default) or the image's"),
|
|
21120
21126
|
faceRoute: z.enum(['face_lane']).optional().describe(FACE_ROUTE_CLIP_DESC),
|
|
21121
21127
|
tier: z.enum(['pro', 'standard']).optional().describe("kling only: 'pro' (default): 1080p and real hand-object interaction. 'standard': about 25% fewer credits and faster, but 720p, and it tends to mime a held object with empty hands. hermoso_capabilities lists the exact credits for both"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hermoso",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.381",
|
|
4
4
|
"mcpName": "io.github.hermoso-ai/hermoso",
|
|
5
5
|
"description": "Marketing on autopilot, run from your own AI agent. 897 tools. Publishing, scheduling, ad campaign management, comments, DMs and analytics cost no credits on every plan; credits are only for generating creative and for Ad Spy research. AD PLATFORMS: Meta, Google Ads, TikTok Ads, LinkedIn Ads, Reddit Ads, X Ads, Pinterest Ads, Snapchat Ads, Microsoft Advertising, Apple Search Ads, ChatGPT Ads and AppLovin Ads, plus product feeds in Google Merchant Center. PUBLISHING AND SCHEDULING: Facebook, Instagram, Threads, TikTok, YouTube, X, LinkedIn, Pinterest, Bluesky and Telegram. INSTAGRAM DM AUTOMATIONS: a keyword comment, a DM, a story reply or an ig.me link gets an automatic private reply from the brand. AD RESEARCH: the Meta, Google and LinkedIn ad libraries plus organic TikTok, Instagram, YouTube, Threads and Reddit. ANALYTICS: Google Analytics 4, Google Search Console and every connected platform's own post and campaign insights. Also brand onboarding, 50+ image and video generation models, ad scoring, competitor teardowns, Google Drive and OneDrive, a CLI and installable Claude skills.",
|
|
6
6
|
"type": "module",
|