@pixeldojo/mcp 0.20.0 → 0.22.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/dist/cli.js CHANGED
@@ -1248,6 +1248,169 @@ var init_upscale = __esm({
1248
1248
  }
1249
1249
  });
1250
1250
  async function run6(input, ctx) {
1251
+ const submitted = await ctx.client.run(MODEL, {
1252
+ prompt: "",
1253
+ video: input.video_url,
1254
+ ...input.style ? { caption_style: input.style } : {},
1255
+ ...input.highlight_color ? { highlight_color: input.highlight_color } : {}
1256
+ });
1257
+ const result = await pollJobUntilDoneOrHandoff(
1258
+ submitted.jobId,
1259
+ (id) => ctx.client.getJob(id),
1260
+ ctx.buildStatusUrl,
1261
+ { now: ctx.pollOverrides?.now, sleep: ctx.pollOverrides?.sleep }
1262
+ );
1263
+ return pollResultToSkillResult(
1264
+ result,
1265
+ (urls) => urls.length === 0 ? `Job ${submitted.jobId} completed but returned no assets.` : `Captioned. Asset: ${urls[0]}`
1266
+ );
1267
+ }
1268
+ var MODEL, STYLES, InputSchema6, InputJsonSchema6, captionSkill;
1269
+ var init_caption = __esm({
1270
+ "src/skills/caption.ts"() {
1271
+ init_poll();
1272
+ init_types();
1273
+ MODEL = "video-autocaption";
1274
+ STYLES = ["pop", "highlighter", "clean"];
1275
+ InputSchema6 = z.object({
1276
+ video_url: z.string().url().describe("URL of the video to caption. It must have speech."),
1277
+ style: z.enum(STYLES).optional(),
1278
+ highlight_color: z.string().optional()
1279
+ });
1280
+ InputJsonSchema6 = {
1281
+ type: "object",
1282
+ required: ["video_url"],
1283
+ properties: {
1284
+ video_url: {
1285
+ type: "string",
1286
+ format: "uri",
1287
+ description: "The video to caption (MP4 or MOV). It must have speech; a silent clip is refused uncharged."
1288
+ },
1289
+ style: {
1290
+ type: "string",
1291
+ enum: STYLES,
1292
+ description: "pop (default): bold caps, the spoken word lit and growing. highlighter: the spoken word on a block of the color. clean: sentence case on a dark box."
1293
+ },
1294
+ highlight_color: {
1295
+ type: "string",
1296
+ description: "The highlight as #RRGGBB (a brand color) or a name like yellow, green, pink. Defaults to yellow."
1297
+ }
1298
+ }
1299
+ };
1300
+ captionSkill = {
1301
+ name: "pixeldojo_caption",
1302
+ description: "Burn animated word-by-word captions onto a video with speech, the short-form look for TikTok, Reels and Shorts: the spoken word lights up, placed above the platform buttons. Pass video_url; optional style (pop, highlighter, clean) and highlight_color (#RRGGBB for a brand color). 5 credits.",
1303
+ inputZod: InputSchema6,
1304
+ inputJsonSchema: InputJsonSchema6,
1305
+ allowedModels: [MODEL],
1306
+ defaultModel: MODEL,
1307
+ run: run6
1308
+ };
1309
+ }
1310
+ });
1311
+ async function run7(input, ctx) {
1312
+ if (!input.offer && !input.end_card) {
1313
+ return { kind: "text", text: "pixeldojo_overlay needs an offer, an end_card, or both." };
1314
+ }
1315
+ const { offer, end_card: card } = input;
1316
+ const submitted = await ctx.client.run(MODEL2, {
1317
+ prompt: "",
1318
+ video: input.video_url,
1319
+ ...input.color ? { color: input.color } : {},
1320
+ ...offer ? {
1321
+ offer_text: offer.text,
1322
+ offer_subtext: offer.subtext,
1323
+ offer_start: offer.start_sec,
1324
+ offer_duration: offer.duration_sec,
1325
+ offer_position: offer.position
1326
+ } : {},
1327
+ ...card ? {
1328
+ end_headline: card.headline,
1329
+ end_cta: card.cta,
1330
+ end_url: card.url,
1331
+ logo_url: card.logo_url,
1332
+ end_duration: card.duration_sec
1333
+ } : {}
1334
+ });
1335
+ const result = await pollJobUntilDoneOrHandoff(
1336
+ submitted.jobId,
1337
+ (id) => ctx.client.getJob(id),
1338
+ ctx.buildStatusUrl,
1339
+ { now: ctx.pollOverrides?.now, sleep: ctx.pollOverrides?.sleep }
1340
+ );
1341
+ return pollResultToSkillResult(
1342
+ result,
1343
+ (urls) => urls.length === 0 ? `Job ${submitted.jobId} completed but returned no assets.` : `Branded. Asset: ${urls[0]}`
1344
+ );
1345
+ }
1346
+ var MODEL2, InputSchema7, InputJsonSchema7, overlaySkill;
1347
+ var init_overlay = __esm({
1348
+ "src/skills/overlay.ts"() {
1349
+ init_poll();
1350
+ init_types();
1351
+ MODEL2 = "brand-overlay";
1352
+ InputSchema7 = z.object({
1353
+ video_url: z.string().url(),
1354
+ color: z.string().optional(),
1355
+ offer: z.object({
1356
+ text: z.string().max(24),
1357
+ subtext: z.string().max(40).optional(),
1358
+ start_sec: z.number().min(0).optional(),
1359
+ duration_sec: z.number().min(1).max(10).optional(),
1360
+ position: z.enum(["top", "bottom"]).optional()
1361
+ }).optional(),
1362
+ end_card: z.object({
1363
+ headline: z.string().max(40),
1364
+ cta: z.string().max(24).optional(),
1365
+ url: z.string().max(40).optional(),
1366
+ logo_url: z.string().url().optional(),
1367
+ duration_sec: z.number().min(2).max(5).optional()
1368
+ }).optional()
1369
+ });
1370
+ InputJsonSchema7 = {
1371
+ type: "object",
1372
+ required: ["video_url"],
1373
+ properties: {
1374
+ video_url: { type: "string", format: "uri", description: "The video to brand (MP4 or MOV)." },
1375
+ color: { type: "string", description: "Brand color as #RRGGBB: the badge and the button. Defaults to #FFE500." },
1376
+ offer: {
1377
+ type: "object",
1378
+ description: "Offer callout: a badge that pops in on cue.",
1379
+ required: ["text"],
1380
+ properties: {
1381
+ text: { type: "string", maxLength: 24, description: 'e.g. "50% OFF".' },
1382
+ subtext: { type: "string", maxLength: 40, description: 'Small line under it, e.g. "This week only".' },
1383
+ start_sec: { type: "number", description: "Second it appears. Default 0.5." },
1384
+ duration_sec: { type: "number", minimum: 1, maximum: 10, description: "Seconds it stays. Default 3." },
1385
+ position: { type: "string", enum: ["top", "bottom"], description: "Default top." }
1386
+ }
1387
+ },
1388
+ end_card: {
1389
+ type: "object",
1390
+ description: "End card held after the last frame.",
1391
+ required: ["headline"],
1392
+ properties: {
1393
+ headline: { type: "string", maxLength: 40, description: 'e.g. "Your new favorite mug".' },
1394
+ cta: { type: "string", maxLength: 24, description: 'Button text, e.g. "Shop now".' },
1395
+ url: { type: "string", maxLength: 40, description: "Web address shown under the button." },
1396
+ logo_url: { type: "string", format: "uri", description: "PNG (transparent works best) or JPG." },
1397
+ duration_sec: { type: "number", minimum: 2, maximum: 5, description: "Default 3." }
1398
+ }
1399
+ }
1400
+ }
1401
+ };
1402
+ overlaySkill = {
1403
+ name: "pixeldojo_overlay",
1404
+ description: 'Add an offer callout (a badge like "50% OFF" that pops in on cue) and/or a branded end card (the last frame held under a logo, headline, button and web address) to a video. The text renders exactly as written, in the brand color. Pass video_url plus offer and/or end_card. 2 credits.',
1405
+ inputZod: InputSchema7,
1406
+ inputJsonSchema: InputJsonSchema7,
1407
+ allowedModels: [MODEL2],
1408
+ defaultModel: MODEL2,
1409
+ run: run7
1410
+ };
1411
+ }
1412
+ });
1413
+ async function run8(input, ctx) {
1251
1414
  const job = await ctx.client.getJob(input.jobId);
1252
1415
  if (job.status === "completed") {
1253
1416
  const urls = extractAssetUrls2(job);
@@ -1311,13 +1474,13 @@ function extractAssetUrls2(job) {
1311
1474
  if (output?.video) return [output.video];
1312
1475
  return [];
1313
1476
  }
1314
- var InputSchema6, InputJsonSchema6, STUCK_HINT_MS, ABANDON_HINT_MS, statusSkill;
1477
+ var InputSchema8, InputJsonSchema8, STUCK_HINT_MS, ABANDON_HINT_MS, statusSkill;
1315
1478
  var init_status = __esm({
1316
1479
  "src/skills/status.ts"() {
1317
- InputSchema6 = z.object({
1480
+ InputSchema8 = z.object({
1318
1481
  jobId: z.string().min(1).max(200).describe("Job id returned from a prior pixeldojo_* skill handoff.")
1319
1482
  });
1320
- InputJsonSchema6 = {
1483
+ InputJsonSchema8 = {
1321
1484
  type: "object",
1322
1485
  properties: {
1323
1486
  jobId: {
@@ -1332,16 +1495,16 @@ var init_status = __esm({
1332
1495
  statusSkill = {
1333
1496
  name: "pixeldojo_status",
1334
1497
  description: 'Check once, without waiting, on a jobId that a generation tool returned instead of an asset after its 30s wait. Free. Returns asset URLs when complete, the failure reason if it failed, or the current state and job age if still running. Campaign ids go to pixeldojo_campaign_status; films report progress through pixeldojo_film with action "status".',
1335
- inputZod: InputSchema6,
1336
- inputJsonSchema: InputJsonSchema6,
1498
+ inputZod: InputSchema8,
1499
+ inputJsonSchema: InputJsonSchema8,
1337
1500
  // status is special — it doesn't dispatch to a model
1338
1501
  allowedModels: [],
1339
1502
  defaultModel: "",
1340
- run: run6
1503
+ run: run8
1341
1504
  };
1342
1505
  }
1343
1506
  });
1344
- async function run7(input, ctx) {
1507
+ async function run9(input, ctx) {
1345
1508
  const product = await ctx.client.extractProduct(input.url);
1346
1509
  const summary = [
1347
1510
  `Product: ${product.name}`,
@@ -1358,13 +1521,13 @@ function truncate(input, max) {
1358
1521
  if (input.length <= max) return input;
1359
1522
  return input.slice(0, max - 3) + "...";
1360
1523
  }
1361
- var InputSchema7, InputJsonSchema7, fromUrlSkill;
1524
+ var InputSchema9, InputJsonSchema9, fromUrlSkill;
1362
1525
  var init_from_url = __esm({
1363
1526
  "src/skills/from-url.ts"() {
1364
- InputSchema7 = z.object({
1527
+ InputSchema9 = z.object({
1365
1528
  url: z.string().min(1).max(2048).describe("Product page URL. Shopify, BigCommerce, WooCommerce, or any page with JSON-LD / OpenGraph product metadata.")
1366
1529
  });
1367
- InputJsonSchema7 = {
1530
+ InputJsonSchema9 = {
1368
1531
  type: "object",
1369
1532
  properties: {
1370
1533
  url: {
@@ -1377,12 +1540,12 @@ var init_from_url = __esm({
1377
1540
  fromUrlSkill = {
1378
1541
  name: "pixeldojo_from_url",
1379
1542
  description: "Extract a product profile from a product page URL: name, description, image URLs (the first three are listed), and the extraction method used. Works on Shopify, BigCommerce, WooCommerce, or any page with JSON-LD / OpenGraph product metadata. Free. pixeldojo_campaign and pixeldojo_ad accept a product URL and run this extraction themselves; call this directly to check the product data first, or to reuse its images in other tools.",
1380
- inputZod: InputSchema7,
1381
- inputJsonSchema: InputJsonSchema7,
1543
+ inputZod: InputSchema9,
1544
+ inputJsonSchema: InputJsonSchema9,
1382
1545
  // No model dispatch — this skill calls a server-side scrape, not a generation.
1383
1546
  allowedModels: [],
1384
1547
  defaultModel: "",
1385
- run: run7
1548
+ run: run9
1386
1549
  };
1387
1550
  }
1388
1551
  });
@@ -1945,7 +2108,7 @@ async function redoShots(input, ctx) {
1945
2108
  followUp: keepGoing(filmId, 30, { soundtrack: input.soundtrack })
1946
2109
  };
1947
2110
  }
1948
- async function run8(input, ctx) {
2111
+ async function run10(input, ctx) {
1949
2112
  const action = input.action === "plan" && input.film_id ? input.cast?.length ? "cast" : "status" : input.action;
1950
2113
  if (action === "plan") return plan(input, ctx);
1951
2114
  if (!input.film_id) return { kind: "text", text: "produce/status/cast/redo need the film_id returned by the plan step." };
@@ -1965,7 +2128,7 @@ async function run8(input, ctx) {
1965
2128
  if (action === "redo") return redoShots(input, ctx);
1966
2129
  return advance(input.film_id, input.soundtrack, input.music_job_id, ctx);
1967
2130
  }
1968
- var STORY_LOOKS, MUSIC_VIDEO_LOOKS, LOOKS, MUSIC_CLIP_ENGINE_ID, InputSchema8, InputJsonSchema8, shots, benignDispatchRefusal, count, frameDone, RETRY_CLEARS, REFUSED, isRefused, PLANNER_POLL_MS, PLANNER_MAX_POLLS, isMusicVideo, needsSlice, stillPlanningText, LOOK_MUSIC, ARC, filmSkill;
2131
+ var STORY_LOOKS, MUSIC_VIDEO_LOOKS, LOOKS, MUSIC_CLIP_ENGINE_ID, InputSchema10, InputJsonSchema10, shots, benignDispatchRefusal, count, frameDone, RETRY_CLEARS, REFUSED, isRefused, PLANNER_POLL_MS, PLANNER_MAX_POLLS, isMusicVideo, needsSlice, stillPlanningText, LOOK_MUSIC, ARC, filmSkill;
1969
2132
  var init_film = __esm({
1970
2133
  "src/skills/film.ts"() {
1971
2134
  init_upload_helpers();
@@ -1973,7 +2136,7 @@ var init_film = __esm({
1973
2136
  MUSIC_VIDEO_LOOKS = ["rap-video", "neon-club", "stage-performance", "retro-camcorder", "y2k-pop", "moody-rnb", "rock-grit", "dream-pop", "k-pop"];
1974
2137
  LOOKS = [...STORY_LOOKS, ...MUSIC_VIDEO_LOOKS];
1975
2138
  MUSIC_CLIP_ENGINE_ID = "minimax-h3-reference";
1976
- InputSchema8 = z.object({
2139
+ InputSchema10 = z.object({
1977
2140
  idea: z.string().max(2e3).optional().describe("One-to-three sentence film idea. The studio writes the screenplay (free). Provide idea OR script for planning."),
1978
2141
  script: z.string().max(2e4).optional().describe("A full screenplay to plan instead of an idea (sluglines, action lines, dialogue)."),
1979
2142
  look: z.enum(LOOKS).optional().describe("Visual style carried through frames, clips, and soundtrack. Default live-action, or rap-video for a music video."),
@@ -2001,7 +2164,7 @@ var init_film = __esm({
2001
2164
  soundtrack: z.enum(["scene-synced", "flat", "none"]).default("scene-synced").describe("scene-synced composes one musical section per scene, pinned to the scene lengths (default). flat is a single cohesive bed. none skips music."),
2002
2165
  music_job_id: z.string().max(80).optional().describe("The music_job_id from the previous status reply while the score is composing, so that score is attached. A status call without it, once all clips are ready and no score is attached, starts and charges for a new score.")
2003
2166
  });
2004
- InputJsonSchema8 = {
2167
+ InputJsonSchema10 = {
2005
2168
  type: "object",
2006
2169
  properties: {
2007
2170
  idea: { type: "string", description: `Plan step: one-to-three sentence film idea. The studio writes the screenplay (free). Provide idea or script. Keep the user's own words for who each character is, including their gender and pronouns ("she", "he") and species; the writer has nothing else to go on.` },
@@ -2115,11 +2278,11 @@ var init_film = __esm({
2115
2278
  name: "pixeldojo_film",
2116
2279
  description: "Make a finished short film from one idea: screenplay, storyboard frames (AI-reviewed), video clips with spoken dialogue, scene-synced soundtrack, stitched export. Or a music video: pass song_path (a local file) or song_url (your own PixelDojo upload from pixeldojo_upload, never a third-party link) with optional lyrics; the film is cut to that song, each clip lip-synced to its slice on the studio music video engine, and no score is composed: the export plays the song. Every named character must be cast (cast: your own images, a portrait prompt, or a Character Studio character) before frames render; cast entries can also replace or remove someone. Phased: plan is free; cast; produce starts charged generation; status advances production (poll every ~30s); redo renders a new take of chosen shots and status re-exports.",
2117
2280
  hostedDescription: "Make a finished short film from one idea: screenplay, storyboard frames (AI-reviewed), video clips with spoken dialogue, scene-synced soundtrack, stitched export. Or a music video: pass song_url (your own PixelDojo upload made with POST /api/v1/upload, never a third-party link) with optional lyrics; the film is cut to that song, each clip lip-synced to its slice on the studio music video engine, and no score is composed: the export plays the song. Every named character must be cast (cast: your own images, a portrait prompt, or a Character Studio character) before frames render; cast entries can also replace or remove someone. Phased: plan is free; cast; produce starts charged generation; status advances production (poll every ~30s); redo renders a new take of chosen shots and status re-exports.",
2118
- inputZod: InputSchema8,
2119
- inputJsonSchema: InputJsonSchema8,
2281
+ inputZod: InputSchema10,
2282
+ inputJsonSchema: InputJsonSchema10,
2120
2283
  allowedModels: [],
2121
2284
  defaultModel: "",
2122
- run: run8
2285
+ run: run10
2123
2286
  };
2124
2287
  }
2125
2288
  });
@@ -2206,7 +2369,7 @@ function buildVideoPayload(verb, apiId, i) {
2206
2369
  };
2207
2370
  }
2208
2371
  }
2209
- async function run9(input, ctx) {
2372
+ async function run11(input, ctx) {
2210
2373
  const paramsGate = paramsWithoutModel(input.params, input.model);
2211
2374
  if (paramsGate) return { kind: "text", text: paramsGate };
2212
2375
  const verb = input.verb;
@@ -2269,7 +2432,7 @@ async function run9(input, ctx) {
2269
2432
  notes.join(" ")
2270
2433
  );
2271
2434
  }
2272
- var VIDEO_STUDIO_ENGINES, VERBS, BACKWARD_ENGINES, clamp, NEEDS_VIDEO_DURATION, NO_IMAGE_ENGINES, InputSchema9, engineList, InputJsonSchema9, BASE_DESCRIPTION, videoSkill;
2435
+ var VIDEO_STUDIO_ENGINES, VERBS, BACKWARD_ENGINES, clamp, NEEDS_VIDEO_DURATION, NO_IMAGE_ENGINES, InputSchema11, engineList, InputJsonSchema11, BASE_DESCRIPTION, videoSkill;
2273
2436
  var init_video = __esm({
2274
2437
  "src/skills/video.ts"() {
2275
2438
  init_poll();
@@ -2303,7 +2466,7 @@ var init_video = __esm({
2303
2466
  "wan-video-character-swap"
2304
2467
  ]);
2305
2468
  NO_IMAGE_ENGINES = /* @__PURE__ */ new Set(["flux-video-edit", "xai-video-edit"]);
2306
- InputSchema9 = z.object({
2469
+ InputSchema11 = z.object({
2307
2470
  verb: z.enum(VERBS),
2308
2471
  prompt: z.string().max(8e3).optional(),
2309
2472
  video_url: z.string().url().optional(),
@@ -2318,7 +2481,7 @@ var init_video = __esm({
2318
2481
  params: ParamsInput
2319
2482
  });
2320
2483
  engineList = (verb) => `${verb}: ${VIDEO_STUDIO_ENGINES[verb].join(", ")} (default ${VIDEO_STUDIO_ENGINES[verb][0]})`;
2321
- InputJsonSchema9 = {
2484
+ InputJsonSchema11 = {
2322
2485
  type: "object",
2323
2486
  properties: {
2324
2487
  verb: {
@@ -2379,11 +2542,11 @@ var init_video = __esm({
2379
2542
  name: "pixeldojo_video",
2380
2543
  description: `${BASE_DESCRIPTION} Local files: video_path / image_path (uploaded to 24h temp storage first).`,
2381
2544
  hostedDescription: `${BASE_DESCRIPTION} Media must be public http(s) URLs.`,
2382
- inputZod: InputSchema9,
2383
- inputJsonSchema: InputJsonSchema9,
2545
+ inputZod: InputSchema11,
2546
+ inputJsonSchema: InputJsonSchema11,
2384
2547
  allowedModels: Array.from(new Set(VERBS.flatMap((v) => VIDEO_STUDIO_ENGINES[v]))),
2385
2548
  defaultModel: VIDEO_STUDIO_ENGINES.change[0],
2386
- run: run9
2549
+ run: run11
2387
2550
  };
2388
2551
  }
2389
2552
  });
@@ -2558,28 +2721,28 @@ async function produce2(id, ctx) {
2558
2721
  const musicNote = ep.musicEnabled && ep.music?.status === "failed" ? " The music bed failed, so this cut has no music." : "";
2559
2722
  return { kind: "text", text: `Export started, free${prior}.${musicNote} ${next(id, "produce", "~60s")}` };
2560
2723
  }
2561
- async function run10(input, ctx) {
2724
+ async function run12(input, ctx) {
2562
2725
  const action = input.action === "create" && input.episode_id ? "status" : input.action;
2563
2726
  if (action === "create") return create(input, ctx);
2564
2727
  if (!input.episode_id) return { kind: "text", text: "produce and status need the episode_id returned by the create step." };
2565
2728
  if (action === "produce") return produce2(input.episode_id, ctx);
2566
2729
  return status(input.episode_id, ctx);
2567
2730
  }
2568
- var STYLES, CATEGORIES, ENGINES, MIN_SECONDS, MAX_SECONDS, MAX_IN_FLIGHT, InputSchema10, InputJsonSchema10, call, next, needsRender, PLAN_POLL_MS, PLAN_MAX_POLLS, stillPlanning, episodeSkill;
2731
+ var STYLES2, CATEGORIES, ENGINES, MIN_SECONDS, MAX_SECONDS, MAX_IN_FLIGHT, InputSchema12, InputJsonSchema12, call, next, needsRender, PLAN_POLL_MS, PLAN_MAX_POLLS, stillPlanning, episodeSkill;
2569
2732
  var init_episode = __esm({
2570
2733
  "src/skills/episode.ts"() {
2571
- STYLES = ["editorial-motion", "paper-diorama", "hand-drawn", "storybook", "poster-vector", "soft-3d"];
2734
+ STYLES2 = ["editorial-motion", "paper-diorama", "hand-drawn", "storybook", "poster-vector", "soft-3d"];
2572
2735
  CATEGORIES = ["education", "history", "kids", "storytelling", "custom"];
2573
2736
  ENGINES = ["omni-draft", "omni-final", "still-motion"];
2574
2737
  MIN_SECONDS = 60;
2575
2738
  MAX_SECONDS = 300;
2576
2739
  MAX_IN_FLIGHT = 4;
2577
- InputSchema10 = z.object({
2740
+ InputSchema12 = z.object({
2578
2741
  action: z.enum(["create", "produce", "status"]).default("create"),
2579
2742
  topic: z.string().min(8).max(400).optional(),
2580
2743
  episode_id: z.string().max(40).optional(),
2581
2744
  category: z.enum(CATEGORIES).default("education"),
2582
- style: z.enum(STYLES).default("editorial-motion"),
2745
+ style: z.enum(STYLES2).default("editorial-motion"),
2583
2746
  aspect: z.enum(["16:9", "9:16"]).default("16:9"),
2584
2747
  length_seconds: z.number().int().min(MIN_SECONDS).max(MAX_SECONDS).default(120),
2585
2748
  voice: z.string().min(1).max(80).default("Wise_Woman"),
@@ -2587,7 +2750,7 @@ var init_episode = __esm({
2587
2750
  music: z.boolean().default(true),
2588
2751
  title: z.string().max(120).optional()
2589
2752
  });
2590
- InputJsonSchema10 = {
2753
+ InputJsonSchema12 = {
2591
2754
  type: "object",
2592
2755
  properties: {
2593
2756
  action: {
@@ -2598,7 +2761,7 @@ var init_episode = __esm({
2598
2761
  topic: { type: "string", description: "Create step: what the episode is about, 8 to 400 characters." },
2599
2762
  episode_id: { type: "string", description: "The episode_id from the create step. Required for produce and status." },
2600
2763
  category: { type: "string", enum: [...CATEGORIES], description: "Create step: tone of the script. Default education." },
2601
- style: { type: "string", enum: [...STYLES], description: "Create step: visual style of every scene. Default editorial-motion." },
2764
+ style: { type: "string", enum: [...STYLES2], description: "Create step: visual style of every scene. Default editorial-motion." },
2602
2765
  aspect: { type: "string", enum: ["16:9", "9:16"], description: "Create step: 16:9 landscape (default) or 9:16 vertical." },
2603
2766
  length_seconds: { type: "integer", minimum: MIN_SECONDS, maximum: MAX_SECONDS, description: "Create step: target length in seconds. Default 120." },
2604
2767
  voice: { type: "string", description: "Create step: narrator voice id from the text-to-speech tool (e.g. Wise_Woman, Deep_Voice_Man, Friendly_Person). Default Wise_Woman." },
@@ -2620,15 +2783,15 @@ var init_episode = __esm({
2620
2783
  episodeSkill = {
2621
2784
  name: "pixeldojo_episode",
2622
2785
  description: "Make a finished narrated faceless video (explainer, history, kids story) from one topic: script, scene visuals, AI narration, music bed, stitched export saved to the Library. Phased: create is free and returns a credit quote; produce is charged and advances one step per call (poll every ~30s until it returns the video URL); status is free.",
2623
- inputZod: InputSchema10,
2624
- inputJsonSchema: InputJsonSchema10,
2786
+ inputZod: InputSchema12,
2787
+ inputJsonSchema: InputJsonSchema12,
2625
2788
  allowedModels: [],
2626
2789
  defaultModel: "",
2627
- run: run10
2790
+ run: run12
2628
2791
  };
2629
2792
  }
2630
2793
  });
2631
- async function run11(input, ctx) {
2794
+ async function run13(input, ctx) {
2632
2795
  const productImages = [
2633
2796
  ...input.product_image_urls ?? [],
2634
2797
  ...await Promise.all((input.product_image_paths ?? []).map((p) => uploadLocalFile(p, ctx)))
@@ -2671,13 +2834,13 @@ async function run11(input, ctx) {
2671
2834
  (urls) => urls.length === 0 ? `Ad job ${submitted.jobId} completed but returned no assets.` : `Ad rendered (style: ${styleLabel}${submitted.ad?.product?.name ? `, product: ${submitted.ad.product.name}` : ""}). Video: ${urls[0]}`
2672
2835
  );
2673
2836
  }
2674
- var InputSchema11, InputJsonSchema11, adSkill;
2837
+ var InputSchema13, InputJsonSchema13, adSkill;
2675
2838
  var init_ad = __esm({
2676
2839
  "src/skills/ad.ts"() {
2677
2840
  init_types();
2678
2841
  init_poll();
2679
2842
  init_upload_helpers();
2680
- InputSchema11 = z.object({
2843
+ InputSchema13 = z.object({
2681
2844
  style: z.string().max(80).optional().describe(
2682
2845
  "Ad style slug (default ugc-testimonial). Catalog: GET /api/v1/marketing/presets \u2014 e.g. ugc-testimonial, unboxing-reveal, unboxing-asmr, virtual-try-on, street-style, product-showcase, macro-details, crush-test, hyper-motion, before-after, day-in-the-life, the-gift, tv-spot."
2683
2846
  ),
@@ -2703,7 +2866,7 @@ var init_ad = __esm({
2703
2866
  path: ["product_url"]
2704
2867
  }
2705
2868
  );
2706
- InputJsonSchema11 = {
2869
+ InputJsonSchema13 = {
2707
2870
  type: "object",
2708
2871
  properties: {
2709
2872
  style: {
@@ -2790,16 +2953,16 @@ var init_ad = __esm({
2790
2953
  name: "pixeldojo_ad",
2791
2954
  description: "Marketing Studio: turn a product into a rendered video ad with voiceover. Requires `product_url` (name, description and images are extracted), or `product_name` plus product_image_urls / product_image_paths. Pick a `style` slug (default ugc-testimonial; full catalog at GET https://pixeldojo.ai/api/v1/marketing/presets), and optionally add presenter reference images, a location, a hook, and the exact spoken script. Returns the video URL, or a jobId to check with pixeldojo_status.",
2792
2955
  hostedDescription: "Marketing Studio: turn a product into a rendered video ad with voiceover. Requires `product_url` (name, description and images are extracted), or `product_name` plus product_image_urls (public http(s) URLs). Pick a `style` slug (default ugc-testimonial; full catalog at GET https://pixeldojo.ai/api/v1/marketing/presets), and optionally add presenter reference images, a location, a hook, and the exact spoken script. Returns the video URL, or a jobId to check with pixeldojo_status.",
2793
- inputZod: InputSchema11,
2794
- inputJsonSchema: InputJsonSchema11,
2956
+ inputZod: InputSchema13,
2957
+ inputJsonSchema: InputJsonSchema13,
2795
2958
  // Composition skill — the server routes to the ad engine internally.
2796
2959
  allowedModels: [],
2797
2960
  defaultModel: "",
2798
- run: run11
2961
+ run: run13
2799
2962
  };
2800
2963
  }
2801
2964
  });
2802
- async function run12(input, ctx) {
2965
+ async function run14(input, ctx) {
2803
2966
  const client = ctx.client;
2804
2967
  const result = await client.runCampaign(input);
2805
2968
  if (result.kind === "campaign") {
@@ -2826,10 +2989,10 @@ async function run12(input, ctx) {
2826
2989
  statusUrl: result.statusUrl ?? ctx.buildStatusUrl(result.campaignId)
2827
2990
  };
2828
2991
  }
2829
- var InputSchema12, InputJsonSchema12, campaignSkill;
2992
+ var InputSchema14, InputJsonSchema14, campaignSkill;
2830
2993
  var init_campaign = __esm({
2831
2994
  "src/skills/campaign.ts"() {
2832
- InputSchema12 = z.object({
2995
+ InputSchema14 = z.object({
2833
2996
  productUrl: z.string().url().optional().describe("Product page URL. We extract name + description + images automatically."),
2834
2997
  product: z.object({
2835
2998
  name: z.string().min(1).max(200),
@@ -2845,7 +3008,7 @@ var init_campaign = __esm({
2845
3008
  message: "productUrl or product is required",
2846
3009
  path: ["productUrl"]
2847
3010
  });
2848
- InputJsonSchema12 = {
3011
+ InputJsonSchema14 = {
2849
3012
  type: "object",
2850
3013
  properties: {
2851
3014
  productUrl: {
@@ -2896,17 +3059,17 @@ var init_campaign = __esm({
2896
3059
  campaignSkill = {
2897
3060
  name: "pixeldojo_campaign",
2898
3061
  description: "Generate a coherent product launch package (hero image + N lifestyle variants + optional vertical video) from one URL or product profile. Composes hero / lifestyle / video models server-side; returns assets in one call or a handoff with campaignId to poll via pixeldojo_campaign_status.",
2899
- inputZod: InputSchema12,
2900
- inputJsonSchema: InputJsonSchema12,
3062
+ inputZod: InputSchema14,
3063
+ inputJsonSchema: InputJsonSchema14,
2901
3064
  // Composition skill — internal sub-jobs route to per-tool models, no
2902
3065
  // single allowed-models list applies here.
2903
3066
  allowedModels: [],
2904
3067
  defaultModel: "",
2905
- run: run12
3068
+ run: run14
2906
3069
  };
2907
3070
  }
2908
3071
  });
2909
- async function run13(input, ctx) {
3072
+ async function run15(input, ctx) {
2910
3073
  const client = ctx.client;
2911
3074
  const result = await client.getCampaign(input.campaignId);
2912
3075
  if (result.kind === "campaign") {
@@ -2936,13 +3099,13 @@ async function run13(input, ctx) {
2936
3099
  statusUrl: result.statusUrl ?? ctx.buildStatusUrl(result.campaignId)
2937
3100
  };
2938
3101
  }
2939
- var InputSchema13, InputJsonSchema13, campaignStatusSkill;
3102
+ var InputSchema15, InputJsonSchema15, campaignStatusSkill;
2940
3103
  var init_campaign_status = __esm({
2941
3104
  "src/skills/campaign-status.ts"() {
2942
- InputSchema13 = z.object({
3105
+ InputSchema15 = z.object({
2943
3106
  campaignId: z.string().min(1).max(200).describe("Campaign id returned from a prior pixeldojo_campaign handoff.")
2944
3107
  });
2945
- InputJsonSchema13 = {
3108
+ InputJsonSchema15 = {
2946
3109
  type: "object",
2947
3110
  properties: {
2948
3111
  campaignId: {
@@ -2955,15 +3118,15 @@ var init_campaign_status = __esm({
2955
3118
  campaignStatusSkill = {
2956
3119
  name: "pixeldojo_campaign_status",
2957
3120
  description: "Poll a campaign by campaignId returned from pixeldojo_campaign. Returns assets when every sub-job is done, or a handoff describing what is still in flight.",
2958
- inputZod: InputSchema13,
2959
- inputJsonSchema: InputJsonSchema13,
3121
+ inputZod: InputSchema15,
3122
+ inputJsonSchema: InputJsonSchema15,
2960
3123
  allowedModels: [],
2961
3124
  defaultModel: "",
2962
- run: run13
3125
+ run: run15
2963
3126
  };
2964
3127
  }
2965
3128
  });
2966
- async function run14(input, ctx) {
3129
+ async function run16(input, ctx) {
2967
3130
  let url;
2968
3131
  try {
2969
3132
  url = await uploadLocalFile(input.path, ctx);
@@ -2979,16 +3142,16 @@ async function run14(input, ctx) {
2979
3142
  (temp storage; auto-deletes in ~24h. Pass this URL as image_url, video_url for a clip, or song_url on pixeldojo_film for a song, to any pixeldojo tool that takes one.)`
2980
3143
  };
2981
3144
  }
2982
- var InputSchema14, InputJsonSchema14, uploadSkill;
3145
+ var InputSchema16, InputJsonSchema16, uploadSkill;
2983
3146
  var init_upload = __esm({
2984
3147
  "src/skills/upload.ts"() {
2985
3148
  init_upload_helpers();
2986
- InputSchema14 = z.object({
3149
+ InputSchema16 = z.object({
2987
3150
  path: z.string().min(1).max(4096).describe(
2988
3151
  "Absolute local file path to upload. JPEG, PNG, WebP, GIF (\u226450MB), MP4, WebM, MOV (\u2264200MB) or MP3, WAV, M4A, AAC, OGG, FLAC audio (\u226450MB). Returns a public URL on the temp CDN; storage auto-expires after 24 hours."
2989
3152
  )
2990
3153
  });
2991
- InputJsonSchema14 = {
3154
+ InputJsonSchema16 = {
2992
3155
  type: "object",
2993
3156
  properties: {
2994
3157
  path: {
@@ -3001,15 +3164,15 @@ var init_upload = __esm({
3001
3164
  uploadSkill = {
3002
3165
  name: "pixeldojo_upload",
3003
3166
  description: "Upload a local image, video or audio file (JPEG, PNG, WebP, GIF up to 50MB; MP4, WebM, MOV up to 200MB; MP3, WAV, M4A, AAC, OGG, FLAC up to 50MB) and get back a public URL that expires after 24 hours. Free. Pass an audio URL as song_url to pixeldojo_film for a music video. Pass the URL as image_url (stills) or video_url (clips) to tools that take hosted URLs only, such as pixeldojo_character, pixeldojo_storyboard and pixeldojo_upscale. pixeldojo_generate, pixeldojo_edit and pixeldojo_ad take local paths directly and need no separate upload.",
3004
- inputZod: InputSchema14,
3005
- inputJsonSchema: InputJsonSchema14,
3167
+ inputZod: InputSchema16,
3168
+ inputJsonSchema: InputJsonSchema16,
3006
3169
  allowedModels: [],
3007
3170
  defaultModel: "",
3008
- run: run14
3171
+ run: run16
3009
3172
  };
3010
3173
  }
3011
3174
  });
3012
- async function run15(input, ctx) {
3175
+ async function run17(input, ctx) {
3013
3176
  const requested = input.model ?? DEFAULT_MODEL6;
3014
3177
  if (!ALLOWED4.includes(requested)) {
3015
3178
  return {
@@ -3047,14 +3210,14 @@ async function run15(input, ctx) {
3047
3210
  (urls) => urls.length === 0 ? `Job ${submitted.jobId} completed but returned no assets.` : `Audio ready. Asset: ${urls[0]}`
3048
3211
  );
3049
3212
  }
3050
- var ALLOWED4, DEFAULT_MODEL6, InputSchema15, InputJsonSchema15, audioSkill;
3213
+ var ALLOWED4, DEFAULT_MODEL6, InputSchema17, InputJsonSchema17, audioSkill;
3051
3214
  var init_audio = __esm({
3052
3215
  "src/skills/audio.ts"() {
3053
3216
  init_poll();
3054
3217
  init_types();
3055
3218
  ALLOWED4 = ["seed-audio", "text-to-speech"];
3056
3219
  DEFAULT_MODEL6 = "seed-audio";
3057
- InputSchema15 = z.object({
3220
+ InputSchema17 = z.object({
3058
3221
  prompt: z.string().min(1).describe(
3059
3222
  "seed-audio: a script to speak, or a scene description (dialogue, sound effects, music), up to 2048 characters; refer to audio_urls clips in order as @Audio1..@Audio3. text-to-speech: the exact text to speak, up to 10,000 characters."
3060
3223
  ),
@@ -3064,7 +3227,7 @@ var init_audio = __esm({
3064
3227
  output_format: z.string().optional().describe("Output format. seed-audio: mp3 (default), wav, pcm, ogg_opus. text-to-speech: mp3 (default), wav, flac, pcm."),
3065
3228
  speed: z.number().min(0.5).max(2).optional().describe("Speech speed multiplier, 0.5-2 (default 1). Both models.")
3066
3229
  });
3067
- InputJsonSchema15 = {
3230
+ InputJsonSchema17 = {
3068
3231
  type: "object",
3069
3232
  properties: {
3070
3233
  prompt: {
@@ -3102,15 +3265,15 @@ var init_audio = __esm({
3102
3265
  audioSkill = {
3103
3266
  name: "pixeldojo_audio",
3104
3267
  description: "Generate audio: speech, multi-character dialogue, sound effects, and background music from one prompt (Seed Audio 1.0 by default; plain text-to-speech available). Voice cloning takes hosted reference clip URLs in audio_urls; local audio files are not accepted. Returns an audio URL, or a jobId for pixeldojo_status.",
3105
- inputZod: InputSchema15,
3106
- inputJsonSchema: InputJsonSchema15,
3268
+ inputZod: InputSchema17,
3269
+ inputJsonSchema: InputJsonSchema17,
3107
3270
  allowedModels: [...ALLOWED4],
3108
3271
  defaultModel: DEFAULT_MODEL6,
3109
- run: run15
3272
+ run: run17
3110
3273
  };
3111
3274
  }
3112
3275
  });
3113
- async function run16(input, ctx) {
3276
+ async function run18(input, ctx) {
3114
3277
  if (input.source === "characters") {
3115
3278
  const { characters } = await ctx.client.listCharacters({ q: input.q, limit: input.limit ?? 25 });
3116
3279
  if (characters.length === 0) {
@@ -3161,17 +3324,17 @@ ${lines.join("\n")}
3161
3324
  Pass any URL back into pixeldojo_generate, pixeldojo_edit, pixeldojo_character, or pixeldojo_upscale as a reference input.`
3162
3325
  };
3163
3326
  }
3164
- var InputSchema16, InputJsonSchema16, librarySkill;
3327
+ var InputSchema18, InputJsonSchema18, librarySkill;
3165
3328
  var init_library = __esm({
3166
3329
  "src/skills/library.ts"() {
3167
- InputSchema16 = z.object({
3330
+ InputSchema18 = z.object({
3168
3331
  source: z.enum(["media", "characters"]).optional().describe("media (default): saved and recent results. characters: saved Character Studio characters and elements, usable as @Name in prompts."),
3169
3332
  q: z.string().optional().describe("Substring match against the prompt that produced the asset."),
3170
3333
  model: z.string().optional().describe("Restrict to a single model (apiId)."),
3171
3334
  modality: z.enum(["image", "video", "audio"]).optional().describe("Restrict to one output type."),
3172
3335
  limit: z.number().int().min(1).max(50).optional().describe("Max results (default 10).")
3173
3336
  });
3174
- InputJsonSchema16 = {
3337
+ InputJsonSchema18 = {
3175
3338
  type: "object",
3176
3339
  properties: {
3177
3340
  source: {
@@ -3196,15 +3359,15 @@ var init_library = __esm({
3196
3359
  librarySkill = {
3197
3360
  name: "pixeldojo_library",
3198
3361
  description: "Search your Library, newest first. source=media (default): saved media (permanent) and recent API/MCP generations (deleted 1 hour after they finish), as URLs to reuse as references. source=characters: saved Character Studio characters and elements, each usable as @Name in any prompt.",
3199
- inputZod: InputSchema16,
3200
- inputJsonSchema: InputJsonSchema16,
3362
+ inputZod: InputSchema18,
3363
+ inputJsonSchema: InputJsonSchema18,
3201
3364
  allowedModels: [],
3202
3365
  defaultModel: "",
3203
- run: run16
3366
+ run: run18
3204
3367
  };
3205
3368
  }
3206
3369
  });
3207
- async function run17(input, ctx) {
3370
+ async function run19(input, ctx) {
3208
3371
  const res = await ctx.client.saveMedia({
3209
3372
  url: input.url,
3210
3373
  prompt: input.prompt,
@@ -3218,17 +3381,17 @@ async function run17(input, ctx) {
3218
3381
  It will appear at https://pixeldojo.ai/my-images and in pixeldojo_library searches.`
3219
3382
  };
3220
3383
  }
3221
- var InputSchema17, InputJsonSchema17, saveSkill;
3384
+ var InputSchema19, InputJsonSchema19, saveSkill;
3222
3385
  var init_save = __esm({
3223
3386
  "src/skills/save.ts"() {
3224
- InputSchema17 = z.object({
3387
+ InputSchema19 = z.object({
3225
3388
  url: z.string().url().describe("The asset URL to save (image or video)."),
3226
3389
  prompt: z.string().optional().describe("The prompt that produced it \u2014 makes it findable in the library later."),
3227
3390
  tool_name: z.string().optional().describe("Tool/model slug that produced it."),
3228
3391
  modality: z.enum(["image", "video", "audio"]).optional(),
3229
3392
  job_id: z.string().optional().describe("The jobId that produced this asset, when known.")
3230
3393
  });
3231
- InputJsonSchema17 = {
3394
+ InputJsonSchema19 = {
3232
3395
  type: "object",
3233
3396
  properties: {
3234
3397
  url: { type: "string", format: "uri", description: "The result URL to keep (image or video from a PixelDojo job)." },
@@ -3242,15 +3405,15 @@ var init_save = __esm({
3242
3405
  saveSkill = {
3243
3406
  name: "pixeldojo_save",
3244
3407
  description: `Save a generated image or video into the user's My Media library permanently ("save this", "keep it"). Results otherwise expire 1 hour after they finish. Pass the result URL; it is safety-checked before it is kept.`,
3245
- inputZod: InputSchema17,
3246
- inputJsonSchema: InputJsonSchema17,
3408
+ inputZod: InputSchema19,
3409
+ inputJsonSchema: InputJsonSchema19,
3247
3410
  allowedModels: [],
3248
3411
  defaultModel: "",
3249
- run: run17
3412
+ run: run19
3250
3413
  };
3251
3414
  }
3252
3415
  });
3253
- async function run18(input, ctx) {
3416
+ async function run20(input, ctx) {
3254
3417
  const res = await ctx.client.saveCharacter({
3255
3418
  name: input.name,
3256
3419
  kind: input.kind ?? "character",
@@ -3262,17 +3425,17 @@ async function run18(input, ctx) {
3262
3425
  text: `Saved ${res.kind} "${res.name}" to Character Studio (${res.image_urls.length} image${res.image_urls.length === 1 ? "" : "s"}). Use it in any prompt as ${res.mention}, for example "${res.mention} walking on a beach at sunset". It also appears at https://pixeldojo.ai/characters.`
3263
3426
  };
3264
3427
  }
3265
- var KINDS, InputSchema18, InputJsonSchema18, saveCharacterSkill;
3428
+ var KINDS, InputSchema20, InputJsonSchema20, saveCharacterSkill;
3266
3429
  var init_save_character = __esm({
3267
3430
  "src/skills/save-character.ts"() {
3268
3431
  KINDS = ["character", "product", "outfit", "location", "object", "style"];
3269
- InputSchema18 = z.object({
3432
+ InputSchema20 = z.object({
3270
3433
  name: z.string().min(1).max(60).describe("What to call it. Used as @Name in prompts."),
3271
3434
  kind: z.enum(KINDS).optional().describe("Default character."),
3272
3435
  image_urls: z.array(z.string().url()).max(8).optional().describe("3 to 5 photos from different angles works best for a character."),
3273
3436
  description: z.string().max(1500).optional().describe("Appearance notes, or the scene text for an element.")
3274
3437
  });
3275
- InputJsonSchema18 = {
3438
+ InputJsonSchema20 = {
3276
3439
  type: "object",
3277
3440
  properties: {
3278
3441
  name: { type: "string", description: "What to call it. Used as @Name in prompts afterwards." },
@@ -3290,11 +3453,11 @@ var init_save_character = __esm({
3290
3453
  saveCharacterSkill = {
3291
3454
  name: "pixeldojo_save_character",
3292
3455
  description: "Save a character (a person, from 3-5 photos) or an element (product, outfit, location, prop, style) to the user's Character Studio library. Afterwards write @Name in any prompt to use it. List saved ones with pixeldojo_library source=characters.",
3293
- inputZod: InputSchema18,
3294
- inputJsonSchema: InputJsonSchema18,
3456
+ inputZod: InputSchema20,
3457
+ inputJsonSchema: InputJsonSchema20,
3295
3458
  allowedModels: [],
3296
3459
  defaultModel: "",
3297
- run: run18
3460
+ run: run20
3298
3461
  };
3299
3462
  }
3300
3463
  });
@@ -3543,6 +3706,7 @@ __export(skills_exports, {
3543
3706
  audioSkill: () => audioSkill,
3544
3707
  campaignSkill: () => campaignSkill,
3545
3708
  campaignStatusSkill: () => campaignStatusSkill,
3709
+ captionSkill: () => captionSkill,
3546
3710
  characterSkill: () => characterSkill,
3547
3711
  editSkill: () => editSkill,
3548
3712
  episodeSkill: () => episodeSkill,
@@ -3553,6 +3717,7 @@ __export(skills_exports, {
3553
3717
  generateSkill: () => generateSkill,
3554
3718
  librarySkill: () => librarySkill,
3555
3719
  listWorkflowsSkill: () => listWorkflowsSkill,
3720
+ overlaySkill: () => overlaySkill,
3556
3721
  runWorkflowSkill: () => runWorkflowSkill,
3557
3722
  saveCharacterSkill: () => saveCharacterSkill,
3558
3723
  saveSkill: () => saveSkill,
@@ -3590,6 +3755,8 @@ var init_skills = __esm({
3590
3755
  init_character();
3591
3756
  init_storyboard();
3592
3757
  init_upscale();
3758
+ init_caption();
3759
+ init_overlay();
3593
3760
  init_status();
3594
3761
  init_from_url();
3595
3762
  init_film();
@@ -3610,6 +3777,8 @@ var init_skills = __esm({
3610
3777
  characterSkill,
3611
3778
  storyboardSkill,
3612
3779
  upscaleSkill,
3780
+ captionSkill,
3781
+ overlaySkill,
3613
3782
  audioSkill,
3614
3783
  librarySkill,
3615
3784
  saveSkill,
@@ -3649,6 +3818,8 @@ var init_skills = __esm({
3649
3818
  pixeldojo_character: render("Consistent character image"),
3650
3819
  pixeldojo_storyboard: render("Storyboard preview video"),
3651
3820
  pixeldojo_upscale: render("Upscale image or video"),
3821
+ pixeldojo_caption: render("Caption video"),
3822
+ pixeldojo_overlay: render("Add offer or end card"),
3652
3823
  pixeldojo_audio: render("Generate music, speech or sound"),
3653
3824
  pixeldojo_video: render("Change, animate or extend video"),
3654
3825
  pixeldojo_ad: render("Make video ad"),
@@ -3719,7 +3890,7 @@ function makeRedactedLogger(target = console) {
3719
3890
  }
3720
3891
 
3721
3892
  // src/server.ts
3722
- var PACKAGE_VERSION = "0.20.0" ;
3893
+ var PACKAGE_VERSION = "0.22.0" ;
3723
3894
  var SERVER_INSTRUCTIONS = "Pixel Dojo generates images, video, audio and short films from one catalog of AI models, billed in credits from the connected Pixel Dojo account. A job that runs past the 30-second wait returns a jobId to check later; check it rather than submitting again, since every new submission is charged. Generated asset URLs are deleted one hour after they are created: pixeldojo_save keeps one in the user's My Media permanently (after a safety check), and finished films stay in the account library. In any prompt, @Name uses a character, product, outfit, location, prop or style the user saved in Character Studio: its photos are attached automatically. pixeldojo_library source=characters lists them, and pixeldojo_save_character saves a new one from 3 to 5 photos.";
3724
3895
  function buildServer(options) {
3725
3896
  if (!options.apiKey) {
@@ -3891,6 +4062,8 @@ function printInit() {
3891
4062
  `# pixeldojo_character Consistent characters across shots.`,
3892
4063
  `# pixeldojo_storyboard Multi-shot scenes from one brief.`,
3893
4064
  `# pixeldojo_upscale Enhance any image or video.`,
4065
+ `# pixeldojo_caption Animated word-by-word captions on a video.`,
4066
+ `# pixeldojo_overlay Offer callout and branded end card on a video.`,
3894
4067
  `# pixeldojo_audio Voices + effects + music in one clip.`,
3895
4068
  `# pixeldojo_library Search your saved media + generations.`,
3896
4069
  `# pixeldojo_*_workflow Save / run / list multi-step chains.`,