hermoso 0.1.377 → 0.1.378

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/tools.mjs +9 -12
  2. package/package.json +1 -1
package/mcp/tools.mjs CHANGED
@@ -5694,7 +5694,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
5694
5694
  // never re-posts the Library; in 'review' mode each batch waits as drafts until approved.
5695
5695
  server.registerTool('get_post_refill', {
5696
5696
  title: 'Autoposting status',
5697
- description: 'Show autoposting (the posting refill) for this profile: whether it is on, its mode (review = each batch of fresh posts waits as drafts for approval; auto = fresh posts are scheduled straight away), its render budget, when it next runs, how many posts are queued, and the DRAFTS waiting for approval (id, time, channels, caption, the new image or video). It also names the channels that CANNOT be posted to and why. Read-only, free.',
5697
+ description: 'Show autoposting (the posting refill) for this profile: whether it is on, its mode (review = each batch of fresh posts waits as drafts for approval; auto = fresh posts are scheduled straight away), WHICH CHANNELS it posts to with each one’s posts per day and posting times, roughly what it costs a day, when it next runs, how many posts are queued, and the DRAFTS waiting for approval (id, time, channels, caption, the new image or video). It also names the channels that CANNOT be posted to and why. Read-only, free.',
5698
5698
  inputSchema: {},
5699
5699
  outputSchema: {
5700
5700
  enabled: z.boolean().optional(), mode: z.string().optional(), daysAhead: z.number().optional(),
@@ -5705,14 +5705,14 @@ function buildTools(rawServer, opts = {}, sink = null) {
5705
5705
  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
5706
5706
  }, wrap(async () => {
5707
5707
  const d = await apiGet('/api/schedule/refill', {});
5708
- const cadence = d.postsPerDay > 0 ? d.postsPerDay : (d.postingSchedule?.slots || []).length;
5709
5708
  const state = !d.enabled ? 'OFF' : (d.mode === 'auto' ? 'ON, AUTO-PUBLISH — each run makes fresh posts and schedules them' : 'ON, REVIEW — each run makes fresh posts and keeps them as drafts until you approve them');
5710
5709
  const drafts = Array.isArray(d.drafts) ? d.drafts : [];
5711
5710
  const draftLines = drafts.slice(0, 20).map((x, i) => ` ${i + 1}. ${x.id} · ${x.at} → ${(x.channels || []).join(', ')} · ${x.kind}\n ${String(x.message || '').replace(/\n+/g, ' / ').slice(0, 200)}\n ${x.media || '(no file)'}`);
5712
5711
  return ok([
5713
5712
  `Autoposting: ${state}.${d.note ? `\n${d.note}` : ''}`,
5714
- `${cadence} post(s) a day at ${(d.postingSchedule?.slots || []).join(', ') || 'no posting times set'} ${d.postingSchedule?.timezone || 'UTC'}, filling ${d.daysAhead} day(s) ahead. ${d.queuedPosts} post(s) queued now.`,
5715
- `Render budget: ${d.maxImagesPerDay || 0} image(s) + ${d.maxVideosPerDay || 0} video(s) a day, ${d.maxCreditsPerDay || 0} credits a day${d.maxCreditsPerWeek ? ` and ${d.maxCreditsPerWeek} a week` : ''} at most. Every post is a new render made for it.${d.zeroBudget ? ` ${d.budgetNote}` : ''} Used: ${d.spend?.today || 0} credits today, ${d.spend?.week || 0} this week.`,
5713
+ `Posting times: ${(d.postingTimes || []).join(', ') || 'none set (add some on the Schedule tab)'} ${d.postingSchedule?.timezone || 'UTC'}. It fills ${d.daysAhead} day(s) ahead; ${d.queuedPosts} post(s) queued now.`,
5714
+ (d.channelsView || []).length ? `Channels:\n${d.channelsView.map((c) => ` ${c.on ? '[on] ' : '[off]'} ${c.label} (${c.channel}): ${c.on ? `${c.postsPerDay} a day${c.times.length ? ` at ${c.times.join(', ')}` : ''}` : 'not posting'}${c.note ? ` · ${c.note}` : ''}`).join('\n')}` : (d.connectedUnreadable ? 'Channels: could not be read just now.' : 'Channels: none connected — autopilot has nowhere to post. Connect one under Workspace ▸ Connectors.'),
5715
+ `Makes ${d.postsPerDayMade || 0} new post(s) a day, every one a fresh render made for it${d.estCreditsPerDay != null && d.postsPerDayMade ? `, about ${d.estCreditsPerDay} credits a day at today's prices` : ''}.${d.zeroBudget ? ` ${d.budgetNote}` : ''} Used: ${d.spend?.today || 0} credits today, ${d.spend?.week || 0} this week.`,
5716
5716
  `Brief: goal ${d.brief?.goal || 'not set (required to switch it on)'}${d.brief?.goalNote ? ` (${d.brief.goalNote})` : ''} · format ${d.brief?.formats || 'mix'}${(d.brief?.pillars || []).length ? ` · pillars: ${d.brief.pillars.join('; ')}` : (d.suggestedPillars || []).length ? ` · no pillars yet; suggested from the brand: ${d.suggestedPillars.join('; ')}` : ''}${(d.brief?.avoid || []).length ? ` · avoid: ${d.brief.avoid.join('; ')}` : ''}`,
5717
5717
  d.suggestAuto ? `${d.approvedBatches} review batches approved so far: auto-publish (mode "auto") is available if the user wants posts to go out without a review step. Ask; never switch it yourself.` : '',
5718
5718
  drafts.length ? `Drafts waiting for approval (${drafts.length}):\n${draftLines.join('\n')}${drafts.length > 20 ? `\n …and ${drafts.length - 20} more` : ''}\nApprove, edit or discard them with run_post_refill (approve / edit / discard).` : 'No drafts waiting for approval.',
@@ -5721,20 +5721,17 @@ function buildTools(rawServer, opts = {}, sink = null) {
5721
5721
  }));
5722
5722
  server.registerTool('set_post_refill', {
5723
5723
  title: 'Configure autoposting',
5724
- description: 'Turn autoposting (the posting refill) on or off and set how it behaves. PASS ONLY WHAT CHANGES. It always makes NEW posts (a fresh image, carousel or video for each) within the render budget, and never re-posts the Library, so turning it on needs a budget: maxImagesPerDay and/or maxVideosPerDay above 0 plus a maxCreditsPerDay or maxCreditsPerWeek ceiling — with a 0 budget it is refused. SWITCHING IT ON ASKS THE BRIEF ONCE: a goal (followers / sales / launch / other) is required; content pillars (get_post_refill suggests some from the brand), formats (mix / video / carousel / image) and an avoid list are optional. Posts are written from the brief, the brand profile, what has worked on each channel (compared only within a channel), the brand’s skills and playbooks and the ads it saved. Ask the user for anything missing (goal, mode, budget) rather than guessing. `mode` picks what happens to each batch: "review" (the default) keeps the fresh posts as drafts until a human approves them (run_post_refill approve), "auto" schedules them straight away. `enabled:false` is the PAUSE — it removes the recurring job outright, and posts already queued are left alone (cancel those with cancel_scheduled). THE CADENCE IS THE BRAND’S POSTING TIMES, not a number here: three posting times means three posts a day.',
5724
+ description: 'Turn autoposting (the posting refill) on or off and set how it behaves. PASS ONLY WHAT CHANGES. It always makes NEW posts (a fresh image, carousel or video for each) and never re-posts the Library. WHERE: every connected channel by default (channels narrows it); HOW OFTEN: the brand’s posting times, and channelPostsPerDay sets fewer on a channel (e.g. {"x":1}). There are no credit limits to set — get_post_refill shows roughly what it costs a day. Switching it on is refused when no channel is connected or nothing a day would be made. SWITCHING IT ON ASKS THE BRIEF ONCE: a goal (followers / sales / launch / other) is required; content pillars (get_post_refill suggests some from the brand), formats (mix / video / carousel / image) and an avoid list are optional. Posts are written from the brief, the brand profile, what has worked on each channel (compared only within a channel), the brand’s skills and playbooks and the ads it saved. Ask the user for anything missing (goal, mode, channels) rather than guessing. `mode` picks what happens to each batch: "review" (the default) keeps the fresh posts as drafts until a human approves them (run_post_refill approve), "auto" schedules them straight away. `enabled:false` is the PAUSE — it removes the recurring job outright, and posts already queued are left alone (cancel those with cancel_scheduled). Three posting times means up to three posts a day; to post MORE per day, add posting times on the Schedule tab.',
5725
5725
  inputSchema: {
5726
- enabled: z.boolean().optional().describe('on/off. true needs a render budget (refused at 0). false PAUSES it: the recurring job is deleted and nothing new is made. Already-queued posts are untouched.'),
5726
+ enabled: z.boolean().optional().describe('on/off. true needs a connected channel and a goal (refused otherwise). false PAUSES it: the recurring job is deleted and nothing new is made. Already-queued posts are untouched.'),
5727
5727
  mode: z.enum(['review', 'auto']).optional().describe('"review" (default): each batch of fresh posts waits as drafts for approval. "auto": fresh posts are scheduled to publish automatically.'),
5728
5728
  daysAhead: z.number().optional().describe('how far ahead to keep the queue full, 1–30 (default 7)'),
5729
5729
  postsPerDay: z.number().optional().describe('cap the posts per day BELOW the number of posting times. 0 (default) = use every posting time, which is where "3 a day" comes from. To post MORE per day, add posting times instead.'),
5730
- maxImagesPerDay: z.number().optional().describe('how many NEW images a day it may render, one per post. 0 = none.'),
5731
- maxVideosPerDay: z.number().optional().describe('how many NEW short videos a day it may render. 0 = none. Video is the expensive one.'),
5732
- maxCreditsPerDay: z.number().optional().describe('a hard credit ceiling per day, checked BEFORE any render starts. It binds independently of the counts above, and 0 means it makes nothing.'),
5733
- maxCreditsPerWeek: z.number().optional().describe('a credit ceiling per week (0 = none). With it, each day may use what the week has left, within any daily cap.'),
5730
+ channelPostsPerDay: z.record(z.number()).optional().describe('per channel, how many posts a day (the first N of the posting times), e.g. {"x":1,"instagram":3}. 0 turns a channel off; null resets it to every posting time. Merged into what is saved.'),
5734
5731
  goal: z.enum(['followers', 'sales', 'launch', 'other']).optional().describe('THE BRIEF: what the posts are for — grow followers, drive sales, promote a launch, or other (say what in goalNote). Required to switch it on.'),
5735
5732
  goalNote: z.string().optional().describe('THE BRIEF: a line about the goal (e.g. the launch date and what is launching).'),
5736
5733
  pillars: z.array(z.string()).optional().describe('THE BRIEF: content pillars / topics, up to 8 short lines. Every post belongs to one. get_post_refill suggests some from the brand profile.'),
5737
- formats: z.enum(['mix', 'video', 'carousel', 'image']).optional().describe('THE BRIEF: what to make — mix (images with a short video now and then, the default), video (short videos only; needs maxVideosPerDay), carousel (3 new images per post; needs maxImagesPerDay ≥ 3), image.'),
5734
+ formats: z.enum(['mix', 'video', 'carousel', 'image']).optional().describe('THE BRIEF: what to make — mix (images with a short video now and then, the default), video (short videos only), carousel (3 new images per post), image.'),
5738
5735
  avoid: z.array(z.string()).optional().describe('THE BRIEF: never mention or show these — topics, claims, competitors, faces.'),
5739
5736
  channels: z.array(z.enum(['facebook', 'instagram', 'threads', 'tiktok', 'youtube', 'linkedin', 'x', 'pinterest', 'google_business', 'bluesky', 'telegram'])).optional().describe('restrict it to these channels. Omit (or send an empty list) to use every connected channel that can carry each post.'),
5740
5737
  boardId: z.string().optional().describe('PINTEREST — which board Pins go on (list_pinterest_boards). Without one, Pinterest is skipped: a Pin on the wrong board is a public mistake, so it is never guessed.'),
@@ -5749,7 +5746,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
5749
5746
  const brief = Object.fromEntries(Object.entries({ goal, goalNote, pillars, formats, avoid }).filter(([, v]) => v !== undefined));
5750
5747
  const d = await apiPut('/api/schedule/refill', { ...rest, ...(Object.keys(brief).length ? { brief } : {}) });
5751
5748
  return ok(d.enabled
5752
- ? `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.'} Budget: ${d.maxImagesPerDay || 0} image(s) + ${d.maxVideosPerDay || 0} video(s) a day, ${d.maxCreditsPerDay || 0} credits at most. Run run_post_refill (a free preview by default) to see a plan.`
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 free preview by default) to see a plan.`
5753
5750
  : '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);
5754
5751
  }));
5755
5752
  server.registerTool('run_post_refill', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hermoso",
3
- "version": "0.1.377",
3
+ "version": "0.1.378",
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",