hermoso 0.1.233 → 0.1.235

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/README.md CHANGED
@@ -5,7 +5,7 @@ scripts. Research the ads already winning in a market, generate finished image &
5
5
  composited in, copy + CTA included), publish them to your own social channels, and build & manage the ad
6
6
  campaigns behind them — all over [MCP](https://modelcontextprotocol.io) tools, a CLI, or installable Claude skills.
7
7
 
8
- **827 tools.** `tools/list` is always the authoritative set; `hermoso_capabilities` (free) returns the live model
8
+ **828 tools.** `tools/list` is always the authoritative set; `hermoso_capabilities` (free) returns the live model
9
9
  catalog with exact per-render credit costs plus the full capability map.
10
10
 
11
11
  **What it connects to.** Ad platforms: Meta, Google Ads, TikTok Ads, LinkedIn Ads, Reddit Ads, X Ads,
@@ -171,7 +171,7 @@ block entirely if you signed in above; it is there for CI, where the process can
171
171
 
172
172
  Then ask your agent: *“Generate an image ad with Hermoso.”*
173
173
 
174
- ### What the 827 tools cover
174
+ ### What the 828 tools cover
175
175
 
176
176
  **Ad spy / research** — `find_competitors`, `competitor_teardown`, `pull_competitor_ads`, `research_ads`; the
177
177
  Meta / Google / LinkedIn ad libraries (`search_meta_ads`, `search_google_ads`, `search_linkedin_ads`); organic
package/mcp/client.mjs CHANGED
@@ -106,7 +106,8 @@ async function unwrap(res) {
106
106
  // `_viaApi` MARKS AN ERROR THAT ALREADY REACHED THE SERVER, so route() has already recorded it in the error
107
107
  // ledger with the tool name off x-hermoso-tool. wrap() reports ONLY the errors that lack this marker — a local
108
108
  // throw, a schema rejection, a socket reset — which is what stops the twins double-counting every 4xx.
109
- throw Object.assign(new Error(msg), { status: res.status, _viaApi: true, ...(body?.connector ? { connector: body.connector } : {}) });
109
+ // `connectUrl` rides a not-connected 401 with the brand already in it, so a hint never rebuilds the link.
110
+ throw Object.assign(new Error(msg), { status: res.status, _viaApi: true, ...(body?.connector ? { connector: body.connector } : {}), ...(typeof body?.connectUrl === 'string' ? { connectUrl: body.connectUrl } : {}) });
110
111
  }
111
112
  return body && Object.prototype.hasOwnProperty.call(body, 'data') ? body.data : body;
112
113
  }
@@ -307,7 +308,10 @@ export async function connectedProviders() {
307
308
  if (!list) return { connected: new Set(), readOk: false }; // a shape we do not recognise is a failed read
308
309
  const offered = Array.isArray(r?.offered) ? new Set(r.offered.filter((p) => typeof p === 'string' && p)) : null; // null = server predates the field → fail open
309
310
  const gated = r?.gated && typeof r.gated === 'object' ? r.gated : {};
310
- return { connected: new Set(list.filter((p) => typeof p === 'string' && p)), readOk: true, offered, gated };
311
+ // connectLink: the app deep link for THIS brand with a {provider} slot, so a tool held back for a missing
312
+ // connection can hand the user the one-click link instead of only naming the Connectors page.
313
+ const connectLink = typeof r?.connectLink === 'string' && r.connectLink.includes('{provider}') ? r.connectLink : '';
314
+ return { connected: new Set(list.filter((p) => typeof p === 'string' && p)), readOk: true, offered, gated, connectLink };
311
315
  } catch { return { connected: new Set(), readOk: false }; }
312
316
  }
313
317
  // Upload raw file BYTES to /api/upload (150MB, persists → returns {url,kind,bytes}). Overrides the JSON content-type so