hermoso 0.1.141 → 0.1.142
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 +32 -4
- package/package.json +1 -1
package/mcp/tools.mjs
CHANGED
|
@@ -469,6 +469,28 @@ const AD_RESULT_HTML = String.raw`<div id="root"></div>
|
|
|
469
469
|
var POLL_MS = 5000, FIRST_MS = 1500, MAX_POLLS = 72, MAX_MISSES = 3; // 72 x 5s = 6 min, past any render this card carries
|
|
470
470
|
function esc(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { return '&#' + c.charCodeAt(0) + ';'; }); }
|
|
471
471
|
function looksVideo(u) { return /\.(mp4|webm|mov|m4v)([?#]|$)/i.test(String(u || '')); }
|
|
472
|
+
function urlOf(x) { return typeof x === 'string' ? x : ((x && (x.image || x.url || x.src)) || ''); }
|
|
473
|
+
function picks(out, raw) {
|
|
474
|
+
var lists = [raw.images, out.images, out.thumbnails, raw.thumbnails];
|
|
475
|
+
for (var i = 0; i < lists.length; i++) {
|
|
476
|
+
if (!Array.isArray(lists[i])) continue;
|
|
477
|
+
var u = lists[i].map(urlOf).filter(Boolean);
|
|
478
|
+
if (u.length) return u;
|
|
479
|
+
}
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
// NOTHING TO SHOW IS NOT A CARD (2026-08-23). Driving Hermoso inside ChatGPT, Dave's conversation filled up with
|
|
483
|
+
// empty Hermoso cards reading "No media in this result yet." — a full-height bordered box with a wordmark and no
|
|
484
|
+
// content, sometimes three or four in a row. Every widget-bound tool draws this card on EVERY call, and several
|
|
485
|
+
// of their results legitimately carry no media at all: render_ad's dryRun and its needsProductPhoto ask,
|
|
486
|
+
// generate_video's over-length refusal, make_thumbnail's two ask-first gates, a get_job on a job whose type has
|
|
487
|
+
// no media, and ANY tool throw (wrap() answers {content,isError} with no structuredContent at all, so the widget
|
|
488
|
+
// is handed {}). In every one of those the model's own text already says what happened, so the card adds a box
|
|
489
|
+
// and no information. The three states that MUST stay visible are unchanged — media, a pending render, and a
|
|
490
|
+
// failure — and only the fourth, "nothing", collapses to literally nothing.
|
|
491
|
+
function collapse() {
|
|
492
|
+
try { var api = window.openai; if (api && typeof api.notifyIntrinsicHeight === 'function') api.notifyIntrinsicHeight(0); } catch (e) {}
|
|
493
|
+
}
|
|
472
494
|
// THREE RESULT SHAPES REACH THIS ONE WIDGET and only one of them is flat. Reading just the flat one is why a
|
|
473
495
|
// finished job rendered as "No media": renderJob nests under .raw, but get_job returns the JOB, whose payload is
|
|
474
496
|
// at .result.data. One resolver, so a field added to either reader is found by both.
|
|
@@ -486,9 +508,14 @@ const AD_RESULT_HTML = String.raw`<div id="root"></div>
|
|
|
486
508
|
// creditsUsed sits BESIDE data on a job envelope ({result:{data:{…}, creditsUsed}}), not inside it — read off a real job row.
|
|
487
509
|
var c = out.creditsUsed != null ? out.creditsUsed : (out.credits != null ? out.credits : (raw.creditsUsed != null ? raw.creditsUsed : (out.result ? out.result.creditsUsed : null)));
|
|
488
510
|
v.credits = c;
|
|
489
|
-
|
|
511
|
+
// A PICTURE SET ARRIVES UNDER THREE NAMES AND IN TWO SHAPES, and reading only images[] is why every
|
|
512
|
+
// SUCCESSFUL make_thumbnail call painted "No media in this result yet." (2026-08-23): that tool answers
|
|
513
|
+
// {thumbnails:[{image,label,…}]} — a real, delivered, PAID render this card threw away. So resolve the first
|
|
514
|
+
// non-empty list out of all four spellings and map each entry to its url, string or object alike.
|
|
515
|
+
v.slides = picks(out, raw);
|
|
516
|
+
if (v.slides && v.slides.length === 1) { v.image = v.slides[0]; v.slides = null; } // one picture is a picture, not a one-tile grid
|
|
490
517
|
v.video = out.video || raw.video || null;
|
|
491
|
-
v.image = out.image || raw.image || null;
|
|
518
|
+
v.image = v.image || out.image || raw.image || null;
|
|
492
519
|
v.poster = out.poster || raw.poster || null;
|
|
493
520
|
var any = out.url || raw.url || null;
|
|
494
521
|
if (!v.video && !v.image && any) { if (looksVideo(any)) { v.video = any; } else { v.image = any; } }
|
|
@@ -524,7 +551,7 @@ const AD_RESULT_HTML = String.raw`<div id="root"></div>
|
|
|
524
551
|
if (v.credits != null) pills += '<span class="pill">' + esc(v.credits) + ' credits</span>';
|
|
525
552
|
var footer = '<div class="meta">' + pills + '<span class="spacer"></span><span class="wordmark">Hermoso</span></div>';
|
|
526
553
|
var body;
|
|
527
|
-
if (v.slides) body = '<div class="grid">' + v.slides.map(function (u) { return '<img src="' + esc(u) + '" alt="
|
|
554
|
+
if (v.slides) body = '<div class="grid">' + v.slides.map(function (u) { return '<img src="' + esc(u) + '" alt="rendered image" loading="lazy">'; }).join('') + '</div>';
|
|
528
555
|
else if (v.video) body = '<div class="media"><video controls muted autoplay loop playsinline preload="metadata"' + (v.poster ? ' poster="' + esc(v.poster) + '"' : '') + ' src="' + esc(v.video) + '"></video></div>';
|
|
529
556
|
else if (v.image) body = '<div class="media"><img src="' + esc(v.image) + '" alt="generated ad"></div>';
|
|
530
557
|
else if (v.error) body = '<div class="empty bad">This render did not finish: ' + esc(v.error) + '</div>';
|
|
@@ -536,7 +563,8 @@ const AD_RESULT_HTML = String.raw`<div id="root"></div>
|
|
|
536
563
|
else note = 'Checking every few seconds; this card updates itself the moment it is ready.';
|
|
537
564
|
body = '<div class="pending"><span class="spin"></span><span>Rendering' + (v.jobId ? ' — job ' + esc(v.jobId) : '') + '. Video renders take 1–3 minutes.<span class="sub">' + note + '</span></span></div>';
|
|
538
565
|
}
|
|
539
|
-
|
|
566
|
+
// …and with no media, no pending render and no failure there is nothing to draw: paint NOTHING (see collapse()).
|
|
567
|
+
if (!body) { root.innerHTML = ''; stop(); collapse(); return; }
|
|
540
568
|
root.innerHTML = '<div class="card">' + body + footer + '</div>';
|
|
541
569
|
if (v.pending) { schedule(); } else { stop(); }
|
|
542
570
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hermoso",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.142",
|
|
4
4
|
"mcpName": "io.github.hermoso-ai/hermoso",
|
|
5
5
|
"description": "AI ad studio and marketing MCP server with 681 tools. Research the ads already running in any market, generate finished image, video and UGC avatar ads, publish and schedule them to your own channels, build and manage the ad campaigns behind them, and read what they achieved. AD PLATFORMS: Meta, Google Ads, TikTok Ads, LinkedIn Ads, Reddit Ads, X Ads, Pinterest Ads, Snapchat Ads, Microsoft Advertising, Apple Search Ads and ChatGPT Ads, plus product feeds in Google Merchant Center. PUBLISHING AND SCHEDULING: Facebook, Instagram, Threads, TikTok, YouTube, X, LinkedIn, Pinterest, Bluesky and Telegram. 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",
|