@pixeldojo/mcp 0.5.1 → 0.6.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/index.js CHANGED
@@ -258,7 +258,7 @@ var init_generate = __esm({
258
258
  InputSchema = z.object({
259
259
  prompt: z.string().min(1).max(8e3).describe("Prompt describing what to generate."),
260
260
  model: z.string().optional().describe(
261
- 'Override the model. Any apiId from /api/v1/models (e.g. "flux", "google-nano-banana", "veo-3.1", "kling-video"). Omit to use the default.'
261
+ 'Override the model. Any apiId from /api/v1/models (e.g. "flux", "google-nano-banana", "veo-3.1-fast", "kling-video-v3-standard-text"). Omit to use the default.'
262
262
  ),
263
263
  preset: z.string().optional().describe(
264
264
  'Preset slug to seed the params (e.g. "cinematic-portrait", "product-floating-clean", "magazine-cover-text"). Browse the catalog at https://pixeldojo.ai/presets. Explicit fields override preset values.'
@@ -277,7 +277,7 @@ var init_generate = __esm({
277
277
  prompt: { type: "string", description: "Prompt describing what to generate." },
278
278
  model: {
279
279
  type: "string",
280
- description: 'Override the model. Any apiId from /api/v1/models (e.g. "flux", "google-nano-banana", "veo-3.1", "kling-video"). Omit to use the default.'
280
+ description: 'Override the model. Any apiId from /api/v1/models (e.g. "flux", "google-nano-banana", "veo-3.1-fast", "kling-video-v3-standard-text"). Omit to use the default.'
281
281
  },
282
282
  preset: {
283
283
  type: "string",
@@ -471,17 +471,22 @@ async function run3(input, ctx) {
471
471
  };
472
472
  }
473
473
  const apiId = requested;
474
- if ((apiId === "flux-edit" || apiId === "gpt-image-2-edit") && !input.reference_image_url) {
474
+ if (REQUIRES_REFERENCE.has(apiId) && !input.reference_image_url) {
475
475
  return {
476
476
  kind: "text",
477
- text: `pixeldojo:character with model="${apiId}" requires reference_image_url (the source image of the character to edit). For generation from a prompt alone, use model="ideogram-character".`
477
+ text: `pixeldojo:character with model="${apiId}" requires reference_image_url (the source image of the character to edit). For generation from a prompt alone, use model="${DEFAULT_MODEL3}".`
478
478
  };
479
479
  }
480
480
  const body = { prompt: input.prompt };
481
- if (input.aspect_ratio) body.aspect_ratio = input.aspect_ratio;
482
481
  if (input.reference_image_url) {
483
- body.image_url = input.reference_image_url;
484
- body.reference_image_url = input.reference_image_url;
482
+ if (apiId === "flux-edit") {
483
+ body.input_image = input.reference_image_url;
484
+ } else {
485
+ body.image_urls = [input.reference_image_url];
486
+ }
487
+ }
488
+ if (input.aspect_ratio && apiId === "flux-edit" && FLUX_EDIT_ASPECT_RATIOS.has(input.aspect_ratio)) {
489
+ body.aspect_ratio = input.aspect_ratio;
485
490
  }
486
491
  const submitted = await ctx.client.run(apiId, body);
487
492
  const result = await pollJobUntilDoneOrHandoff(
@@ -498,20 +503,35 @@ async function run3(input, ctx) {
498
503
  (urls) => urls.length === 0 ? `Job ${submitted.jobId} completed but returned no assets.` : `Character shot ready. Asset: ${urls[0]}`
499
504
  );
500
505
  }
501
- var ALLOWED, DEFAULT_MODEL3, InputSchema3, InputJsonSchema3, characterSkill;
506
+ var ALLOWED, DEFAULT_MODEL3, REQUIRES_REFERENCE, FLUX_EDIT_ASPECT_RATIOS, InputSchema3, InputJsonSchema3, characterSkill;
502
507
  var init_character = __esm({
503
508
  "src/skills/character.ts"() {
504
509
  init_poll();
505
510
  init_types();
506
- ALLOWED = ["ideogram-character", "flux-edit", "gpt-image-2-edit"];
507
- DEFAULT_MODEL3 = "ideogram-character";
511
+ ALLOWED = ["seedream-5", "seedream-4", "flux-edit", "gpt-image-2-edit"];
512
+ DEFAULT_MODEL3 = "seedream-5";
513
+ REQUIRES_REFERENCE = /* @__PURE__ */ new Set(["flux-edit", "gpt-image-2-edit"]);
514
+ FLUX_EDIT_ASPECT_RATIOS = /* @__PURE__ */ new Set([
515
+ "1:1",
516
+ "16:9",
517
+ "9:16",
518
+ "21:9",
519
+ "9:21",
520
+ "3:2",
521
+ "2:3",
522
+ "4:5",
523
+ "5:4",
524
+ "3:4",
525
+ "4:3",
526
+ "match_input_image"
527
+ ]);
508
528
  InputSchema3 = z.object({
509
529
  prompt: z.string().min(1).max(8e3),
510
530
  model: z.string().optional().describe(`One of: ${ALLOWED.join(", ")}. Defaults to ${DEFAULT_MODEL3}.`),
511
531
  reference_image_url: z.string().url().optional().describe(
512
- "Reference image of the character. Required by flux-edit and gpt-image-2-edit; optional for ideogram-character."
532
+ "Reference image of the character. Required by flux-edit and gpt-image-2-edit; optional for seedream-5/seedream-4."
513
533
  ),
514
- aspect_ratio: z.string().optional()
534
+ aspect_ratio: z.string().optional().describe('Only honored for model="flux-edit".')
515
535
  });
516
536
  InputJsonSchema3 = {
517
537
  type: "object",
@@ -529,7 +549,7 @@ var init_character = __esm({
529
549
  };
530
550
  characterSkill = {
531
551
  name: "pixeldojo:character",
532
- description: "Generate or edit consistent characters across shots. Pass a reference_image_url for edit-style models.",
552
+ description: "Generate or edit consistent characters across shots. Pass a reference_image_url for identity locking (seedream-5 by default).",
533
553
  inputZod: InputSchema3,
534
554
  inputJsonSchema: InputJsonSchema3,
535
555
  allowedModels: [...ALLOWED],
@@ -570,8 +590,13 @@ var init_storyboard = __esm({
570
590
  "src/skills/storyboard.ts"() {
571
591
  init_poll();
572
592
  init_types();
573
- ALLOWED2 = ["veo-3.1", "kling-video", "seedance-2"];
574
- DEFAULT_MODEL4 = "veo-3.1";
593
+ ALLOWED2 = [
594
+ "veo-3.1-fast",
595
+ "veo-3.1-standard",
596
+ "kling-video-v3-standard-text",
597
+ "seedance-2-high"
598
+ ];
599
+ DEFAULT_MODEL4 = "veo-3.1-fast";
575
600
  InputSchema4 = z.object({
576
601
  brief: z.string().min(1).max(8e3).describe("Storyboard brief: scene, shots, mood, action."),
577
602
  model: z.string().optional().describe(`One of: ${ALLOWED2.join(", ")}. Defaults to ${DEFAULT_MODEL4}.`),
@@ -617,11 +642,20 @@ async function run5(input, ctx) {
617
642
  const apiId = requested;
618
643
  const body = {
619
644
  prompt: "",
620
- image_url: input.image_url
645
+ image: input.image_url
621
646
  };
622
647
  if (apiId === "magnific-upscaler") {
623
- body.scale_factor = input.scale_factor ?? 2;
648
+ const allowed = [2, 4, 8, 16];
649
+ const requestedScale = input.scale_factor ?? 2;
650
+ const nearest = allowed.reduce(
651
+ (a, b) => Math.abs(b - requestedScale) < Math.abs(a - requestedScale) ? b : a
652
+ );
653
+ body.scale_factor = `${nearest}x`;
624
654
  if (input.creativity !== void 0) body.creativity = input.creativity;
655
+ } else if (apiId === "creative-upscale") {
656
+ body.scale_factor = Math.min(10, Math.max(1, input.scale_factor ?? 2));
657
+ } else if (apiId === "face-enhance") {
658
+ body.scale_factor = Math.min(8, Math.max(1, input.scale_factor ?? 4));
625
659
  }
626
660
  const submitted = await ctx.client.run(apiId, body);
627
661
  const result = await pollJobUntilDoneOrHandoff(
@@ -648,7 +682,9 @@ var init_upscale = __esm({
648
682
  InputSchema5 = z.object({
649
683
  image_url: z.string().url().describe("URL of the source image to upscale."),
650
684
  model: z.string().optional().describe(`One of: ${ALLOWED3.join(", ")}. Defaults to ${DEFAULT_MODEL5}.`),
651
- scale_factor: z.number().int().min(1).max(4).optional().describe("Magnific only: 1, 2, or 4. Defaults to 2."),
685
+ scale_factor: z.number().min(1).max(16).optional().describe(
686
+ "Upscale multiplier. Magnific snaps to the nearest of 2/4/8/16 (default 2); creative-upscale accepts 1-10 (default 2); face-enhance accepts 1-8 (default 4)."
687
+ ),
652
688
  creativity: z.number().min(0).max(1).optional().describe("Magnific only: 0 (preserve) \u2192 1 (creative). Defaults to 0.")
653
689
  });
654
690
  InputJsonSchema5 = {
@@ -656,7 +692,7 @@ var init_upscale = __esm({
656
692
  properties: {
657
693
  image_url: { type: "string", format: "uri" },
658
694
  model: { type: "string", enum: ALLOWED3 },
659
- scale_factor: { type: "integer", minimum: 1, maximum: 4 },
695
+ scale_factor: { type: "number", minimum: 1, maximum: 16 },
660
696
  creativity: { type: "number", minimum: 0, maximum: 1 }
661
697
  },
662
698
  required: ["image_url"]
@@ -1285,7 +1321,24 @@ async function runWorkflow(input, ctx) {
1285
1321
  text: `Step ${i + 1} (${step.skill}) has invalid args: ${issue?.path.join(".") || "input"} ${issue?.message || "invalid"}.`
1286
1322
  };
1287
1323
  }
1288
- const result = await skill.run(parsed.data, ctx);
1324
+ let result = await skill.run(parsed.data, ctx);
1325
+ if (result.kind === "handoff") {
1326
+ const handoffJobId = result.jobId;
1327
+ const polled = await pollJobUntilDoneOrHandoff(
1328
+ handoffJobId,
1329
+ (id) => ctx.client.getJob(id),
1330
+ ctx.buildStatusUrl,
1331
+ {
1332
+ syncBudgetMs: WORKFLOW_STEP_EXTRA_BUDGET_MS,
1333
+ now: ctx.pollOverrides?.now,
1334
+ sleep: ctx.pollOverrides?.sleep
1335
+ }
1336
+ );
1337
+ result = pollResultToSkillResult(
1338
+ polled,
1339
+ (urls) => urls.length === 0 ? `Job ${handoffJobId} completed but returned no assets.` : `Done. Asset: ${urls[0]}`
1340
+ );
1341
+ }
1289
1342
  const url = firstUrl(result);
1290
1343
  if (url) {
1291
1344
  prevUrl = url;
@@ -1295,6 +1348,11 @@ async function runWorkflow(input, ctx) {
1295
1348
  kind: "text",
1296
1349
  text: `Workflow "${workflow.name}" stopped at step ${i + 1} (${step.skill}): ${result.text}`
1297
1350
  };
1351
+ } else if (result.kind === "handoff") {
1352
+ return {
1353
+ kind: "text",
1354
+ text: `Workflow "${workflow.name}" is still on step ${i + 1} (${step.skill}) after an extended wait. Poll with pixeldojo:status { jobId: "${result.jobId}" }, then re-run the workflow once it completes.`
1355
+ };
1298
1356
  } else {
1299
1357
  outputs.push(`step ${i + 1} (${step.skill}): done (no asset URL)`);
1300
1358
  }
@@ -1305,9 +1363,12 @@ async function runWorkflow(input, ctx) {
1305
1363
  ${outputs.join("\n")}`
1306
1364
  };
1307
1365
  }
1308
- var StepSchema, SaveInputSchema, RunInputSchema, ListInputSchema, stepJson, saveWorkflowSkill, runWorkflowSkill, listWorkflowsSkill;
1366
+ var WORKFLOW_STEP_EXTRA_BUDGET_MS, StepSchema, SaveInputSchema, RunInputSchema, ListInputSchema, stepJson, saveWorkflowSkill, runWorkflowSkill, listWorkflowsSkill;
1309
1367
  var init_workflows = __esm({
1310
1368
  "src/skills/workflows.ts"() {
1369
+ init_poll();
1370
+ init_types();
1371
+ WORKFLOW_STEP_EXTRA_BUDGET_MS = 6e4;
1311
1372
  StepSchema = z.object({
1312
1373
  skill: z.string().regex(/^pixeldojo:(generate|edit|character|storyboard|upscale|audio|upload|from_url)$/, {
1313
1374
  message: "Runnable skills only: generate, edit, character, storyboard, upscale, audio, upload, from_url."
@@ -1492,7 +1553,7 @@ function makeRedactedLogger(target = console) {
1492
1553
  }
1493
1554
 
1494
1555
  // src/server.ts
1495
- var PACKAGE_VERSION = "0.5.1" ;
1556
+ var PACKAGE_VERSION = "0.6.0" ;
1496
1557
  function buildServer(options) {
1497
1558
  if (!options.apiKey) {
1498
1559
  throw new Error(