@hubfluencer/mcp 0.9.1 → 0.10.0
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 +8 -6
- package/dist/index.js +53 -7
- package/package.json +1 -1
- package/src/core.ts +20 -0
- package/src/index.ts +94 -13
- package/src/output-schemas.ts +51 -2
- package/src/uploads.ts +1 -0
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ claude mcp add hubfluencer --env HUBFLUENCER_API_TOKEN=YOUR_TOKEN -- npx -y @hub
|
|
|
68
68
|
|
|
69
69
|
## Tools
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
93 tools, grouped by workflow. Every tool prices its action in its own `description` — costs
|
|
72
72
|
below are the ground truth. **"$0" means it spends no video credits**; planning/AI-helper tools
|
|
73
73
|
instead draw a **free daily quota of 20 AI assists** (top up with `unlock_ai_assists`: 1 credit
|
|
74
74
|
→ +10). Reads need the `video:read` scope; writes need `video:generate`. Nothing here needs
|
|
@@ -80,9 +80,9 @@ instead draw a **free daily quota of 20 AI assists** (top up with `unlock_ai_ass
|
|
|
80
80
|
|---|---|---|
|
|
81
81
|
| **`make_video`** | **One shot: prompt → finished MP4** (create → **price** → start → poll → download). `kind:"auto"` picks a multi-scene editor ad for ad/promo/story briefs, a short for simple ones (reported as `kind_inferred`). Pass `dry_run:true` to preview the cost without spending, or `max_credits` to cap it. | short **15**, editor ~**28** (`dry_run`/`max_credits` gate it) |
|
|
82
82
|
| `create_short` | Create a short draft (12s vertical: two 6s AI segments + title overlay + music; 14s when it ends on a poster or brand-lockup end card). Set `headline` (≈4 words, ≤40 chars), `subheadline`, `music_vibe`, and an explicit `visual_language` (`kinetic_creator` is a strong default) — and **always `cta_text`** (a short without a CTA doesn't convert; blank `cta_text` → the server renders a neutral localized ask, e.g. "Learn more" — a truly CTA-less short is not currently supported). End-card fields: `closing_claim` (≤80), `brand_name` (≤40), `end_card` (`"auto"`\|`"none"`). Other opt-in conversion graphics: `offer_text`/`badge_text`/`star_rating` — only what you can substantiate. | **0** |
|
|
83
|
-
| `update_short` | Patch an existing short draft — brief (`product_prompt`), copy, styling, end-card fields (`closing_claim`/`brand_name`/`poster_includes_lockup`/`end_card`), or the opt-in conversion graphics; only the fields you pass change. Clear one with `""` (`[]` for `text_beats`, `0` for `star_rating`). Apply text/style/CTA/end-card edits to a generated short free with `rerender_short`; footage/music edits need `generate_short`. | **0** |
|
|
83
|
+
| `update_short` | Patch an existing short draft — brief (`product_prompt`), copy, styling, end-card fields (`closing_claim`/`brand_name`/`poster_includes_lockup`/`end_card`), logo overlay placement (`short_logo_position`/`short_logo_treatment`), or the opt-in conversion graphics; only the fields you pass change. Clear one with `""` (`[]` for `text_beats`, `0` for `star_rating`). Apply text/style/CTA/end-card/logo edits to a generated short free with `rerender_short`; footage/music edits need `generate_short`. | **0** |
|
|
84
84
|
| `generate_short` | Render an existing short — re-rolls the footage + music (fresh AI generation). If headline/subheadline/beats are all blank the server auto-writes the copy first (`skip_auto_text:true` for a deliberately bare clip). Safe to re-call (in-flight = reported, no double charge; a failed short can re-generate). | **15** |
|
|
85
|
-
| `rerender_short` | **FREE re-render** — applies the short's current text/style/CTA/end-card state over the already-paid footage + music. Use after `update_short` for copy/style/CTA tweaks; footage or music changes need `generate_short` instead. Needs one completed generation first (422 `short_not_ready` otherwise); changed footage/music inputs return 422 `short_paid_regeneration_required`. A failed free re-render leaves the delivered video intact — retry `rerender_short`, it stays free. | **0** |
|
|
85
|
+
| `rerender_short` | **FREE re-render** — applies the short's current text/style/CTA/end-card/logo state over the already-paid footage + music. Use after `update_short` for copy/style/CTA tweaks, or after a logo change (`set_short_logo`, or `short_logo_position`/`short_logo_treatment`); footage or music changes need `generate_short` instead. Needs one completed generation first (422 `short_not_ready` otherwise); changed footage/music inputs return 422 `short_paid_regeneration_required`. A failed free re-render leaves the delivered video intact — retry `rerender_short`, it stays free. | **0** |
|
|
86
86
|
| `generate_short_text` | AI-draft editable headline / subheadline / caption beats for a short. | 1 assist |
|
|
87
87
|
| `create_editor_ad` | Create an editor project, attach optional local product/logo/closing assets, and return the live autopilot quote. Omit `max_credits` to stop free; provide an approved cap to launch. For a mascot/hero product that must remain identical with no humans, pass `cast_mode:"product_only"` plus `product_image_path`. | **0** until capped launch |
|
|
88
88
|
| `start_autopilot` | Quote autopilot on an **existing** editor draft when called with only `slug`; launch only with an explicit `max_credits` that covers the estimate and an adequate balance. On a completed project, pass `restart:true` to quote and build a fresh creative run; omit it to resume pending edits. | **0** quote; credits on capped launch |
|
|
@@ -133,6 +133,7 @@ images at **8 MiB (8,388,608 bytes)**; closing/short/catalog images at **20 MiB
|
|
|
133
133
|
| `set_closing_image` | Set the ~2s end-card image (JPEG/PNG, ≤20 MiB; upload a file, or reuse the product image with `from_product:true`). |
|
|
134
134
|
| `set_logo` | Upload a brand logo overlay (JPEG/PNG, ≤1 MiB; editor only; optional treatment/position/duration). |
|
|
135
135
|
| `set_short_product` | Attach a product image (JPEG/PNG, ≤20 MiB) to a **short**. |
|
|
136
|
+
| `set_short_logo` | Attach an exact deterministic logo overlay (JPEG/PNG, ≤5 MiB) to a **short**; choose its corner and `throughout`/`end_card`/`none` (hide) treatment. The logo is never sent to the video model. |
|
|
136
137
|
| `set_short_poster` | Set a short's end-card poster (JPEG/PNG, ≤20 MiB; extends the render 12s → 14s). Pass `poster_includes_lockup:true` if the poster already carries brand name/CTA/badges — suppresses the overlaid claim/CTA so they don't collide. |
|
|
137
138
|
|
|
138
139
|
### Status & delivery
|
|
@@ -233,7 +234,7 @@ edit are **0 credits** — you only pay to generate/render.
|
|
|
233
234
|
|---|---|
|
|
234
235
|
| Any draft create / edit / reorder | 0 |
|
|
235
236
|
| Short render | **15** |
|
|
236
|
-
| Short re-render (`rerender_short` — text/style/CTA/end-card over existing footage) | **0** |
|
|
237
|
+
| Short re-render (`rerender_short` — text/style/CTA/end-card/logo over existing footage) | **0** |
|
|
237
238
|
| Editor AI scene (`generate_segment`) | **5** (4 each in a batch of ≥3) |
|
|
238
239
|
| Editor scene re-roll (`regenerate_segment`, new version of a completed scene) | **4** |
|
|
239
240
|
| Editor voice | **3** |
|
|
@@ -259,8 +260,9 @@ If it returns `terminal:false`, the render is still going — call `wait_for_com
|
|
|
259
260
|
`create_editor_ad` (free configure + quote) → obtain spend approval → `start_autopilot {slug, max_credits}` → `wait_for_completion {kind:"editor"}` → `download_result`.
|
|
260
261
|
|
|
261
262
|
**Iterate a short for free (never re-roll):** the first `generate_short` (15 credits) buys the footage +
|
|
262
|
-
music; after that, every text/style/CTA/end-card tweak re-renders over them for **0 credits** —
|
|
263
|
-
`update_short` (new hook/CTA/style) → `rerender_short` → compare → repeat → keep the winner.
|
|
263
|
+
music; after that, every text/style/CTA/end-card/logo tweak re-renders over them for **0 credits** —
|
|
264
|
+
`update_short` (new hook/CTA/style) → `rerender_short` → compare → repeat → keep the winner. Logo
|
|
265
|
+
placement (`set_short_logo`, or `short_logo_position`/`short_logo_treatment`) is free too. Only
|
|
264
266
|
footage/music-affecting edits (`product_prompt`, product image, `creative_format`, `visual_language`,
|
|
265
267
|
`theme`, `music_vibe`, `music_instruments`, `language`) need another `generate_short`. Edits that
|
|
266
268
|
add/remove the end card (poster, `end_card`) can shift the video between 12s and 14s; the pinned music
|
package/dist/index.js
CHANGED
|
@@ -29498,6 +29498,16 @@ function normalizeStatus(kind, slug, data) {
|
|
|
29498
29498
|
video_url: videoUrl,
|
|
29499
29499
|
error: d.error_message ?? null
|
|
29500
29500
|
};
|
|
29501
|
+
if (typeof d.failed_stage === "string")
|
|
29502
|
+
status.failed_stage = d.failed_stage;
|
|
29503
|
+
if (typeof d.failure_code === "string")
|
|
29504
|
+
status.failure_code = d.failure_code;
|
|
29505
|
+
if (d.failure_details && typeof d.failure_details === "object")
|
|
29506
|
+
status.failure_details = d.failure_details;
|
|
29507
|
+
if (typeof d.failure_source === "string")
|
|
29508
|
+
status.failure_source = d.failure_source;
|
|
29509
|
+
if (typeof d.failure_attempt === "number")
|
|
29510
|
+
status.failure_attempt = d.failure_attempt;
|
|
29501
29511
|
if (typeof d.latest_render_free === "boolean") {
|
|
29502
29512
|
status.latest_render_free = d.latest_render_free;
|
|
29503
29513
|
}
|
|
@@ -30233,7 +30243,7 @@ function readStoredCredentials() {
|
|
|
30233
30243
|
// package.json
|
|
30234
30244
|
var package_default = {
|
|
30235
30245
|
name: "@hubfluencer/mcp",
|
|
30236
|
-
version: "0.
|
|
30246
|
+
version: "0.10.0",
|
|
30237
30247
|
description: "Model Context Protocol server for Hubfluencer — let AI agents generate post-ready shorts and editor ads.",
|
|
30238
30248
|
license: "MIT",
|
|
30239
30249
|
author: "Monocursive <contact@monocursive.com>",
|
|
@@ -30546,6 +30556,11 @@ var getStatusOutput = exports_external.object({
|
|
|
30546
30556
|
stale: exports_external.boolean().optional(),
|
|
30547
30557
|
active_segment_ids: exports_external.array(exports_external.union([exports_external.number(), exports_external.string()])).optional(),
|
|
30548
30558
|
stale_segment_ids: exports_external.array(exports_external.union([exports_external.number(), exports_external.string()])).optional(),
|
|
30559
|
+
failed_stage: exports_external.string().optional().describe("Short only: exact API failure phase behind a terminal failure."),
|
|
30560
|
+
failure_code: exports_external.string().optional().describe("Short only: stable machine-readable failure classification."),
|
|
30561
|
+
failure_details: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("Short only: structured provider/QC details for recovery."),
|
|
30562
|
+
failure_source: exports_external.string().optional().describe("Short only: worker/recovery layer that finalized the failure."),
|
|
30563
|
+
failure_attempt: exports_external.number().optional().describe("Short only: paid generation attempt number that failed."),
|
|
30549
30564
|
latest_render_free: exports_external.boolean().optional(),
|
|
30550
30565
|
last_free_rerender_failed: exports_external.boolean().optional()
|
|
30551
30566
|
}).passthrough();
|
|
@@ -30560,6 +30575,11 @@ var waitForCompletionOutput = exports_external.object({
|
|
|
30560
30575
|
stale: exports_external.boolean().optional(),
|
|
30561
30576
|
active_segment_ids: exports_external.array(exports_external.union([exports_external.number(), exports_external.string()])).optional(),
|
|
30562
30577
|
stale_segment_ids: exports_external.array(exports_external.union([exports_external.number(), exports_external.string()])).optional(),
|
|
30578
|
+
failed_stage: exports_external.string().optional().describe("Short only: exact API failure phase behind a terminal failure."),
|
|
30579
|
+
failure_code: exports_external.string().optional().describe("Short only: stable machine-readable failure classification."),
|
|
30580
|
+
failure_details: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("Short only: structured provider/QC details for recovery."),
|
|
30581
|
+
failure_source: exports_external.string().optional().describe("Short only: worker/recovery layer that finalized the failure."),
|
|
30582
|
+
failure_attempt: exports_external.number().optional().describe("Short only: paid generation attempt number that failed."),
|
|
30563
30583
|
latest_render_free: exports_external.boolean().optional(),
|
|
30564
30584
|
last_free_rerender_failed: exports_external.boolean().optional(),
|
|
30565
30585
|
saved_to: exports_external.union([exports_external.string(), exports_external.null()]).optional(),
|
|
@@ -31103,6 +31123,7 @@ var MAX_VIDEO_BYTES = 500000000;
|
|
|
31103
31123
|
var MAX_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
31104
31124
|
var MAX_PRODUCT_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
31105
31125
|
var MAX_LOGO_BYTES = 1 * 1024 * 1024;
|
|
31126
|
+
var MAX_SHORT_LOGO_BYTES = 5 * 1024 * 1024;
|
|
31106
31127
|
var MAX_CLOSING_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
31107
31128
|
var MULTIPART_THRESHOLD = 50 * 1024 * 1024;
|
|
31108
31129
|
var MAX_CATALOG_VIDEO_DURATION_SECONDS = 60;
|
|
@@ -31795,7 +31816,7 @@ DELEGATE THE CONTENT OPERATION (the full loop — steps 1-3 are $0)
|
|
|
31795
31816
|
8. RECURRING: create_series + plan_series_episodes → materialize_episode drafts each episode; get_series_dashboard shows upcoming/drafted/published lanes; mark_episode_posted closes a recurring show's loop.
|
|
31796
31817
|
|
|
31797
31818
|
CREDITS (spent server-side; each tool prices before charging — see its description)
|
|
31798
|
-
- Free (0 credits): every draft create/edit, reorder, all slider re-composites, short re-renders (rerender_short — applies text/style/CTA/end-card edits over a short's already-paid footage), and the whole onboard/plan/pack/materialize path (steps 1-3). get_credits shows the balance.
|
|
31819
|
+
- Free (0 credits): every draft create/edit, reorder, all slider re-composites, short re-renders (rerender_short — applies text/style/CTA/end-card/logo edits over a short's already-paid footage), and the whole onboard/plan/pack/materialize path (steps 1-3). get_credits shows the balance.
|
|
31799
31820
|
- Charged: short render 15; editor AI scene 5 (4 in a batch of ≥3); voice 3; music 5; editor render 0 (auto-charges ungenerated scenes); slider 1/slide; tracking render 1 (refunded on failure). make_video supports dry_run/max_credits; create_editor_ad/start_autopilot require max_credits before launching.
|
|
31800
31821
|
- AI ASSISTS (all generate_*/enhance/suggest/plan/hook tools) draw a FREE daily quota (20/day), NOT credits. On 429, set auto_unlock:true to spend 1 credit for +10 assists (retried once), or write the copy yourself and use the set_* tools.
|
|
31801
31822
|
|
|
@@ -32088,7 +32109,7 @@ registerTool("list_projects", {
|
|
|
32088
32109
|
}));
|
|
32089
32110
|
registerTool("create_short", {
|
|
32090
32111
|
title: "Create a short (draft)",
|
|
32091
|
-
description: "Creates a short draft from a product prompt (min 10 chars). A short is a 12s vertical (two 6s AI " + "segments + an on-screen title overlay + music; 14s when it ends on a poster or brand-lockup end card). " + "Returns the slug, costs 0 credits. Follow with generate_short to render. Iteration is FREE after that " + "first generate: the 15 credits bought the footage + music, and every later text/style/CTA/end-card " + "tweak re-renders over them for 0 credits (update_short → rerender_short). " + "BE PROACTIVE: don't ship a bare clip — set a headline (the on-screen TITLE) and subheadline (secondary " + "title), and pick a music_vibe plus visual_language that fit the brand. " + "Always set cta_text — a short without a CTA doesn't convert (plus offer_text only if there's a real " + "deal). Note: blank cta_text → the server renders a neutral localized ask (e.g. 'Learn more'); a truly " + "CTA-less short is not currently supported. " + "Keep headline ≈4 words (≤40 chars). Set visual_language explicitly (kinetic_creator is a strong " + "default). If headline/subheadline/text_beats are left blank, the server auto-writes the headline + " + "caption beats at generate time (pass skip_auto_text:true to generate_short for a deliberately bare clip). " + "To brand it further, attach a product image " + "(set_short_product)
|
|
32112
|
+
description: "Creates a short draft from a product prompt (min 10 chars). A short is a 12s vertical (two 6s AI " + "segments + an on-screen title overlay + music; 14s when it ends on a poster or brand-lockup end card). " + "Returns the slug, costs 0 credits. Follow with generate_short to render. Iteration is FREE after that " + "first generate: the 15 credits bought the footage + music, and every later text/style/CTA/end-card/logo " + "tweak re-renders over them for 0 credits (update_short → rerender_short). " + "BE PROACTIVE: don't ship a bare clip — set a headline (the on-screen TITLE) and subheadline (secondary " + "title), and pick a music_vibe plus visual_language that fit the brand. " + "Always set cta_text — a short without a CTA doesn't convert (plus offer_text only if there's a real " + "deal). Note: blank cta_text → the server renders a neutral localized ask (e.g. 'Learn more'); a truly " + "CTA-less short is not currently supported. " + "Keep headline ≈4 words (≤40 chars). Set visual_language explicitly (kinetic_creator is a strong " + "default). If headline/subheadline/text_beats are left blank, the server auto-writes the headline + " + "caption beats at generate time (pass skip_auto_text:true to generate_short for a deliberately bare clip). " + "To brand it further, attach a product image " + "(set_short_product), a deterministic brand logo (set_short_logo), or an end-card poster " + "(set_short_poster) before generate_short.",
|
|
32092
32113
|
inputSchema: {
|
|
32093
32114
|
product_prompt: exports_external.string().min(10).describe("What the short should be about"),
|
|
32094
32115
|
language: exports_external.enum(LANGUAGES).optional().describe('Language code, e.g. "en" (default). Bare ISO-639-1 code only — no region/script subtag like "en-US".'),
|
|
@@ -32185,7 +32206,7 @@ registerTool("create_short", {
|
|
|
32185
32206
|
}));
|
|
32186
32207
|
registerTool("update_short", {
|
|
32187
32208
|
title: "Update a short draft (0 credits)",
|
|
32188
|
-
description: "Patches an existing short draft — only the fields you pass change; omitted fields are left untouched. " + "0 credits, no re-render by itself. APPLYING the edit: changed only text/style/CTA/end-card fields " + "(headline, subheadline, text_beats, cta_text, offer_text, badge_text, star_rating, colors, font, " + "position, animation, closing_claim, brand_name, poster_includes_lockup, end_card) on an " + "already-generated short? " + "rerender_short applies them for 0 credits. Footage/music-affecting edits (product_prompt, product " + "image, creative_format, visual_language, theme, music_vibe, music_instruments, language) need " + "generate_short (15 credits) again. Use this to edit the brief, copy, " + "styling, or the optional conversion graphics after create_short. " + "Always set cta_text — a short without a CTA doesn't convert (plus offer_text only if there's a real " + "deal). Note: blank cta_text → the server renders a neutral localized ask (e.g. 'Learn more'); a truly " + "CTA-less short is not currently supported. " + "Keep headline ≈4 words (≤40 chars). Set visual_language explicitly (kinetic_creator is a strong " + "default). " + "CLEARING a field: the conversion graphics (offer_text, cta_text, badge_text, star_rating) and " + 'text_beats are OPT-IN and OFF by default — pass "" (or 0 for star_rating, [] for text_beats) to REMOVE ' + "one that was set. The four conversion graphics are user-supplied only and never written by the AI copy " + "assist; text_beats (like headline/subheadline) IS rewritten by generate_short_text, so hand-crafted " + "beats don't survive a later assist call. " + "404 for an unknown/foreign slug.",
|
|
32209
|
+
description: "Patches an existing short draft — only the fields you pass change; omitted fields are left untouched. " + "0 credits, no re-render by itself. APPLYING the edit: changed only text/style/CTA/end-card/logo fields " + "(headline, subheadline, text_beats, cta_text, offer_text, badge_text, star_rating, colors, font, " + "position, animation, closing_claim, brand_name, poster_includes_lockup, end_card, short_logo_position, " + "short_logo_treatment) on an " + "already-generated short? " + "rerender_short applies them for 0 credits. Footage/music-affecting edits (product_prompt, product " + "image, creative_format, visual_language, theme, music_vibe, music_instruments, language) need " + "generate_short (15 credits) again. Use this to edit the brief, copy, " + "styling, or the optional conversion graphics after create_short. " + "Always set cta_text — a short without a CTA doesn't convert (plus offer_text only if there's a real " + "deal). Note: blank cta_text → the server renders a neutral localized ask (e.g. 'Learn more'); a truly " + "CTA-less short is not currently supported. " + "Keep headline ≈4 words (≤40 chars). Set visual_language explicitly (kinetic_creator is a strong " + "default). " + "CLEARING a field: the conversion graphics (offer_text, cta_text, badge_text, star_rating) and " + 'text_beats are OPT-IN and OFF by default — pass "" (or 0 for star_rating, [] for text_beats) to REMOVE ' + "one that was set. The four conversion graphics are user-supplied only and never written by the AI copy " + "assist; text_beats (like headline/subheadline) IS rewritten by generate_short_text, so hand-crafted " + "beats don't survive a later assist call. " + "404 for an unknown/foreign slug.",
|
|
32189
32210
|
inputSchema: {
|
|
32190
32211
|
slug: exports_external.string().describe("Short slug (from create_short)"),
|
|
32191
32212
|
product_prompt: exports_external.string().min(10).optional().describe("Rewrite the brief (what the short is about, ≥10 chars); drives the next generate_short."),
|
|
@@ -32221,6 +32242,8 @@ registerTool("update_short", {
|
|
|
32221
32242
|
brand_name: exports_external.string().max(40).optional().describe("Brand/product display name (≤40 chars) shown big on the closing brand-lockup slate when no " + `poster is set. Server falls back to your brand profile's name when unset. Pass "" to remove it.`),
|
|
32222
32243
|
end_card: exports_external.enum(["auto", "none"]).optional().describe('"auto" (default): end on the poster, or on a brand-lockup slate (brand_name + claim + CTA pill) ' + 'when no poster is set — a 14s render. "none": no end card at all (12s, for deliberate ' + "loop-style shorts)."),
|
|
32223
32244
|
poster_includes_lockup: exports_external.boolean().optional().describe("Set true if your poster already contains brand name/CTA/badges — suppresses the overlaid " + "claim/CTA so they don't collide (default false)."),
|
|
32245
|
+
short_logo_position: exports_external.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).optional().describe("Corner for the deterministic brand-logo overlay (set the image with set_short_logo). Moving it is " + "a FREE re-render — logo fields never cost credits."),
|
|
32246
|
+
short_logo_treatment: exports_external.enum(["throughout", "end_card", "none"]).optional().describe('When the brand logo shows: "throughout" the footage, only on the "end_card", or "none" to hide it. ' + "FREE re-render."),
|
|
32224
32247
|
brand_profile_id: exports_external.number().int().optional().describe("Seed brand defaults — colors/font/CTA — from a saved brand profile (id from " + "list_brand_profiles). Best set at create_short time (that path also freezes a brand snapshot); " + "on update it sets the linked profile. Must be one of your own profiles.")
|
|
32225
32248
|
},
|
|
32226
32249
|
annotations: {
|
|
@@ -32278,13 +32301,17 @@ registerTool("update_short", {
|
|
|
32278
32301
|
body.end_card = args.end_card;
|
|
32279
32302
|
if (args.poster_includes_lockup !== undefined)
|
|
32280
32303
|
body.poster_includes_lockup = args.poster_includes_lockup;
|
|
32304
|
+
if (args.short_logo_position !== undefined)
|
|
32305
|
+
body.short_logo_position = args.short_logo_position;
|
|
32306
|
+
if (args.short_logo_treatment !== undefined)
|
|
32307
|
+
body.short_logo_treatment = args.short_logo_treatment;
|
|
32281
32308
|
if (args.brand_profile_id !== undefined)
|
|
32282
32309
|
body.brand_profile_id = args.brand_profile_id;
|
|
32283
32310
|
const res = await client.patch(`/shorts/${args.slug}`, body);
|
|
32284
32311
|
const data = asRecord(res).data ?? res;
|
|
32285
32312
|
return ok({
|
|
32286
32313
|
...asRecord(data),
|
|
32287
|
-
next: "Saved. Only text/style/CTA/end-card changed on an already-generated short? " + `rerender_short({ slug: "${args.slug}" }) applies it for 0 credits. Footage/music edits ` + "(prompt, product image, format, visual_language, theme, music_*, language) need " + "generate_short (15 credits)."
|
|
32314
|
+
next: "Saved. Only text/style/CTA/end-card/logo changed on an already-generated short? " + `rerender_short({ slug: "${args.slug}" }) applies it for 0 credits. Footage/music edits ` + "(prompt, product image, format, visual_language, theme, music_*, language) need " + "generate_short (15 credits)."
|
|
32288
32315
|
});
|
|
32289
32316
|
}));
|
|
32290
32317
|
async function getTracking(client, slug, deadline) {
|
|
@@ -32453,7 +32480,7 @@ registerTool("generate_short", {
|
|
|
32453
32480
|
}));
|
|
32454
32481
|
registerTool("rerender_short", {
|
|
32455
32482
|
title: "Re-render a short (FREE — 0 credits)",
|
|
32456
|
-
description: "FREE re-render: applies the short's current text/style/CTA/end-card state over the already-paid " + "footage and music — 0 credits. Use after update_short for copy/style/CTA tweaks. Footage or music " + "changes need generate_short (15 credits) instead. Edits that add/remove the end card (poster, " + "end_card) can shift the video between 12s and 14s; when the pinned music bed is shorter than the " + "export it crossfades into a repeated continuation, then fades at the export end, without regeneration. " + "Requires a completed generation to re-render over " + "(422 short_not_ready otherwise — run generate_short once first); while any generation/render is in " + "flight it reports in-progress (409) rather than duplicating. Then poll with get_status or " + "wait_for_completion (kind=short). A failed free re-render leaves the delivered video intact — retry " + "rerender_short, it stays free (the status fields latest_render_free / last_free_rerender_failed " + "report this when the server sends them).",
|
|
32483
|
+
description: "FREE re-render: applies the short's current text/style/CTA/end-card/logo state over the already-paid " + "footage and music — 0 credits. Use after update_short for copy/style/CTA tweaks, or after a logo change " + "(set_short_logo, or short_logo_position/short_logo_treatment via update_short). Footage or music " + "changes need generate_short (15 credits) instead. Edits that add/remove the end card (poster, " + "end_card) can shift the video between 12s and 14s; when the pinned music bed is shorter than the " + "export it crossfades into a repeated continuation, then fades at the export end, without regeneration. " + "Requires a completed generation to re-render over " + "(422 short_not_ready otherwise — run generate_short once first); while any generation/render is in " + "flight it reports in-progress (409) rather than duplicating. Then poll with get_status or " + "wait_for_completion (kind=short). A failed free re-render leaves the delivered video intact — retry " + "rerender_short, it stays free (the status fields latest_render_free / last_free_rerender_failed " + "report this when the server sends them).",
|
|
32457
32484
|
inputSchema: {
|
|
32458
32485
|
slug: exports_external.string().describe("Short slug (from create_short)")
|
|
32459
32486
|
},
|
|
@@ -33633,9 +33660,28 @@ registerTool("set_short_product", {
|
|
|
33633
33660
|
const res = await client.post(`/shorts/${args.slug}/product/confirm`, { s3_key, product_description: args.description });
|
|
33634
33661
|
return ok(asRecord(res).data ?? res);
|
|
33635
33662
|
}));
|
|
33663
|
+
registerTool("set_short_logo", {
|
|
33664
|
+
title: "Set a Short brand logo overlay (0 credits)",
|
|
33665
|
+
description: "Uploads an exact brand logo for deterministic compositing over a Short. The image is never sent to the " + "AI video provider, so lettering is preserved. PNG/JPEG, ≤5 MiB, 0 credits. Choose a corner and whether " + "it appears throughout the footage or only on the end card.",
|
|
33666
|
+
inputSchema: {
|
|
33667
|
+
slug: exports_external.string().describe("Short slug"),
|
|
33668
|
+
file_path: exports_external.string().describe("Local logo image (.png/.jpg/.jpeg)"),
|
|
33669
|
+
position: exports_external.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).optional().describe("Logo corner (default top-right)"),
|
|
33670
|
+
treatment: exports_external.enum(["throughout", "end_card", "none"]).optional().describe("When the logo appears (default throughout)")
|
|
33671
|
+
},
|
|
33672
|
+
annotations: { title: "Set Short logo", ...WRITE, idempotentHint: false }
|
|
33673
|
+
}, tool(async (args, client) => {
|
|
33674
|
+
const { s3_key } = await uploadImageFile(client, `/shorts/${args.slug}/logo/presign`, args.file_path, MAX_SHORT_LOGO_BYTES);
|
|
33675
|
+
const res = await client.post(`/shorts/${args.slug}/logo/confirm`, {
|
|
33676
|
+
s3_key,
|
|
33677
|
+
position: args.position ?? "top-right",
|
|
33678
|
+
treatment: args.treatment ?? "throughout"
|
|
33679
|
+
});
|
|
33680
|
+
return ok(asRecord(res).data ?? res);
|
|
33681
|
+
}));
|
|
33636
33682
|
registerTool("set_short_poster", {
|
|
33637
33683
|
title: "Set a short's end-card poster image (0 credits)",
|
|
33638
|
-
description: "Uploads a local image as the SHORT's end-card poster — a closing still shown at the end (extends the " + "render from 12s to 14s). Accepted: " + IMAGE_EXTS.map((e) => `.${e}`).join(", ") + " (≤20 MiB = 20,971,520 bytes), confined to HUBFLUENCER_INPUT_DIR/cwd. 0 credits. There is one poster per short; calling " + "again replaces it.
|
|
33684
|
+
description: "Uploads a local image as the SHORT's end-card poster — a closing still shown at the end (extends the " + "render from 12s to 14s). Accepted: " + IMAGE_EXTS.map((e) => `.${e}`).join(", ") + " (≤20 MiB = 20,971,520 bytes), confined to HUBFLUENCER_INPUT_DIR/cwd. 0 credits. There is one poster per short; calling " + "again replaces it. Use set_short_logo for an exact, non-generative brand overlay.",
|
|
33639
33685
|
inputSchema: {
|
|
33640
33686
|
slug: exports_external.string().describe("Short slug (from create_short)"),
|
|
33641
33687
|
file_path: exports_external.string().describe("Local poster image (.jpg/.jpeg/.png)"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubfluencer/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Model Context Protocol server for Hubfluencer — let AI agents generate post-ready shorts and editor ads.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Monocursive <contact@monocursive.com>",
|
package/src/core.ts
CHANGED
|
@@ -265,6 +265,16 @@ export interface NormalizedStatus {
|
|
|
265
265
|
ready: boolean;
|
|
266
266
|
video_url: string | null;
|
|
267
267
|
error: string | null;
|
|
268
|
+
/** Short only: exact API failure phase instead of a collapsed terminal state. */
|
|
269
|
+
failed_stage?: string;
|
|
270
|
+
/** Short only: stable machine-readable terminal failure classification. */
|
|
271
|
+
failure_code?: string;
|
|
272
|
+
/** Short only: structured provider/QC details for recovery decisions. */
|
|
273
|
+
failure_details?: Record<string, unknown>;
|
|
274
|
+
/** Short only: worker/recovery layer that finalized the failed attempt. */
|
|
275
|
+
failure_source?: string;
|
|
276
|
+
/** Short only: paid generation attempt number that failed. */
|
|
277
|
+
failure_attempt?: number;
|
|
268
278
|
/** Editor only: the newest delivered render no longer matches the live project. */
|
|
269
279
|
stale?: boolean;
|
|
270
280
|
/** Editor only: current scene ids that are still generating. */
|
|
@@ -341,6 +351,16 @@ export function normalizeStatus(
|
|
|
341
351
|
video_url: videoUrl,
|
|
342
352
|
error: (d.error_message as string) ?? null,
|
|
343
353
|
};
|
|
354
|
+
if (typeof d.failed_stage === "string")
|
|
355
|
+
status.failed_stage = d.failed_stage;
|
|
356
|
+
if (typeof d.failure_code === "string")
|
|
357
|
+
status.failure_code = d.failure_code;
|
|
358
|
+
if (d.failure_details && typeof d.failure_details === "object")
|
|
359
|
+
status.failure_details = d.failure_details as Record<string, unknown>;
|
|
360
|
+
if (typeof d.failure_source === "string")
|
|
361
|
+
status.failure_source = d.failure_source;
|
|
362
|
+
if (typeof d.failure_attempt === "number")
|
|
363
|
+
status.failure_attempt = d.failure_attempt;
|
|
344
364
|
// Additive free-re-render bookkeeping (2026-07 API): surfaced only when
|
|
345
365
|
// the server actually sends the booleans, so older payloads normalize
|
|
346
366
|
// byte-identically. A failed free re-render does NOT flip stage to
|
package/src/index.ts
CHANGED
|
@@ -121,6 +121,7 @@ import {
|
|
|
121
121
|
MAX_CLOSING_IMAGE_BYTES,
|
|
122
122
|
MAX_LOGO_BYTES,
|
|
123
123
|
MAX_PRODUCT_IMAGE_BYTES,
|
|
124
|
+
MAX_SHORT_LOGO_BYTES,
|
|
124
125
|
type PreparedUploadFile,
|
|
125
126
|
preparedUploadIdentity,
|
|
126
127
|
prepareImageUpload,
|
|
@@ -614,7 +615,7 @@ DELEGATE THE CONTENT OPERATION (the full loop — steps 1-3 are $0)
|
|
|
614
615
|
8. RECURRING: create_series + plan_series_episodes → materialize_episode drafts each episode; get_series_dashboard shows upcoming/drafted/published lanes; mark_episode_posted closes a recurring show's loop.
|
|
615
616
|
|
|
616
617
|
CREDITS (spent server-side; each tool prices before charging — see its description)
|
|
617
|
-
- Free (0 credits): every draft create/edit, reorder, all slider re-composites, short re-renders (rerender_short — applies text/style/CTA/end-card edits over a short's already-paid footage), and the whole onboard/plan/pack/materialize path (steps 1-3). get_credits shows the balance.
|
|
618
|
+
- Free (0 credits): every draft create/edit, reorder, all slider re-composites, short re-renders (rerender_short — applies text/style/CTA/end-card/logo edits over a short's already-paid footage), and the whole onboard/plan/pack/materialize path (steps 1-3). get_credits shows the balance.
|
|
618
619
|
- Charged: short render 15; editor AI scene 5 (4 in a batch of ≥3); voice 3; music 5; editor render 0 (auto-charges ungenerated scenes); slider 1/slide; tracking render 1 (refunded on failure). make_video supports dry_run/max_credits; create_editor_ad/start_autopilot require max_credits before launching.
|
|
619
620
|
- AI ASSISTS (all generate_*/enhance/suggest/plan/hook tools) draw a FREE daily quota (20/day), NOT credits. On 429, set auto_unlock:true to spend 1 credit for +10 assists (retried once), or write the copy yourself and use the set_* tools.
|
|
620
621
|
|
|
@@ -1436,7 +1437,7 @@ registerTool(
|
|
|
1436
1437
|
"Creates a short draft from a product prompt (min 10 chars). A short is a 12s vertical (two 6s AI " +
|
|
1437
1438
|
"segments + an on-screen title overlay + music; 14s when it ends on a poster or brand-lockup end card). " +
|
|
1438
1439
|
"Returns the slug, costs 0 credits. Follow with generate_short to render. Iteration is FREE after that " +
|
|
1439
|
-
"first generate: the 15 credits bought the footage + music, and every later text/style/CTA/end-card " +
|
|
1440
|
+
"first generate: the 15 credits bought the footage + music, and every later text/style/CTA/end-card/logo " +
|
|
1440
1441
|
"tweak re-renders over them for 0 credits (update_short → rerender_short). " +
|
|
1441
1442
|
"BE PROACTIVE: don't ship a bare clip — set a headline (the on-screen TITLE) and subheadline (secondary " +
|
|
1442
1443
|
"title), and pick a music_vibe plus visual_language that fit the brand. " +
|
|
@@ -1447,8 +1448,8 @@ registerTool(
|
|
|
1447
1448
|
"default). If headline/subheadline/text_beats are left blank, the server auto-writes the headline + " +
|
|
1448
1449
|
"caption beats at generate time (pass skip_auto_text:true to generate_short for a deliberately bare clip). " +
|
|
1449
1450
|
"To brand it further, attach a product image " +
|
|
1450
|
-
"(set_short_product)
|
|
1451
|
-
"
|
|
1451
|
+
"(set_short_product), a deterministic brand logo (set_short_logo), or an end-card poster " +
|
|
1452
|
+
"(set_short_poster) before generate_short.",
|
|
1452
1453
|
inputSchema: {
|
|
1453
1454
|
product_prompt: z
|
|
1454
1455
|
.string()
|
|
@@ -1736,9 +1737,10 @@ registerTool(
|
|
|
1736
1737
|
title: "Update a short draft (0 credits)",
|
|
1737
1738
|
description:
|
|
1738
1739
|
"Patches an existing short draft — only the fields you pass change; omitted fields are left untouched. " +
|
|
1739
|
-
"0 credits, no re-render by itself. APPLYING the edit: changed only text/style/CTA/end-card fields " +
|
|
1740
|
+
"0 credits, no re-render by itself. APPLYING the edit: changed only text/style/CTA/end-card/logo fields " +
|
|
1740
1741
|
"(headline, subheadline, text_beats, cta_text, offer_text, badge_text, star_rating, colors, font, " +
|
|
1741
|
-
"position, animation, closing_claim, brand_name, poster_includes_lockup, end_card
|
|
1742
|
+
"position, animation, closing_claim, brand_name, poster_includes_lockup, end_card, short_logo_position, " +
|
|
1743
|
+
"short_logo_treatment) on an " +
|
|
1742
1744
|
"already-generated short? " +
|
|
1743
1745
|
"rerender_short applies them for 0 credits. Footage/music-affecting edits (product_prompt, product " +
|
|
1744
1746
|
"image, creative_format, visual_language, theme, music_vibe, music_instruments, language) need " +
|
|
@@ -1922,6 +1924,20 @@ registerTool(
|
|
|
1922
1924
|
"Set true if your poster already contains brand name/CTA/badges — suppresses the overlaid " +
|
|
1923
1925
|
"claim/CTA so they don't collide (default false).",
|
|
1924
1926
|
),
|
|
1927
|
+
short_logo_position: z
|
|
1928
|
+
.enum(["top-left", "top-right", "bottom-left", "bottom-right"])
|
|
1929
|
+
.optional()
|
|
1930
|
+
.describe(
|
|
1931
|
+
"Corner for the deterministic brand-logo overlay (set the image with set_short_logo). Moving it is " +
|
|
1932
|
+
"a FREE re-render — logo fields never cost credits.",
|
|
1933
|
+
),
|
|
1934
|
+
short_logo_treatment: z
|
|
1935
|
+
.enum(["throughout", "end_card", "none"])
|
|
1936
|
+
.optional()
|
|
1937
|
+
.describe(
|
|
1938
|
+
'When the brand logo shows: "throughout" the footage, only on the "end_card", or "none" to hide it. ' +
|
|
1939
|
+
"FREE re-render.",
|
|
1940
|
+
),
|
|
1925
1941
|
brand_profile_id: z
|
|
1926
1942
|
.number()
|
|
1927
1943
|
.int()
|
|
@@ -1967,6 +1983,12 @@ registerTool(
|
|
|
1967
1983
|
brand_name?: string;
|
|
1968
1984
|
end_card?: string;
|
|
1969
1985
|
poster_includes_lockup?: boolean;
|
|
1986
|
+
short_logo_position?:
|
|
1987
|
+
| "top-left"
|
|
1988
|
+
| "top-right"
|
|
1989
|
+
| "bottom-left"
|
|
1990
|
+
| "bottom-right";
|
|
1991
|
+
short_logo_treatment?: "throughout" | "end_card" | "none";
|
|
1970
1992
|
brand_profile_id?: number;
|
|
1971
1993
|
},
|
|
1972
1994
|
client,
|
|
@@ -2013,6 +2035,13 @@ registerTool(
|
|
|
2013
2035
|
if (args.end_card !== undefined) body.end_card = args.end_card;
|
|
2014
2036
|
if (args.poster_includes_lockup !== undefined)
|
|
2015
2037
|
body.poster_includes_lockup = args.poster_includes_lockup;
|
|
2038
|
+
// Logo overlay placement — the API maps these body keys straight onto the
|
|
2039
|
+
// factory (short_logo_position/short_logo_treatment). They're excluded from
|
|
2040
|
+
// the paid-inputs snapshot, so a corner/treatment change is a FREE re-render.
|
|
2041
|
+
if (args.short_logo_position !== undefined)
|
|
2042
|
+
body.short_logo_position = args.short_logo_position;
|
|
2043
|
+
if (args.short_logo_treatment !== undefined)
|
|
2044
|
+
body.short_logo_treatment = args.short_logo_treatment;
|
|
2016
2045
|
if (args.brand_profile_id !== undefined)
|
|
2017
2046
|
body.brand_profile_id = args.brand_profile_id;
|
|
2018
2047
|
|
|
@@ -2024,7 +2053,7 @@ registerTool(
|
|
|
2024
2053
|
return ok({
|
|
2025
2054
|
...asRecord(data),
|
|
2026
2055
|
next:
|
|
2027
|
-
"Saved. Only text/style/CTA/end-card changed on an already-generated short? " +
|
|
2056
|
+
"Saved. Only text/style/CTA/end-card/logo changed on an already-generated short? " +
|
|
2028
2057
|
`rerender_short({ slug: "${args.slug}" }) applies it for 0 credits. Footage/music edits ` +
|
|
2029
2058
|
"(prompt, product image, format, visual_language, theme, music_*, language) need " +
|
|
2030
2059
|
"generate_short (15 credits).",
|
|
@@ -2464,8 +2493,9 @@ registerTool(
|
|
|
2464
2493
|
{
|
|
2465
2494
|
title: "Re-render a short (FREE — 0 credits)",
|
|
2466
2495
|
description:
|
|
2467
|
-
"FREE re-render: applies the short's current text/style/CTA/end-card state over the already-paid " +
|
|
2468
|
-
"footage and music — 0 credits. Use after update_short for copy/style/CTA tweaks
|
|
2496
|
+
"FREE re-render: applies the short's current text/style/CTA/end-card/logo state over the already-paid " +
|
|
2497
|
+
"footage and music — 0 credits. Use after update_short for copy/style/CTA tweaks, or after a logo change " +
|
|
2498
|
+
"(set_short_logo, or short_logo_position/short_logo_treatment via update_short). Footage or music " +
|
|
2469
2499
|
"changes need generate_short (15 credits) instead. Edits that add/remove the end card (poster, " +
|
|
2470
2500
|
"end_card) can shift the video between 12s and 14s; when the pinned music bed is shorter than the " +
|
|
2471
2501
|
"export it crossfades into a repeated continuation, then fades at the export end, without regeneration. " +
|
|
@@ -5043,11 +5073,11 @@ registerTool(
|
|
|
5043
5073
|
),
|
|
5044
5074
|
);
|
|
5045
5075
|
|
|
5046
|
-
// ── Shorts: branding images (product + end-card poster)
|
|
5076
|
+
// ── Shorts: branding images (product + logo + end-card poster) ───────────────
|
|
5047
5077
|
//
|
|
5048
5078
|
// The editor branding tools above hit /editor/...; these hit /shorts/... A short
|
|
5049
|
-
//
|
|
5050
|
-
//
|
|
5079
|
+
// Product images are woven into AI footage; logos are deterministic Forge
|
|
5080
|
+
// overlays; posters are closing cards. All are 0 credits.
|
|
5051
5081
|
|
|
5052
5082
|
registerTool(
|
|
5053
5083
|
"set_short_product",
|
|
@@ -5096,6 +5126,57 @@ registerTool(
|
|
|
5096
5126
|
),
|
|
5097
5127
|
);
|
|
5098
5128
|
|
|
5129
|
+
registerTool(
|
|
5130
|
+
"set_short_logo",
|
|
5131
|
+
{
|
|
5132
|
+
title: "Set a Short brand logo overlay (0 credits)",
|
|
5133
|
+
description:
|
|
5134
|
+
"Uploads an exact brand logo for deterministic compositing over a Short. The image is never sent to the " +
|
|
5135
|
+
"AI video provider, so lettering is preserved. PNG/JPEG, ≤5 MiB, 0 credits. Choose a corner and whether " +
|
|
5136
|
+
"it appears throughout the footage or only on the end card.",
|
|
5137
|
+
inputSchema: {
|
|
5138
|
+
slug: z.string().describe("Short slug"),
|
|
5139
|
+
file_path: z.string().describe("Local logo image (.png/.jpg/.jpeg)"),
|
|
5140
|
+
position: z
|
|
5141
|
+
.enum(["top-left", "top-right", "bottom-left", "bottom-right"])
|
|
5142
|
+
.optional()
|
|
5143
|
+
.describe("Logo corner (default top-right)"),
|
|
5144
|
+
treatment: z
|
|
5145
|
+
.enum(["throughout", "end_card", "none"])
|
|
5146
|
+
.optional()
|
|
5147
|
+
.describe("When the logo appears (default throughout)"),
|
|
5148
|
+
},
|
|
5149
|
+
annotations: { title: "Set Short logo", ...WRITE, idempotentHint: false },
|
|
5150
|
+
},
|
|
5151
|
+
tool(
|
|
5152
|
+
async (
|
|
5153
|
+
args: {
|
|
5154
|
+
slug: string;
|
|
5155
|
+
file_path: string;
|
|
5156
|
+
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
5157
|
+
treatment?: "throughout" | "end_card" | "none";
|
|
5158
|
+
},
|
|
5159
|
+
client,
|
|
5160
|
+
) => {
|
|
5161
|
+
const { s3_key } = await uploadImageFile(
|
|
5162
|
+
client,
|
|
5163
|
+
`/shorts/${args.slug}/logo/presign`,
|
|
5164
|
+
args.file_path,
|
|
5165
|
+
MAX_SHORT_LOGO_BYTES,
|
|
5166
|
+
);
|
|
5167
|
+
const res = await client.post<{ data: unknown }>(
|
|
5168
|
+
`/shorts/${args.slug}/logo/confirm`,
|
|
5169
|
+
{
|
|
5170
|
+
s3_key,
|
|
5171
|
+
position: args.position ?? "top-right",
|
|
5172
|
+
treatment: args.treatment ?? "throughout",
|
|
5173
|
+
},
|
|
5174
|
+
);
|
|
5175
|
+
return ok(asRecord(res).data ?? res);
|
|
5176
|
+
},
|
|
5177
|
+
),
|
|
5178
|
+
);
|
|
5179
|
+
|
|
5099
5180
|
registerTool(
|
|
5100
5181
|
"set_short_poster",
|
|
5101
5182
|
{
|
|
@@ -5105,7 +5186,7 @@ registerTool(
|
|
|
5105
5186
|
"render from 12s to 14s). Accepted: " +
|
|
5106
5187
|
IMAGE_EXTS.map((e) => `.${e}`).join(", ") +
|
|
5107
5188
|
" (≤20 MiB = 20,971,520 bytes), confined to HUBFLUENCER_INPUT_DIR/cwd. 0 credits. There is one poster per short; calling " +
|
|
5108
|
-
"again replaces it.
|
|
5189
|
+
"again replaces it. Use set_short_logo for an exact, non-generative brand overlay.",
|
|
5109
5190
|
inputSchema: {
|
|
5110
5191
|
slug: z.string().describe("Short slug (from create_short)"),
|
|
5111
5192
|
file_path: z.string().describe("Local poster image (.jpg/.jpeg/.png)"),
|
package/src/output-schemas.ts
CHANGED
|
@@ -65,8 +65,32 @@ export const getStatusOutput = z
|
|
|
65
65
|
stale: z.boolean().optional(),
|
|
66
66
|
active_segment_ids: z.array(z.union([z.number(), z.string()])).optional(),
|
|
67
67
|
stale_segment_ids: z.array(z.union([z.number(), z.string()])).optional(),
|
|
68
|
-
// Short only, additive (normalizeStatus forwards
|
|
69
|
-
//
|
|
68
|
+
// Short only, additive (normalizeStatus forwards each gated on its typeof,
|
|
69
|
+
// so the precise type can never reject a payload).
|
|
70
|
+
failed_stage: z
|
|
71
|
+
.string()
|
|
72
|
+
.optional()
|
|
73
|
+
.describe(
|
|
74
|
+
"Short only: exact API failure phase behind a terminal failure.",
|
|
75
|
+
),
|
|
76
|
+
failure_code: z
|
|
77
|
+
.string()
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("Short only: stable machine-readable failure classification."),
|
|
80
|
+
failure_details: z
|
|
81
|
+
.record(z.string(), z.unknown())
|
|
82
|
+
.optional()
|
|
83
|
+
.describe("Short only: structured provider/QC details for recovery."),
|
|
84
|
+
failure_source: z
|
|
85
|
+
.string()
|
|
86
|
+
.optional()
|
|
87
|
+
.describe(
|
|
88
|
+
"Short only: worker/recovery layer that finalized the failure.",
|
|
89
|
+
),
|
|
90
|
+
failure_attempt: z
|
|
91
|
+
.number()
|
|
92
|
+
.optional()
|
|
93
|
+
.describe("Short only: paid generation attempt number that failed."),
|
|
70
94
|
latest_render_free: z.boolean().optional(),
|
|
71
95
|
last_free_rerender_failed: z.boolean().optional(),
|
|
72
96
|
})
|
|
@@ -85,6 +109,31 @@ export const waitForCompletionOutput = z
|
|
|
85
109
|
stale: z.boolean().optional(),
|
|
86
110
|
active_segment_ids: z.array(z.union([z.number(), z.string()])).optional(),
|
|
87
111
|
stale_segment_ids: z.array(z.union([z.number(), z.string()])).optional(),
|
|
112
|
+
// Short only, additive — mirrors getStatusOutput (see there).
|
|
113
|
+
failed_stage: z
|
|
114
|
+
.string()
|
|
115
|
+
.optional()
|
|
116
|
+
.describe(
|
|
117
|
+
"Short only: exact API failure phase behind a terminal failure.",
|
|
118
|
+
),
|
|
119
|
+
failure_code: z
|
|
120
|
+
.string()
|
|
121
|
+
.optional()
|
|
122
|
+
.describe("Short only: stable machine-readable failure classification."),
|
|
123
|
+
failure_details: z
|
|
124
|
+
.record(z.string(), z.unknown())
|
|
125
|
+
.optional()
|
|
126
|
+
.describe("Short only: structured provider/QC details for recovery."),
|
|
127
|
+
failure_source: z
|
|
128
|
+
.string()
|
|
129
|
+
.optional()
|
|
130
|
+
.describe(
|
|
131
|
+
"Short only: worker/recovery layer that finalized the failure.",
|
|
132
|
+
),
|
|
133
|
+
failure_attempt: z
|
|
134
|
+
.number()
|
|
135
|
+
.optional()
|
|
136
|
+
.describe("Short only: paid generation attempt number that failed."),
|
|
88
137
|
latest_render_free: z.boolean().optional(),
|
|
89
138
|
last_free_rerender_failed: z.boolean().optional(),
|
|
90
139
|
saved_to: z.union([z.string(), z.null()]).optional(),
|
package/src/uploads.ts
CHANGED
|
@@ -64,6 +64,7 @@ const MAX_VIDEO_BYTES = 500_000_000; // EditorUpload.@max_upload_bytes
|
|
|
64
64
|
const MAX_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
65
65
|
export const MAX_PRODUCT_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
66
66
|
export const MAX_LOGO_BYTES = 1 * 1024 * 1024; // Editor.Logo.@max_logo_bytes
|
|
67
|
+
export const MAX_SHORT_LOGO_BYTES = 5 * 1024 * 1024;
|
|
67
68
|
export const MAX_CLOSING_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
68
69
|
const MULTIPART_THRESHOLD = 50 * 1024 * 1024; // @multipart_min_size — at/above this, use multipart
|
|
69
70
|
const MAX_CATALOG_VIDEO_DURATION_SECONDS = 60; // CatalogAsset video validator
|