@pixeldojo/mcp 0.9.0 → 0.9.1
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 +15 -15
- package/dist/cli.cjs +183 -83
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +183 -83
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +170 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +170 -70
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -95,7 +95,7 @@ function pollResultToSkillResult(result, successText) {
|
|
|
95
95
|
}
|
|
96
96
|
return {
|
|
97
97
|
kind: "handoff",
|
|
98
|
-
text: `Job ${result.jobId} is still running after the 30s sync window. Use
|
|
98
|
+
text: `Job ${result.jobId} is still running after the 30s sync window. Use pixeldojo_status with this jobId to check completion, or follow ${result.statusUrl}.`,
|
|
99
99
|
jobId: result.jobId,
|
|
100
100
|
statusUrl: result.statusUrl
|
|
101
101
|
};
|
|
@@ -188,6 +188,7 @@ var init_upload_helpers = __esm({
|
|
|
188
188
|
function getFieldConstraints(apiId, client) {
|
|
189
189
|
const cached = schemaCache.get(apiId);
|
|
190
190
|
if (cached) return cached;
|
|
191
|
+
if (typeof client?.getModelSchema !== "function") return Promise.resolve(null);
|
|
191
192
|
const pending = Promise.resolve().then(() => client.getModelSchema(apiId)).then(
|
|
192
193
|
(res) => res.schema?.properties ?? {}
|
|
193
194
|
).catch(() => {
|
|
@@ -232,6 +233,46 @@ async function snapAspectRatio(apiId, requested, client, opts = {}) {
|
|
|
232
233
|
if (!field.enum || field.enum.length === 0) return requested;
|
|
233
234
|
return nearestAspectFromEnum(requested, field.enum);
|
|
234
235
|
}
|
|
236
|
+
async function resolveReferenceField(apiId, client) {
|
|
237
|
+
const props = await getFieldConstraints(apiId, client);
|
|
238
|
+
if (props === null) return void 0;
|
|
239
|
+
for (const name of REFERENCE_FIELD_PRIORITY) {
|
|
240
|
+
const field = props[name];
|
|
241
|
+
if (field) return { name, isArray: field.type === "array" };
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
function resolutionHeight(value) {
|
|
246
|
+
const v = value.trim().toLowerCase();
|
|
247
|
+
if (v === "4k") return 2160;
|
|
248
|
+
const n = Number.parseInt(v, 10);
|
|
249
|
+
return Number.isFinite(n) ? n : null;
|
|
250
|
+
}
|
|
251
|
+
async function snapResolution(apiId, requested, client, opts = {}) {
|
|
252
|
+
if (!requested) return void 0;
|
|
253
|
+
const props = await getFieldConstraints(apiId, client);
|
|
254
|
+
if (props === null) {
|
|
255
|
+
return opts.onUnknown === "omit" ? void 0 : requested;
|
|
256
|
+
}
|
|
257
|
+
const field = props.resolution;
|
|
258
|
+
if (!field) return void 0;
|
|
259
|
+
if (!field.enum || field.enum.length === 0) return requested;
|
|
260
|
+
if (field.enum.includes(requested)) return requested;
|
|
261
|
+
const target = resolutionHeight(requested);
|
|
262
|
+
if (target === null) return requested;
|
|
263
|
+
let best = requested;
|
|
264
|
+
let bestDiff = Infinity;
|
|
265
|
+
for (const candidate of field.enum) {
|
|
266
|
+
const h = resolutionHeight(candidate);
|
|
267
|
+
if (h === null) continue;
|
|
268
|
+
const diff = Math.abs(h - target);
|
|
269
|
+
if (diff < bestDiff) {
|
|
270
|
+
bestDiff = diff;
|
|
271
|
+
best = candidate;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return best;
|
|
275
|
+
}
|
|
235
276
|
async function snapDuration(apiId, requested, client, opts = {}) {
|
|
236
277
|
if (requested === void 0) return void 0;
|
|
237
278
|
const props = await getFieldConstraints(apiId, client);
|
|
@@ -270,10 +311,22 @@ async function resolveImageRefs(apiId, urls, client) {
|
|
|
270
311
|
}
|
|
271
312
|
return { key, value: urls[0], dropped: urls.length - 1 };
|
|
272
313
|
}
|
|
273
|
-
var schemaCache, REF_KEYS;
|
|
314
|
+
var schemaCache, REFERENCE_FIELD_PRIORITY, REF_KEYS;
|
|
274
315
|
var init_schema_snap = __esm({
|
|
275
316
|
"src/skills/schema-snap.ts"() {
|
|
276
317
|
schemaCache = /* @__PURE__ */ new Map();
|
|
318
|
+
REFERENCE_FIELD_PRIORITY = [
|
|
319
|
+
"image_url",
|
|
320
|
+
"start_image_url",
|
|
321
|
+
"input_image",
|
|
322
|
+
"image",
|
|
323
|
+
"reference_images",
|
|
324
|
+
"reference_urls",
|
|
325
|
+
"reference_image_urls",
|
|
326
|
+
"image_urls",
|
|
327
|
+
"image_input",
|
|
328
|
+
"images"
|
|
329
|
+
];
|
|
277
330
|
REF_KEYS = [
|
|
278
331
|
"image_urls",
|
|
279
332
|
"images",
|
|
@@ -286,7 +339,7 @@ var init_schema_snap = __esm({
|
|
|
286
339
|
];
|
|
287
340
|
}
|
|
288
341
|
});
|
|
289
|
-
function buildPayload(apiId, input) {
|
|
342
|
+
function buildPayload(apiId, input, refField, referenceUrls = []) {
|
|
290
343
|
const body = { prompt: input.prompt };
|
|
291
344
|
if (input.aspect_ratio) body.aspect_ratio = input.aspect_ratio;
|
|
292
345
|
if (input.preset) body.preset = input.preset;
|
|
@@ -304,8 +357,16 @@ function buildPayload(apiId, input) {
|
|
|
304
357
|
}
|
|
305
358
|
break;
|
|
306
359
|
default:
|
|
307
|
-
if (input.image_url)
|
|
360
|
+
if (input.image_url) {
|
|
361
|
+
const refs = referenceUrls.length > 0 ? referenceUrls : [input.image_url];
|
|
362
|
+
if (refField) {
|
|
363
|
+
body[refField.name] = refField.isArray ? refs : refs[0];
|
|
364
|
+
} else {
|
|
365
|
+
body.image_url = refs[0];
|
|
366
|
+
}
|
|
367
|
+
}
|
|
308
368
|
if (input.duration !== void 0) body.duration = input.duration;
|
|
369
|
+
if (input.resolution) body.resolution = input.resolution;
|
|
309
370
|
break;
|
|
310
371
|
}
|
|
311
372
|
return body;
|
|
@@ -313,39 +374,51 @@ function buildPayload(apiId, input) {
|
|
|
313
374
|
async function run(input, ctx) {
|
|
314
375
|
const apiId = input.model ?? DEFAULT_MODEL;
|
|
315
376
|
let resolved = input;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
377
|
+
let referenceUrls = [];
|
|
378
|
+
try {
|
|
379
|
+
const singleUrl = input.image_url ?? (input.image_path ? await uploadLocalFile(input.image_path, ctx.client) : void 0);
|
|
380
|
+
const manyUrls = [...input.image_urls ?? []];
|
|
381
|
+
for (const p of input.image_paths ?? []) {
|
|
382
|
+
manyUrls.push(await uploadLocalFile(p, ctx.client));
|
|
383
|
+
}
|
|
384
|
+
referenceUrls = [...singleUrl ? [singleUrl] : [], ...manyUrls].filter(
|
|
385
|
+
(u, i, all) => all.indexOf(u) === i
|
|
386
|
+
);
|
|
387
|
+
if (referenceUrls[0] && referenceUrls[0] !== input.image_url) {
|
|
388
|
+
resolved = { ...input, image_url: referenceUrls[0] };
|
|
325
389
|
}
|
|
390
|
+
} catch (err) {
|
|
391
|
+
if (err instanceof SkillUploadError) {
|
|
392
|
+
return { kind: "text", text: `pixeldojo_generate (upload): ${err.userMessage}` };
|
|
393
|
+
}
|
|
394
|
+
throw err;
|
|
326
395
|
}
|
|
327
396
|
if (apiId === "grok-video-extend") {
|
|
328
397
|
if (!resolved.image_url) {
|
|
329
398
|
return {
|
|
330
399
|
kind: "text",
|
|
331
|
-
text: '
|
|
400
|
+
text: 'pixeldojo_generate with model="grok-video-extend" requires image_url (the source video URL to extend, or image_path for a local file).'
|
|
332
401
|
};
|
|
333
402
|
}
|
|
334
403
|
if (resolved.duration === void 0) {
|
|
335
404
|
return {
|
|
336
405
|
kind: "text",
|
|
337
|
-
text: '
|
|
406
|
+
text: 'pixeldojo_generate with model="grok-video-extend" requires duration (extension length in seconds).'
|
|
338
407
|
};
|
|
339
408
|
}
|
|
340
409
|
}
|
|
410
|
+
let refField;
|
|
341
411
|
if (!VIDEO_INPUT_MODELS.has(apiId)) {
|
|
342
|
-
const [aspect_ratio, duration] = await Promise.all([
|
|
412
|
+
const [aspect_ratio, duration, resolution, ref] = await Promise.all([
|
|
343
413
|
snapAspectRatio(apiId, resolved.aspect_ratio, ctx.client),
|
|
344
|
-
snapDuration(apiId, resolved.duration, ctx.client)
|
|
414
|
+
snapDuration(apiId, resolved.duration, ctx.client),
|
|
415
|
+
snapResolution(apiId, resolved.resolution, ctx.client),
|
|
416
|
+
resolved.image_url ? resolveReferenceField(apiId, ctx.client) : Promise.resolve(void 0)
|
|
345
417
|
]);
|
|
346
|
-
resolved = { ...resolved, aspect_ratio, duration };
|
|
418
|
+
resolved = { ...resolved, aspect_ratio, duration, resolution };
|
|
419
|
+
refField = ref;
|
|
347
420
|
}
|
|
348
|
-
const body = buildPayload(apiId, resolved);
|
|
421
|
+
const body = buildPayload(apiId, resolved, refField, referenceUrls);
|
|
349
422
|
const submitted = await ctx.client.run(apiId, body);
|
|
350
423
|
const result = await pollJobUntilDoneOrHandoff(
|
|
351
424
|
submitted.jobId,
|
|
@@ -378,9 +451,18 @@ var init_generate = __esm({
|
|
|
378
451
|
),
|
|
379
452
|
aspect_ratio: zod.z.string().optional().describe('Aspect ratio supported by the chosen model (e.g. "1:1", "16:9", "9:16").'),
|
|
380
453
|
duration: zod.z.number().int().positive().optional().describe("Video duration in seconds, when applicable."),
|
|
454
|
+
resolution: zod.z.string().optional().describe(
|
|
455
|
+
'Output resolution when the model supports it (e.g. "480p", "720p", "1080p", "4k"). Lower tiers cost fewer credits. Omit to use the model default.'
|
|
456
|
+
),
|
|
381
457
|
image_url: zod.z.string().url().optional().describe("Reference image URL for image-to-image / image-to-video tools."),
|
|
382
458
|
image_path: zod.z.string().min(1).optional().describe(
|
|
383
459
|
"Absolute local file path to a reference image/video. The skill uploads it to PixelDojo first (24h temp storage) and then dispatches. Prefer this over image_url when the user has the file on disk."
|
|
460
|
+
),
|
|
461
|
+
image_urls: zod.z.array(zod.z.string().url()).optional().describe(
|
|
462
|
+
"Multiple reference image URLs, for models that accept a reference set (character + wardrobe + location, for example). Models that take only one reference use the first and the rest are ignored."
|
|
463
|
+
),
|
|
464
|
+
image_paths: zod.z.array(zod.z.string().min(1)).optional().describe(
|
|
465
|
+
"Multiple absolute local file paths. Each is uploaded to PixelDojo (24h temp storage) and used as a reference, same rules as image_urls."
|
|
384
466
|
)
|
|
385
467
|
});
|
|
386
468
|
DEFAULT_MODEL = "google-nano-banana";
|
|
@@ -399,7 +481,21 @@ var init_generate = __esm({
|
|
|
399
481
|
},
|
|
400
482
|
aspect_ratio: { type: "string" },
|
|
401
483
|
duration: { type: "integer" },
|
|
484
|
+
resolution: {
|
|
485
|
+
type: "string",
|
|
486
|
+
description: 'Output resolution when supported (e.g. "480p", "720p", "1080p", "4k"). Lower tiers cost fewer credits.'
|
|
487
|
+
},
|
|
402
488
|
image_url: { type: "string", format: "uri" },
|
|
489
|
+
image_urls: {
|
|
490
|
+
type: "array",
|
|
491
|
+
items: { type: "string", format: "uri" },
|
|
492
|
+
description: "Multiple reference image URLs for models that accept a reference set. Single-reference models use the first."
|
|
493
|
+
},
|
|
494
|
+
image_paths: {
|
|
495
|
+
type: "array",
|
|
496
|
+
items: { type: "string" },
|
|
497
|
+
description: "Multiple absolute local file paths, each uploaded to 24h temp storage and used as a reference."
|
|
498
|
+
},
|
|
403
499
|
image_path: {
|
|
404
500
|
type: "string",
|
|
405
501
|
description: "Absolute local file path. The skill uploads it to PixelDojo (24h temp storage) and uses the resulting URL as the reference image. Prefer this over image_url when the user has a local file."
|
|
@@ -408,7 +504,7 @@ var init_generate = __esm({
|
|
|
408
504
|
required: ["prompt"]
|
|
409
505
|
};
|
|
410
506
|
generateSkill = {
|
|
411
|
-
name: "
|
|
507
|
+
name: "pixeldojo_generate",
|
|
412
508
|
description: "Generate any image or video from a prompt. Auto-routes to a sensible default model; pass `model` to pick a specific apiId from the catalog. For reference-image / image-to-video flows: pass `image_url` if you already have a URL, or `image_path` (absolute local path) and the skill uploads it to 24h temp storage automatically.",
|
|
413
509
|
inputZod: InputSchema,
|
|
414
510
|
inputJsonSchema: InputJsonSchema,
|
|
@@ -493,14 +589,14 @@ async function run2(input, ctx) {
|
|
|
493
589
|
}
|
|
494
590
|
} catch (err) {
|
|
495
591
|
if (err instanceof SkillUploadError) {
|
|
496
|
-
return { kind: "text", text: `
|
|
592
|
+
return { kind: "text", text: `pixeldojo_edit (upload): ${err.userMessage}` };
|
|
497
593
|
}
|
|
498
594
|
throw err;
|
|
499
595
|
}
|
|
500
596
|
if (!resolved.image_url && (!resolved.image_urls || resolved.image_urls.length === 0)) {
|
|
501
597
|
return {
|
|
502
598
|
kind: "text",
|
|
503
|
-
text: "
|
|
599
|
+
text: "pixeldojo_edit requires at least one reference image. Pass image_url / image_urls (hosted URL) or image_path / image_paths (local file)."
|
|
504
600
|
};
|
|
505
601
|
}
|
|
506
602
|
const apiId = resolved.model ?? DEFAULT_MODEL2;
|
|
@@ -594,7 +690,7 @@ var init_edit = __esm({
|
|
|
594
690
|
required: ["prompt"]
|
|
595
691
|
};
|
|
596
692
|
editSkill = {
|
|
597
|
-
name: "
|
|
693
|
+
name: "pixeldojo_edit",
|
|
598
694
|
description: "Edit an existing image with a text instruction. Defaults to wan-2.7-image-edit; pass `model` to pick another edit-capable apiId. Supports up to 9 reference images for multi-image composition. For reference images: pass image_url(s) for hosted URLs or image_path(s) for absolute local file paths (skill uploads to 24h temp storage automatically).",
|
|
599
695
|
inputZod: InputSchema2,
|
|
600
696
|
inputJsonSchema: InputJsonSchema2,
|
|
@@ -609,14 +705,14 @@ async function run3(input, ctx) {
|
|
|
609
705
|
if (!ALLOWED.includes(requested)) {
|
|
610
706
|
return {
|
|
611
707
|
kind: "text",
|
|
612
|
-
text: `Unknown model "${requested}" for
|
|
708
|
+
text: `Unknown model "${requested}" for pixeldojo_character. Allowed: ${ALLOWED.join(", ")}.`
|
|
613
709
|
};
|
|
614
710
|
}
|
|
615
711
|
const apiId = requested;
|
|
616
712
|
if (REQUIRES_REFERENCE.has(apiId) && !input.reference_image_url) {
|
|
617
713
|
return {
|
|
618
714
|
kind: "text",
|
|
619
|
-
text: `
|
|
715
|
+
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}".`
|
|
620
716
|
};
|
|
621
717
|
}
|
|
622
718
|
const body = { prompt: input.prompt };
|
|
@@ -678,7 +774,7 @@ var init_character = __esm({
|
|
|
678
774
|
required: ["prompt"]
|
|
679
775
|
};
|
|
680
776
|
characterSkill = {
|
|
681
|
-
name: "
|
|
777
|
+
name: "pixeldojo_character",
|
|
682
778
|
description: "Generate or edit consistent characters across shots. Pass a reference_image_url for identity locking (seedream-5 by default).",
|
|
683
779
|
inputZod: InputSchema3,
|
|
684
780
|
inputJsonSchema: InputJsonSchema3,
|
|
@@ -693,7 +789,7 @@ async function run4(input, ctx) {
|
|
|
693
789
|
if (!ALLOWED2.includes(requested)) {
|
|
694
790
|
return {
|
|
695
791
|
kind: "text",
|
|
696
|
-
text: `Unknown model "${requested}" for
|
|
792
|
+
text: `Unknown model "${requested}" for pixeldojo_storyboard. Allowed: ${ALLOWED2.join(", ")}.`
|
|
697
793
|
};
|
|
698
794
|
}
|
|
699
795
|
const apiId = requested;
|
|
@@ -756,7 +852,7 @@ var init_storyboard = __esm({
|
|
|
756
852
|
required: ["brief"]
|
|
757
853
|
};
|
|
758
854
|
storyboardSkill = {
|
|
759
|
-
name: "
|
|
855
|
+
name: "pixeldojo_storyboard",
|
|
760
856
|
description: "Generate a video storyboard preview from a single brief. v1: single-shot preview; multi-shot orchestration coming.",
|
|
761
857
|
inputZod: InputSchema4,
|
|
762
858
|
inputJsonSchema: InputJsonSchema4,
|
|
@@ -771,7 +867,7 @@ async function run5(input, ctx) {
|
|
|
771
867
|
if (!ALLOWED3.includes(requested)) {
|
|
772
868
|
return {
|
|
773
869
|
kind: "text",
|
|
774
|
-
text: `Unknown model "${requested}" for
|
|
870
|
+
text: `Unknown model "${requested}" for pixeldojo_upscale. Allowed: ${ALLOWED3.join(", ")}.`
|
|
775
871
|
};
|
|
776
872
|
}
|
|
777
873
|
const apiId = requested;
|
|
@@ -833,7 +929,7 @@ var init_upscale = __esm({
|
|
|
833
929
|
required: ["image_url"]
|
|
834
930
|
};
|
|
835
931
|
upscaleSkill = {
|
|
836
|
-
name: "
|
|
932
|
+
name: "pixeldojo_upscale",
|
|
837
933
|
description: "Enhance an image. Magnific (default) supports configurable scale_factor and creativity; face-enhance for portraits.",
|
|
838
934
|
inputZod: InputSchema5,
|
|
839
935
|
inputJsonSchema: InputJsonSchema5,
|
|
@@ -911,14 +1007,14 @@ var InputSchema6, InputJsonSchema6, STUCK_HINT_MS, ABANDON_HINT_MS, statusSkill;
|
|
|
911
1007
|
var init_status = __esm({
|
|
912
1008
|
"src/skills/status.ts"() {
|
|
913
1009
|
InputSchema6 = zod.z.object({
|
|
914
|
-
jobId: zod.z.string().min(1).max(200).describe("Job id returned from a prior
|
|
1010
|
+
jobId: zod.z.string().min(1).max(200).describe("Job id returned from a prior pixeldojo_* skill handoff.")
|
|
915
1011
|
});
|
|
916
1012
|
InputJsonSchema6 = {
|
|
917
1013
|
type: "object",
|
|
918
1014
|
properties: {
|
|
919
1015
|
jobId: {
|
|
920
1016
|
type: "string",
|
|
921
|
-
description: "Job id returned from a prior
|
|
1017
|
+
description: "Job id returned from a prior pixeldojo_* skill handoff."
|
|
922
1018
|
}
|
|
923
1019
|
},
|
|
924
1020
|
required: ["jobId"]
|
|
@@ -926,7 +1022,7 @@ var init_status = __esm({
|
|
|
926
1022
|
STUCK_HINT_MS = 5 * 60 * 1e3;
|
|
927
1023
|
ABANDON_HINT_MS = 12 * 60 * 1e3;
|
|
928
1024
|
statusSkill = {
|
|
929
|
-
name: "
|
|
1025
|
+
name: "pixeldojo_status",
|
|
930
1026
|
description: "Check the status of a long-running job after a 30s sync handoff. Returns assets when ready, failure reason if failed, or current state if still running.",
|
|
931
1027
|
inputZod: InputSchema6,
|
|
932
1028
|
inputJsonSchema: InputJsonSchema6,
|
|
@@ -971,8 +1067,8 @@ var init_from_url = __esm({
|
|
|
971
1067
|
required: ["url"]
|
|
972
1068
|
};
|
|
973
1069
|
fromUrlSkill = {
|
|
974
|
-
name: "
|
|
975
|
-
description: "Extract product info (name, description, images) from any product page URL. Use this as the first step before
|
|
1070
|
+
name: "pixeldojo_from_url",
|
|
1071
|
+
description: "Extract product info (name, description, images) from any product page URL. Use this as the first step before pixeldojo_campaign / :ad / :lookbook so the agent has structured product data instead of a free-text description.",
|
|
976
1072
|
inputZod: InputSchema7,
|
|
977
1073
|
inputJsonSchema: InputJsonSchema7,
|
|
978
1074
|
// No model dispatch — this skill calls a server-side scrape, not a generation.
|
|
@@ -1033,7 +1129,7 @@ async function plan(input, ctx) {
|
|
|
1033
1129
|
...sceneLines,
|
|
1034
1130
|
"",
|
|
1035
1131
|
`Nothing has been charged yet. Producing renders every shot (roughly 20-25 credits per shot at default engines, charged as each renders) plus a few credits for the soundtrack.`,
|
|
1036
|
-
`Review or edit at ${ctx.baseUrl}/film-studio/${film.id} \u2014 or call
|
|
1132
|
+
`Review or edit at ${ctx.baseUrl}/film-studio/${film.id} \u2014 or call pixeldojo_film with {"film_id": "${film.id}", "action": "produce"} to start production.`
|
|
1037
1133
|
].join("\n")
|
|
1038
1134
|
};
|
|
1039
1135
|
}
|
|
@@ -1048,7 +1144,7 @@ async function produce(filmId, ctx) {
|
|
|
1048
1144
|
}
|
|
1049
1145
|
return {
|
|
1050
1146
|
kind: "text",
|
|
1051
|
-
text: `Production started on "${data.film.title}": ${fired} frame(s) dispatched (each is a normal frame charge; the server's Frame check reviews every one). Poll
|
|
1147
|
+
text: `Production started on "${data.film.title}": ${fired} frame(s) dispatched (each is a normal frame charge; the server's Frame check reviews every one). Poll pixeldojo_film with {"film_id": "${filmId}", "action": "status"} every ~30 seconds \u2014 each poll advances the pipeline.`
|
|
1052
1148
|
};
|
|
1053
1149
|
}
|
|
1054
1150
|
function buildScorePlanBody(film) {
|
|
@@ -1177,13 +1273,13 @@ async function run8(input, ctx) {
|
|
|
1177
1273
|
var LOOKS, InputSchema8, InputJsonSchema8, shots, count, LOOK_MUSIC, ARC, filmSkill;
|
|
1178
1274
|
var init_film = __esm({
|
|
1179
1275
|
"src/skills/film.ts"() {
|
|
1180
|
-
LOOKS = ["live-action", "anime", "3d-animation", "noir", "horror", "sci-fi", "documentary", "western"];
|
|
1276
|
+
LOOKS = ["live-action", "anime", "3d-animation", "noir", "horror", "sci-fi", "documentary", "western", "romance", "romantic-comedy", "comedy"];
|
|
1181
1277
|
InputSchema8 = zod.z.object({
|
|
1182
1278
|
idea: zod.z.string().max(2e3).optional().describe("One-to-three sentence film idea. The studio writes the screenplay (free). Provide idea OR script for planning."),
|
|
1183
1279
|
script: zod.z.string().max(2e4).optional().describe("A full screenplay to plan instead of an idea (sluglines, action lines, dialogue)."),
|
|
1184
1280
|
look: zod.z.enum(LOOKS).default("live-action").describe("Visual style carried through frames, clips, and soundtrack."),
|
|
1185
1281
|
aspect: zod.z.enum(["16:9", "9:16"]).default("16:9"),
|
|
1186
|
-
length: zod.z.enum(["30s", "60s", "2-3min"]).default("60s").describe("Target length; drives the shot count (~6/12/24 shots)."),
|
|
1282
|
+
length: zod.z.enum(["30s", "60s", "2-3min", "5min"]).default("60s").describe("Target length; drives the shot count (~6/12/24/60 shots)."),
|
|
1187
1283
|
film_id: zod.z.string().max(40).optional().describe("Existing film id (from a previous call) for produce/status."),
|
|
1188
1284
|
action: zod.z.enum(["plan", "produce", "status"]).default("plan").describe("'plan' creates + storyboards (free). 'produce' starts charged generation. 'status' advances production and reports progress; poll it every ~30s until it returns the finished film."),
|
|
1189
1285
|
soundtrack: zod.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."),
|
|
@@ -1196,7 +1292,7 @@ var init_film = __esm({
|
|
|
1196
1292
|
script: { type: "string", description: "A full screenplay to plan instead of an idea." },
|
|
1197
1293
|
look: { type: "string", enum: [...LOOKS], description: "Visual style. Default live-action." },
|
|
1198
1294
|
aspect: { type: "string", enum: ["16:9", "9:16"], description: "Default 16:9." },
|
|
1199
|
-
length: { type: "string", enum: ["30s", "60s", "2-3min"], description: "Target length (~6/12/24 shots). Default 60s." },
|
|
1295
|
+
length: { type: "string", enum: ["30s", "60s", "2-3min", "5min"], description: "Target length (~6/12/24/60 shots). Default 60s." },
|
|
1200
1296
|
film_id: { type: "string", description: "Existing film id for produce/status." },
|
|
1201
1297
|
action: { type: "string", enum: ["plan", "produce", "status"], description: "plan (free) | produce (charged) | status (poll every ~30s)." },
|
|
1202
1298
|
soundtrack: { type: "string", enum: ["scene-synced", "flat", "none"], description: "Default scene-synced." },
|
|
@@ -1213,7 +1309,10 @@ var init_film = __esm({
|
|
|
1213
1309
|
horror: "sparse horror underscore, low cello drones, dissonant string swells, creeping heartbeat pulse, unsettling quiet",
|
|
1214
1310
|
"sci-fi": "atmospheric science-fiction score, analog synth pads, deep sub-bass pulses, shimmering arpeggios, vast and cold",
|
|
1215
1311
|
documentary: "understated documentary score, felt piano, soft string beds, organic percussion textures, reflective and true",
|
|
1216
|
-
western: "cinematic western score, lonely twangy guitar, dusty strings, distant horns, wide-open desert tension"
|
|
1312
|
+
western: "cinematic western score, lonely twangy guitar, dusty strings, distant horns, wide-open desert tension",
|
|
1313
|
+
romance: "tender romantic score, felt piano and warm strings, soft acoustic guitar, intimate and unhurried, gently swelling",
|
|
1314
|
+
"romantic-comedy": "bright romantic-comedy score, plucky ukulele and handclaps, warm brushed drums, sunny and flirtatious",
|
|
1315
|
+
comedy: "playful comedy score, bouncy pizzicato strings, cheeky woodwinds, light percussion hits punctuating the gags"
|
|
1217
1316
|
};
|
|
1218
1317
|
ARC = {
|
|
1219
1318
|
opening: ["establishing the theme", "restrained", "quiet anticipation"],
|
|
@@ -1221,7 +1320,7 @@ var init_film = __esm({
|
|
|
1221
1320
|
finale: ["climactic", "full arrangement", "emotional peak resolving to stillness"]
|
|
1222
1321
|
};
|
|
1223
1322
|
filmSkill = {
|
|
1224
|
-
name: "
|
|
1323
|
+
name: "pixeldojo_film",
|
|
1225
1324
|
description: "Make a finished short film from one idea: screenplay, storyboard frames (AI-reviewed), video clips with spoken dialogue, scene-synced soundtrack, stitched export. Phased: plan is free and returns a cost estimate; produce starts charged generation; status advances production (poll every ~30s).",
|
|
1226
1325
|
inputZod: InputSchema8,
|
|
1227
1326
|
inputJsonSchema: InputJsonSchema8,
|
|
@@ -1360,8 +1459,8 @@ var init_ad = __esm({
|
|
|
1360
1459
|
// Zod refine; no root-level oneOf (Grok rejects non-object root unions).
|
|
1361
1460
|
};
|
|
1362
1461
|
adSkill = {
|
|
1363
|
-
name: "
|
|
1364
|
-
description: "Marketing Studio: turn any product into a video ad. Pass a product URL (auto-extracted) or product images, pick an ad style (UGC testimonial, unboxing, try-on, showcase, TV spot, and more via GET /api/v1/marketing/presets), optionally add presenter reference images, a location, a hook, and the exact spoken script. Renders with voiceover; returns the video or a statusUrl handoff to poll via
|
|
1462
|
+
name: "pixeldojo_ad",
|
|
1463
|
+
description: "Marketing Studio: turn any product into a video ad. Pass a product URL (auto-extracted) or product images, pick an ad style (UGC testimonial, unboxing, try-on, showcase, TV spot, and more via GET /api/v1/marketing/presets), optionally add presenter reference images, a location, a hook, and the exact spoken script. Renders with voiceover; returns the video or a statusUrl handoff to poll via pixeldojo_status. Ideal for automated generate-and-post workflows.",
|
|
1365
1464
|
inputZod: InputSchema9,
|
|
1366
1465
|
inputJsonSchema: InputJsonSchema9,
|
|
1367
1466
|
// Composition skill — the server routes to the ad engine internally.
|
|
@@ -1393,7 +1492,7 @@ async function run10(input, ctx) {
|
|
|
1393
1492
|
}
|
|
1394
1493
|
return {
|
|
1395
1494
|
kind: "handoff",
|
|
1396
|
-
text: result.message ?? `Campaign ${result.campaignId} queued. Poll via
|
|
1495
|
+
text: result.message ?? `Campaign ${result.campaignId} queued. Poll via pixeldojo_campaign_status.`,
|
|
1397
1496
|
jobId: result.campaignId,
|
|
1398
1497
|
statusUrl: result.statusUrl ?? ctx.buildStatusUrl(result.campaignId)
|
|
1399
1498
|
};
|
|
@@ -1446,8 +1545,8 @@ var init_campaign = __esm({
|
|
|
1446
1545
|
oneOf: [{ required: ["productUrl"] }, { required: ["product"] }]
|
|
1447
1546
|
};
|
|
1448
1547
|
campaignSkill = {
|
|
1449
|
-
name: "
|
|
1450
|
-
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
|
|
1548
|
+
name: "pixeldojo_campaign",
|
|
1549
|
+
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.",
|
|
1451
1550
|
inputZod: InputSchema10,
|
|
1452
1551
|
inputJsonSchema: InputJsonSchema10,
|
|
1453
1552
|
// Composition skill — internal sub-jobs route to per-tool models, no
|
|
@@ -1492,21 +1591,21 @@ var InputSchema11, InputJsonSchema11, campaignStatusSkill;
|
|
|
1492
1591
|
var init_campaign_status = __esm({
|
|
1493
1592
|
"src/skills/campaign-status.ts"() {
|
|
1494
1593
|
InputSchema11 = zod.z.object({
|
|
1495
|
-
campaignId: zod.z.string().min(1).max(200).describe("Campaign id returned from a prior
|
|
1594
|
+
campaignId: zod.z.string().min(1).max(200).describe("Campaign id returned from a prior pixeldojo_campaign handoff.")
|
|
1496
1595
|
});
|
|
1497
1596
|
InputJsonSchema11 = {
|
|
1498
1597
|
type: "object",
|
|
1499
1598
|
properties: {
|
|
1500
1599
|
campaignId: {
|
|
1501
1600
|
type: "string",
|
|
1502
|
-
description: "Campaign id returned from a prior
|
|
1601
|
+
description: "Campaign id returned from a prior pixeldojo_campaign handoff."
|
|
1503
1602
|
}
|
|
1504
1603
|
},
|
|
1505
1604
|
required: ["campaignId"]
|
|
1506
1605
|
};
|
|
1507
1606
|
campaignStatusSkill = {
|
|
1508
|
-
name: "
|
|
1509
|
-
description: "Poll a campaign by campaignId returned from
|
|
1607
|
+
name: "pixeldojo_campaign_status",
|
|
1608
|
+
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.",
|
|
1510
1609
|
inputZod: InputSchema11,
|
|
1511
1610
|
inputJsonSchema: InputJsonSchema11,
|
|
1512
1611
|
allowedModels: [],
|
|
@@ -1521,7 +1620,7 @@ async function run12(input, ctx) {
|
|
|
1521
1620
|
url = await uploadLocalFile(input.path, ctx.client);
|
|
1522
1621
|
} catch (err) {
|
|
1523
1622
|
if (err instanceof SkillUploadError) {
|
|
1524
|
-
return { kind: "text", text: `
|
|
1623
|
+
return { kind: "text", text: `pixeldojo_upload: ${err.userMessage}` };
|
|
1525
1624
|
}
|
|
1526
1625
|
throw err;
|
|
1527
1626
|
}
|
|
@@ -1551,7 +1650,7 @@ var init_upload = __esm({
|
|
|
1551
1650
|
required: ["path"]
|
|
1552
1651
|
};
|
|
1553
1652
|
uploadSkill = {
|
|
1554
|
-
name: "
|
|
1653
|
+
name: "pixeldojo_upload",
|
|
1555
1654
|
description: "Upload a local image or video file and get back a public URL you can pass to :generate or :edit as image_url. Use when the user has a reference image on disk (drag-dropped into Claude Code, screenshot, etc.) and you need a URL. Storage auto-expires after 24 hours.",
|
|
1556
1655
|
inputZod: InputSchema12,
|
|
1557
1656
|
inputJsonSchema: InputJsonSchema12,
|
|
@@ -1566,7 +1665,7 @@ async function run13(input, ctx) {
|
|
|
1566
1665
|
if (!ALLOWED4.includes(requested)) {
|
|
1567
1666
|
return {
|
|
1568
1667
|
kind: "text",
|
|
1569
|
-
text: `Unknown model "${requested}" for
|
|
1668
|
+
text: `Unknown model "${requested}" for pixeldojo_audio. Allowed: ${ALLOWED4.join(", ")}.`
|
|
1570
1669
|
};
|
|
1571
1670
|
}
|
|
1572
1671
|
const apiId = requested;
|
|
@@ -1577,7 +1676,7 @@ async function run13(input, ctx) {
|
|
|
1577
1676
|
audioUrls = [...audioUrls, ...uploaded].slice(0, 3);
|
|
1578
1677
|
} catch (err) {
|
|
1579
1678
|
if (err instanceof SkillUploadError) {
|
|
1580
|
-
return { kind: "text", text: `
|
|
1679
|
+
return { kind: "text", text: `pixeldojo_audio (upload): ${err.userMessage}` };
|
|
1581
1680
|
}
|
|
1582
1681
|
throw err;
|
|
1583
1682
|
}
|
|
@@ -1643,7 +1742,7 @@ var init_audio = __esm({
|
|
|
1643
1742
|
required: ["prompt"]
|
|
1644
1743
|
};
|
|
1645
1744
|
audioSkill = {
|
|
1646
|
-
name: "
|
|
1745
|
+
name: "pixeldojo_audio",
|
|
1647
1746
|
description: "Generate audio: speech, multi-character dialogue, sound effects, and background music from one prompt (Seed Audio 1.0 by default; plain TTS available).",
|
|
1648
1747
|
inputZod: InputSchema13,
|
|
1649
1748
|
inputJsonSchema: InputJsonSchema13,
|
|
@@ -1663,7 +1762,7 @@ async function run14(input, ctx) {
|
|
|
1663
1762
|
if (res.items.length === 0) {
|
|
1664
1763
|
return {
|
|
1665
1764
|
kind: "text",
|
|
1666
|
-
text: input.q ? `No library items match "${input.q}". Try a broader query, or omit q to list recent generations.` : "Your library is empty. Generate something with
|
|
1765
|
+
text: input.q ? `No library items match "${input.q}". Try a broader query, or omit q to list recent generations.` : "Your library is empty. Generate something with pixeldojo_generate first."
|
|
1667
1766
|
};
|
|
1668
1767
|
}
|
|
1669
1768
|
const lines = res.items.map((it) => {
|
|
@@ -1704,8 +1803,8 @@ var init_library = __esm({
|
|
|
1704
1803
|
required: []
|
|
1705
1804
|
};
|
|
1706
1805
|
librarySkill = {
|
|
1707
|
-
name: "
|
|
1708
|
-
description: "Search your media library: saved My Media items (permanent) and recent API/MCP generations (expire after 24h), newest first. Returns asset URLs to reuse as reference inputs. Keep a result forever with
|
|
1806
|
+
name: "pixeldojo_library",
|
|
1807
|
+
description: "Search your media library: saved My Media items (permanent) and recent API/MCP generations (expire after 24h), newest first. Returns asset URLs to reuse as reference inputs. Keep a result forever with pixeldojo_save.",
|
|
1709
1808
|
inputZod: InputSchema14,
|
|
1710
1809
|
inputJsonSchema: InputJsonSchema14,
|
|
1711
1810
|
allowedModels: [],
|
|
@@ -1725,7 +1824,7 @@ async function run15(input, ctx) {
|
|
|
1725
1824
|
return {
|
|
1726
1825
|
kind: "text",
|
|
1727
1826
|
text: `Saved to My Media (permanent): ${res.url}
|
|
1728
|
-
It will appear at https://pixeldojo.ai/my-images and in
|
|
1827
|
+
It will appear at https://pixeldojo.ai/my-images and in pixeldojo_library searches.`
|
|
1729
1828
|
};
|
|
1730
1829
|
}
|
|
1731
1830
|
var InputSchema15, InputJsonSchema15, saveSkill;
|
|
@@ -1750,7 +1849,7 @@ var init_save = __esm({
|
|
|
1750
1849
|
required: ["url"]
|
|
1751
1850
|
};
|
|
1752
1851
|
saveSkill = {
|
|
1753
|
-
name: "
|
|
1852
|
+
name: "pixeldojo_save",
|
|
1754
1853
|
description: "Save a generated image or video into the user's My Media library permanently. Generation outputs otherwise expire after 24 hours \u2014 call this when the user wants to keep a result.",
|
|
1755
1854
|
inputZod: InputSchema15,
|
|
1756
1855
|
inputJsonSchema: InputJsonSchema15,
|
|
@@ -1798,7 +1897,7 @@ async function runSave(input, ctx) {
|
|
|
1798
1897
|
});
|
|
1799
1898
|
return {
|
|
1800
1899
|
kind: "text",
|
|
1801
|
-
text: res.created ? `Saved workflow "${res.name}" (${res.steps} step${res.steps === 1 ? "" : "s"}). Run it with
|
|
1900
|
+
text: res.created ? `Saved workflow "${res.name}" (${res.steps} step${res.steps === 1 ? "" : "s"}). Run it with pixeldojo_run_workflow { name: "${res.name}" }.` : `Overwrote workflow "${res.name}" (${res.steps} step${res.steps === 1 ? "" : "s"}).`
|
|
1802
1901
|
};
|
|
1803
1902
|
}
|
|
1804
1903
|
async function runList(_input, ctx) {
|
|
@@ -1806,7 +1905,7 @@ async function runList(_input, ctx) {
|
|
|
1806
1905
|
if (res.count === 0) {
|
|
1807
1906
|
return {
|
|
1808
1907
|
kind: "text",
|
|
1809
|
-
text: "No saved workflows yet. Save one with
|
|
1908
|
+
text: "No saved workflows yet. Save one with pixeldojo_save_workflow { name, steps: [{ skill, args }] }."
|
|
1810
1909
|
};
|
|
1811
1910
|
}
|
|
1812
1911
|
const lines = res.workflows.map(
|
|
@@ -1875,7 +1974,7 @@ async function runWorkflow(input, ctx) {
|
|
|
1875
1974
|
} else if (result.kind === "handoff") {
|
|
1876
1975
|
return {
|
|
1877
1976
|
kind: "text",
|
|
1878
|
-
text: `Workflow "${workflow.name}" is still on step ${i + 1} (${step.skill}) after an extended wait. Poll with
|
|
1977
|
+
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.`
|
|
1879
1978
|
};
|
|
1880
1979
|
} else {
|
|
1881
1980
|
outputs.push(`step ${i + 1} (${step.skill}): done (no asset URL)`);
|
|
@@ -1894,7 +1993,7 @@ var init_workflows = __esm({
|
|
|
1894
1993
|
init_types();
|
|
1895
1994
|
WORKFLOW_STEP_EXTRA_BUDGET_MS = 6e4;
|
|
1896
1995
|
StepSchema = zod.z.object({
|
|
1897
|
-
skill: zod.z.string().regex(/^pixeldojo:(generate|edit|character|storyboard|upscale|audio|upload|from_url)$/, {
|
|
1996
|
+
skill: zod.z.string().regex(/^pixeldojo[:_](generate|edit|character|storyboard|upscale|audio|upload|from_url)$/, {
|
|
1898
1997
|
message: "Runnable skills only: generate, edit, character, storyboard, upscale, audio, upload, from_url."
|
|
1899
1998
|
}),
|
|
1900
1999
|
args: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
@@ -1920,7 +2019,7 @@ var init_workflows = __esm({
|
|
|
1920
2019
|
required: ["skill"]
|
|
1921
2020
|
};
|
|
1922
2021
|
saveWorkflowSkill = {
|
|
1923
|
-
name: "
|
|
2022
|
+
name: "pixeldojo_save_workflow",
|
|
1924
2023
|
description: "Save a named multi-step skill chain (generate \u2192 edit \u2192 upscale \u2026). String args may contain {{input.<key>}} and {{prev}} placeholders. Saving to an existing name overwrites it.",
|
|
1925
2024
|
inputZod: SaveInputSchema,
|
|
1926
2025
|
inputJsonSchema: {
|
|
@@ -1937,7 +2036,7 @@ var init_workflows = __esm({
|
|
|
1937
2036
|
run: runSave
|
|
1938
2037
|
};
|
|
1939
2038
|
runWorkflowSkill = {
|
|
1940
|
-
name: "
|
|
2039
|
+
name: "pixeldojo_run_workflow",
|
|
1941
2040
|
description: "Run a saved workflow by name. Pass inputs for its {{input.<key>}} placeholders; each step feeds the next via {{prev}}.",
|
|
1942
2041
|
inputZod: RunInputSchema,
|
|
1943
2042
|
inputJsonSchema: {
|
|
@@ -1953,7 +2052,7 @@ var init_workflows = __esm({
|
|
|
1953
2052
|
run: runWorkflow
|
|
1954
2053
|
};
|
|
1955
2054
|
listWorkflowsSkill = {
|
|
1956
|
-
name: "
|
|
2055
|
+
name: "pixeldojo_list_workflows",
|
|
1957
2056
|
description: "List your saved workflows with step counts and run totals.",
|
|
1958
2057
|
inputZod: ListInputSchema,
|
|
1959
2058
|
inputJsonSchema: { type: "object", properties: {}, required: [] },
|
|
@@ -1989,7 +2088,8 @@ __export(skills_exports, {
|
|
|
1989
2088
|
upscaleSkill: () => upscaleSkill
|
|
1990
2089
|
});
|
|
1991
2090
|
function findSkill(name) {
|
|
1992
|
-
|
|
2091
|
+
const wanted = name.replace(/^pixeldojo:/, "pixeldojo_");
|
|
2092
|
+
return ALL_SKILLS.find((s) => s.name === wanted);
|
|
1993
2093
|
}
|
|
1994
2094
|
var ALL_SKILLS;
|
|
1995
2095
|
var init_skills = __esm({
|
|
@@ -2083,7 +2183,7 @@ function makeRedactedLogger(target = console) {
|
|
|
2083
2183
|
}
|
|
2084
2184
|
|
|
2085
2185
|
// src/server.ts
|
|
2086
|
-
var PACKAGE_VERSION = "0.9.
|
|
2186
|
+
var PACKAGE_VERSION = "0.9.1" ;
|
|
2087
2187
|
function buildServer(options) {
|
|
2088
2188
|
if (!options.apiKey) {
|
|
2089
2189
|
throw new Error(
|
|
@@ -2250,19 +2350,19 @@ function printInit() {
|
|
|
2250
2350
|
`# Same shape as above.`,
|
|
2251
2351
|
``,
|
|
2252
2352
|
`# 3. Restart your agent. You'll see these tools:`,
|
|
2253
|
-
`#
|
|
2254
|
-
`#
|
|
2255
|
-
`#
|
|
2256
|
-
`#
|
|
2257
|
-
`#
|
|
2258
|
-
`#
|
|
2259
|
-
`#
|
|
2260
|
-
`#
|
|
2261
|
-
`#
|
|
2262
|
-
`#
|
|
2263
|
-
`#
|
|
2264
|
-
`#
|
|
2265
|
-
`#
|
|
2353
|
+
`# pixeldojo_generate Any prompt \u2192 image or video.`,
|
|
2354
|
+
`# pixeldojo_edit Change an image with a text instruction.`,
|
|
2355
|
+
`# pixeldojo_character Consistent characters across shots.`,
|
|
2356
|
+
`# pixeldojo_storyboard Multi-shot scenes from one brief.`,
|
|
2357
|
+
`# pixeldojo_upscale Enhance any image.`,
|
|
2358
|
+
`# pixeldojo_audio Voices + effects + music in one clip.`,
|
|
2359
|
+
`# pixeldojo_library Search your saved media + generations.`,
|
|
2360
|
+
`# pixeldojo_save Keep a result in My Media permanently.`,
|
|
2361
|
+
`# pixeldojo_*_workflow Save / run / list multi-step chains.`,
|
|
2362
|
+
`# pixeldojo_upload Local file \u2192 temp URL for references.`,
|
|
2363
|
+
`# pixeldojo_from_url Product URL \u2192 structured product info.`,
|
|
2364
|
+
`# pixeldojo_campaign* Product URL \u2192 full launch pack.`,
|
|
2365
|
+
`# pixeldojo_status Poll long-running jobs.`,
|
|
2266
2366
|
``
|
|
2267
2367
|
].join("\n")
|
|
2268
2368
|
);
|