hermoso 0.1.426 → 0.1.428
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/tools.mjs +8 -5
- package/package.json +1 -1
package/mcp/tools.mjs
CHANGED
|
@@ -4364,7 +4364,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
4364
4364
|
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('; ')}${_rm.wanLabel ? ` (${_rm.wanLabel})` : ''}` : ''}${_rm.wanPrimeCredits ? `. wan-prime (Wan 3.0 Prime by name) ${Object.entries(_rm.wanPrimeCredits).map(([res, m]) => `${res} ${Object.entries(m).map(([s, c]) => `${s}s ${c}cr`).join(' / ')}`).join('; ')}` : ''}` : '';
|
|
4365
4365
|
const _je = d.jobEngines; // the certified engine per job (watched side-by-side tests, 2026-10-19)
|
|
4366
4366
|
const _jeLine = _je && _je.defaults ? `\nDefault engine per job (watched side-by-side tests): ${Object.entries(_je.defaults).map(([k, v]) => `${k} → ${v}`).join(', ')}; a talking ad is one take of up to ${_je.talkingMaxSeconds}s. A model the user picks by name is kept; never switch it.` : '';
|
|
4367
|
-
const text = `Image: ${d.image ? img : 'unavailable'}\nVideo: ${d.video ? vid : 'unavailable'}\n${_lenLine}
|
|
4367
|
+
const text = `Image: ${d.image ? img : 'unavailable'}\nVideo: ${d.video ? vid : 'unavailable'}\n${_lenLine}\nExtend a clip (generate_video extend:true + refVideo): by default it cuts to a NEW SHOT of what happens next (new angle, same scene, any i2v model, default ${d.jobEngines?.defaults?.extend || 'certified'})${_extLine ? `; extendMode:'continue' keeps the SAME take instead, on: ${_extLine}` : ''}${_shotLine ? `\nMulti-shot clips (generate_video shots): ${_shotLine}` : ''}${_rsLine ? `\nRestyle looks (restyle_video style): ${_rsLine}` : ''}${_rmLine}${_jeLine}\n${_pickLine}\n${_resLine}${_motionLine}${_avLine}\nVoice engines (generate_voice): ${voice}\nWriting models (generate_text): ${llm}\nPRICES: every credit figure above is an estimate. ${PRICE_ESTIMATE_LINE}\ncanEdit:${d.canEdit} canAvatar:${d.canAvatar}\nRecipes (${(d.recipes || []).length}): ${(d.recipes || []).slice(0, 20).map(r => r.id).join(', ')}…\n\n${CAPABILITY_MAP}`; return ok(text + connLine, d);
|
|
4368
4368
|
}));
|
|
4369
4369
|
|
|
4370
4370
|
server.registerTool('hermoso_credits', {
|
|
@@ -17996,20 +17996,22 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
17996
17996
|
|
|
17997
17997
|
server.registerTool('list_drive_files', {
|
|
17998
17998
|
title: 'List Google Drive files',
|
|
17999
|
-
description: 'List the Google Drive files & folders Hermoso can reach
|
|
17999
|
+
description: 'List the Google Drive files & folders Hermoso can reach: the ones it created plus any the user picked with the Google file picker in the app (drive.file, never their whole Drive). This is how you find a picked file\'s id. Filter by query (name contains …), folderId, or onlyFolders:true; paginate with pageToken. Read-only. A FOLDER: picking a folder hands over the folder alone, not its files, so a folder can list empty or partly; the reply then carries `pickUrl`, an app link that opens the picker inside that folder (Cmd+A / Ctrl+A selects every file and subfolder). Give the user that exact link, then list again. recursive:true walks every subfolder Hermoso can see (each row has its `path`) and reports picked files whose folder it cannot see.',
|
|
18000
18000
|
inputSchema: {
|
|
18001
18001
|
query: z.string().optional().describe('only files whose name contains this'),
|
|
18002
18002
|
folderId: z.string().optional().describe('list the contents of this folder id'),
|
|
18003
|
+
recursive: z.boolean().optional().describe('with folderId: the whole tree'),
|
|
18003
18004
|
onlyFolders: z.boolean().optional().describe('list folders only'),
|
|
18004
18005
|
pageSize: z.number().optional().describe('rows per page (1–200, default 50)'),
|
|
18005
18006
|
pageToken: z.string().optional().describe('cursor from a previous call'),
|
|
18006
18007
|
includeTrashed: z.boolean().optional().describe('include trashed files (default false)'),
|
|
18007
18008
|
},
|
|
18008
|
-
outputSchema: { files: z.array(z.any()).optional(), cursor: z.string().nullable().optional(), note: z.string().optional(), pickUrl: z.string().optional(), folderVisible: z.boolean().nullable().optional() },
|
|
18009
|
+
outputSchema: { files: z.array(z.any()).optional(), cursor: z.string().nullable().optional(), note: z.string().optional(), pickUrl: z.string().optional(), folderVisible: z.boolean().nullable().optional(), hiddenParents: z.array(z.any()).optional() },
|
|
18009
18010
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
18010
18011
|
}, wrap(async (a) => {
|
|
18011
18012
|
const d = await apiGet('/api/drive/files', a);
|
|
18012
|
-
const lines = (d.files || []).map(f => `• ${f.name} (${f.id})${f.mimeType && f.mimeType.includes('folder') ? ' [folder]' : ''}${f.webViewLink ? ` — ${f.webViewLink}` : ''}`)
|
|
18013
|
+
const lines = (d.files || []).map(f => `• ${f.path || ''}${f.name} (${f.id})${f.mimeType && f.mimeType.includes('folder') ? ' [folder]' : ''}${f.webViewLink ? ` — ${f.webViewLink}` : ''}`)
|
|
18014
|
+
.concat((d.hiddenParents || []).map(g => `• in folder ${g.folderId} (not visible to Hermoso): ${g.count} file(s), e.g. ${(g.names || []).join(', ')}`));
|
|
18013
18015
|
// A folder listing carries the server's note (why it is empty or partial, and the link that fixes it): print it, never a bare "(none)".
|
|
18014
18016
|
return ok(`${(d.files || []).length} item(s):\n${lines.join('\n') || '(none)'}${d.note ? `\n${d.note}${d.pickUrl && !String(d.note).includes(d.pickUrl) ? ` Link: ${d.pickUrl}` : ''}` : ''}`, d);
|
|
18015
18017
|
}));
|
|
@@ -19337,7 +19339,8 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
19337
19339
|
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.'),
|
|
19338
19340
|
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.'),
|
|
19339
19341
|
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 add up to a length the model renders. Replaces `prompt`. Only models with multiShot true in hermoso_capabilities; a shot prompt over that model’s shotPromptMaxChars (Kling 3.0: 512) is refused free, never cut.'),
|
|
19340
|
-
extend: z.boolean().optional().describe('true = EXTEND refVideo
|
|
19342
|
+
extend: z.boolean().optional().describe('true = EXTEND refVideo by durationSeconds (default 5) with what your prompt says happens next, as ONE clip. By default it CUTS to a NEW SHOT: new angle, same scene, room sound carried, no music at the cut. A named model (any i2v) renders it.'),
|
|
19343
|
+
extendMode: z.enum(['cut', 'continue']).optional().describe("with extend: 'continue' = the SAME take past its last frame, only when asked seamless (models with extend in hermoso_capabilities)."),
|
|
19341
19344
|
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.'),
|
|
19342
19345
|
durationSeconds: z.number().optional().describe('length of THIS ONE clip in seconds: one of the CHOSEN model’s listed durations in hermoso_capabilities (they differ per model, 4–8s on the short ones up to 30s). One continuous generation, so a longer ask than the model’s longest clip is REFUSED, nothing charged (never quietly truncated). Past ~15s NAME a long-clip model in `model`. For a longer spot use plan_ad with durationSeconds then render_ad, which stitches acts (on a 15s model, 40s = 15+15+10).'),
|
|
19343
19346
|
aspectRatio: z.string().optional().describe("default '9:16'"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hermoso",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.428",
|
|
4
4
|
"mcpName": "io.github.hermoso-ai/hermoso",
|
|
5
5
|
"description": "AI marketing agent and AI ad generator, run from your own AI agent. 922 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, Telegram and Google Business Profile. 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",
|