hermoso 0.1.503 → 0.1.504

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.
Files changed (2) hide show
  1. package/mcp/client.mjs +14 -2
  2. package/package.json +1 -1
package/mcp/client.mjs CHANGED
@@ -152,7 +152,7 @@ export function signInMessage(status, body, msg, isSignedOut = signedOut()) {
152
152
  // unwrap the {data}|{error} envelope; throw a clean Error (with .status) on failure
153
153
  async function unwrap(res, { read = false } = {}) {
154
154
  let body = null;
155
- try { body = await res.json(); } catch {}
155
+ try { body = await res.json(); } catch (e) { if (read && isDeadline(e)) throw readDeadlineError(); } // a body cut off by the read deadline is not an empty answer
156
156
  if (!res.ok) {
157
157
  const msg = signInMessage(res.status, body, (body && (body.error || body.message)) || `HTTP ${res.status}`);
158
158
  // `_viaApi` MARKS AN ERROR THAT ALREADY REACHED THE SERVER, so route() has already recorded it in the error
@@ -299,14 +299,26 @@ async function throughFrontDoor(send, { read }) {
299
299
  return res;
300
300
  }
301
301
 
302
- async function fetchRead(url, init) { return throughFrontDoor(() => fetchReadOnce(url, init), { read: true }); }
302
+ // ── A READ HAS A DEADLINE (2026-11-04) ────────────────────────────────────────────────────────────────────────────
303
+ // fetch has none of its own, only undici's 300-second wait for response headers, and that wait ends in the same bare
304
+ // "fetch failed" as a dropped socket, so the blip retry below SENT THE SLOW READ AGAIN: one hosted
305
+ // read_meta_conversation sat open for the whole 10-minute MCP wall and held off every deploy. A read now ends at
306
+ // READ_DEADLINE_MS with a sentence, and a deadline is never retried (resending a read the server is still working on
307
+ // only doubles the wait). No read a tool makes runs this long: the slowest seen in a week of request logs was under
308
+ // 100 seconds, which is also the edge's limit for a stdio client. A read changes nothing, so ending it is safe.
309
+ export const READ_DEADLINE_MS = Number(process.env.HERMOSO_READ_DEADLINE_MS) > 0 ? Number(process.env.HERMOSO_READ_DEADLINE_MS) : 180000;
310
+ const isDeadline = (e) => !!e && (e.name === 'TimeoutError' || e.name === 'AbortError' || /UND_ERR_(HEADERS|BODY)_TIMEOUT/.test(String(e.cause?.code || '')));
311
+ const readDeadlineError = () => Object.assign(new Error(`Hermoso did not finish that read within ${Math.round(READ_DEADLINE_MS / 1000)} seconds, so it was stopped and nothing came back. It was a read, so nothing was changed. Try again, or ask for less (a smaller limit or one account).`), { status: 504, _readDeadline: true });
312
+ async function fetchRead(url, init) { return throughFrontDoor(() => fetchReadOnce(url, { ...init, signal: AbortSignal.timeout(READ_DEADLINE_MS) }), { read: true }); }
303
313
  async function fetchReadOnce(url, init) {
304
314
  try { return await fetch(url, init); }
305
315
  catch (e) {
316
+ if (isDeadline(e)) throw readDeadlineError();
306
317
  if (!isTransport(e)) throw e;
307
318
  await new Promise((r) => setTimeout(r, 400));
308
319
  try { return await fetch(url, init); }
309
320
  catch (e2) {
321
+ if (isDeadline(e2)) throw readDeadlineError();
310
322
  if (!isTransport(e2)) throw e2;
311
323
  throw Object.assign(new Error('Could not reach Hermoso just now — the request never got a response, so nothing was read. This is a connection problem, not a rejected request; try again.'), { _transport: true, _viaApi: true });
312
324
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hermoso",
3
- "version": "0.1.503",
3
+ "version": "0.1.504",
4
4
  "mcpName": "io.github.hermoso-ai/hermoso",
5
5
  "description": "AI marketing agent and AI ad generator, run from your own AI agent. 1182 tools. No markup on models on Starter and up. Publishing, scheduling, ad campaign management, comments, DMs and analytics cost no credits on any plan; Free includes 10 posts a month and every paid plan posts without a limit. 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, WordPress, Discord, Slack, Whop 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",