@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.js
CHANGED
|
@@ -93,7 +93,7 @@ function pollResultToSkillResult(result, successText) {
|
|
|
93
93
|
}
|
|
94
94
|
return {
|
|
95
95
|
kind: "handoff",
|
|
96
|
-
text: `Job ${result.jobId} is still running after the 30s sync window. Use
|
|
96
|
+
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}.`,
|
|
97
97
|
jobId: result.jobId,
|
|
98
98
|
statusUrl: result.statusUrl
|
|
99
99
|
};
|
|
@@ -186,6 +186,7 @@ var init_upload_helpers = __esm({
|
|
|
186
186
|
function getFieldConstraints(apiId, client) {
|
|
187
187
|
const cached = schemaCache.get(apiId);
|
|
188
188
|
if (cached) return cached;
|
|
189
|
+
if (typeof client?.getModelSchema !== "function") return Promise.resolve(null);
|
|
189
190
|
const pending = Promise.resolve().then(() => client.getModelSchema(apiId)).then(
|
|
190
191
|
(res) => res.schema?.properties ?? {}
|
|
191
192
|
).catch(() => {
|
|
@@ -230,6 +231,46 @@ async function snapAspectRatio(apiId, requested, client, opts = {}) {
|
|
|
230
231
|
if (!field.enum || field.enum.length === 0) return requested;
|
|
231
232
|
return nearestAspectFromEnum(requested, field.enum);
|
|
232
233
|
}
|
|
234
|
+
async function resolveReferenceField(apiId, client) {
|
|
235
|
+
const props = await getFieldConstraints(apiId, client);
|
|
236
|
+
if (props === null) return void 0;
|
|
237
|
+
for (const name of REFERENCE_FIELD_PRIORITY) {
|
|
238
|
+
const field = props[name];
|
|
239
|
+
if (field) return { name, isArray: field.type === "array" };
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
function resolutionHeight(value) {
|
|
244
|
+
const v = value.trim().toLowerCase();
|
|
245
|
+
if (v === "4k") return 2160;
|
|
246
|
+
const n = Number.parseInt(v, 10);
|
|
247
|
+
return Number.isFinite(n) ? n : null;
|
|
248
|
+
}
|
|
249
|
+
async function snapResolution(apiId, requested, client, opts = {}) {
|
|
250
|
+
if (!requested) return void 0;
|
|
251
|
+
const props = await getFieldConstraints(apiId, client);
|
|
252
|
+
if (props === null) {
|
|
253
|
+
return opts.onUnknown === "omit" ? void 0 : requested;
|
|
254
|
+
}
|
|
255
|
+
const field = props.resolution;
|
|
256
|
+
if (!field) return void 0;
|
|
257
|
+
if (!field.enum || field.enum.length === 0) return requested;
|
|
258
|
+
if (field.enum.includes(requested)) return requested;
|
|
259
|
+
const target = resolutionHeight(requested);
|
|
260
|
+
if (target === null) return requested;
|
|
261
|
+
let best = requested;
|
|
262
|
+
let bestDiff = Infinity;
|
|
263
|
+
for (const candidate of field.enum) {
|
|
264
|
+
const h = resolutionHeight(candidate);
|
|
265
|
+
if (h === null) continue;
|
|
266
|
+
const diff = Math.abs(h - target);
|
|
267
|
+
if (diff < bestDiff) {
|
|
268
|
+
bestDiff = diff;
|
|
269
|
+
best = candidate;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return best;
|
|
273
|
+
}
|
|
233
274
|
async function snapDuration(apiId, requested, client, opts = {}) {
|
|
234
275
|
if (requested === void 0) return void 0;
|
|
235
276
|
const props = await getFieldConstraints(apiId, client);
|
|
@@ -268,10 +309,22 @@ async function resolveImageRefs(apiId, urls, client) {
|
|
|
268
309
|
}
|
|
269
310
|
return { key, value: urls[0], dropped: urls.length - 1 };
|
|
270
311
|
}
|
|
271
|
-
var schemaCache, REF_KEYS;
|
|
312
|
+
var schemaCache, REFERENCE_FIELD_PRIORITY, REF_KEYS;
|
|
272
313
|
var init_schema_snap = __esm({
|
|
273
314
|
"src/skills/schema-snap.ts"() {
|
|
274
315
|
schemaCache = /* @__PURE__ */ new Map();
|
|
316
|
+
REFERENCE_FIELD_PRIORITY = [
|
|
317
|
+
"image_url",
|
|
318
|
+
"start_image_url",
|
|
319
|
+
"input_image",
|
|
320
|
+
"image",
|
|
321
|
+
"reference_images",
|
|
322
|
+
"reference_urls",
|
|
323
|
+
"reference_image_urls",
|
|
324
|
+
"image_urls",
|
|
325
|
+
"image_input",
|
|
326
|
+
"images"
|
|
327
|
+
];
|
|
275
328
|
REF_KEYS = [
|
|
276
329
|
"image_urls",
|
|
277
330
|
"images",
|
|
@@ -284,7 +337,7 @@ var init_schema_snap = __esm({
|
|
|
284
337
|
];
|
|
285
338
|
}
|
|
286
339
|
});
|
|
287
|
-
function buildPayload(apiId, input) {
|
|
340
|
+
function buildPayload(apiId, input, refField, referenceUrls = []) {
|
|
288
341
|
const body = { prompt: input.prompt };
|
|
289
342
|
if (input.aspect_ratio) body.aspect_ratio = input.aspect_ratio;
|
|
290
343
|
if (input.preset) body.preset = input.preset;
|
|
@@ -302,8 +355,16 @@ function buildPayload(apiId, input) {
|
|
|
302
355
|
}
|
|
303
356
|
break;
|
|
304
357
|
default:
|
|
305
|
-
if (input.image_url)
|
|
358
|
+
if (input.image_url) {
|
|
359
|
+
const refs = referenceUrls.length > 0 ? referenceUrls : [input.image_url];
|
|
360
|
+
if (refField) {
|
|
361
|
+
body[refField.name] = refField.isArray ? refs : refs[0];
|
|
362
|
+
} else {
|
|
363
|
+
body.image_url = refs[0];
|
|
364
|
+
}
|
|
365
|
+
}
|
|
306
366
|
if (input.duration !== void 0) body.duration = input.duration;
|
|
367
|
+
if (input.resolution) body.resolution = input.resolution;
|
|
307
368
|
break;
|
|
308
369
|
}
|
|
309
370
|
return body;
|
|
@@ -311,39 +372,51 @@ function buildPayload(apiId, input) {
|
|
|
311
372
|
async function run(input, ctx) {
|
|
312
373
|
const apiId = input.model ?? DEFAULT_MODEL;
|
|
313
374
|
let resolved = input;
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
375
|
+
let referenceUrls = [];
|
|
376
|
+
try {
|
|
377
|
+
const singleUrl = input.image_url ?? (input.image_path ? await uploadLocalFile(input.image_path, ctx.client) : void 0);
|
|
378
|
+
const manyUrls = [...input.image_urls ?? []];
|
|
379
|
+
for (const p of input.image_paths ?? []) {
|
|
380
|
+
manyUrls.push(await uploadLocalFile(p, ctx.client));
|
|
381
|
+
}
|
|
382
|
+
referenceUrls = [...singleUrl ? [singleUrl] : [], ...manyUrls].filter(
|
|
383
|
+
(u, i, all) => all.indexOf(u) === i
|
|
384
|
+
);
|
|
385
|
+
if (referenceUrls[0] && referenceUrls[0] !== input.image_url) {
|
|
386
|
+
resolved = { ...input, image_url: referenceUrls[0] };
|
|
323
387
|
}
|
|
388
|
+
} catch (err) {
|
|
389
|
+
if (err instanceof SkillUploadError) {
|
|
390
|
+
return { kind: "text", text: `pixeldojo_generate (upload): ${err.userMessage}` };
|
|
391
|
+
}
|
|
392
|
+
throw err;
|
|
324
393
|
}
|
|
325
394
|
if (apiId === "grok-video-extend") {
|
|
326
395
|
if (!resolved.image_url) {
|
|
327
396
|
return {
|
|
328
397
|
kind: "text",
|
|
329
|
-
text: '
|
|
398
|
+
text: 'pixeldojo_generate with model="grok-video-extend" requires image_url (the source video URL to extend, or image_path for a local file).'
|
|
330
399
|
};
|
|
331
400
|
}
|
|
332
401
|
if (resolved.duration === void 0) {
|
|
333
402
|
return {
|
|
334
403
|
kind: "text",
|
|
335
|
-
text: '
|
|
404
|
+
text: 'pixeldojo_generate with model="grok-video-extend" requires duration (extension length in seconds).'
|
|
336
405
|
};
|
|
337
406
|
}
|
|
338
407
|
}
|
|
408
|
+
let refField;
|
|
339
409
|
if (!VIDEO_INPUT_MODELS.has(apiId)) {
|
|
340
|
-
const [aspect_ratio, duration] = await Promise.all([
|
|
410
|
+
const [aspect_ratio, duration, resolution, ref] = await Promise.all([
|
|
341
411
|
snapAspectRatio(apiId, resolved.aspect_ratio, ctx.client),
|
|
342
|
-
snapDuration(apiId, resolved.duration, ctx.client)
|
|
412
|
+
snapDuration(apiId, resolved.duration, ctx.client),
|
|
413
|
+
snapResolution(apiId, resolved.resolution, ctx.client),
|
|
414
|
+
resolved.image_url ? resolveReferenceField(apiId, ctx.client) : Promise.resolve(void 0)
|
|
343
415
|
]);
|
|
344
|
-
resolved = { ...resolved, aspect_ratio, duration };
|
|
416
|
+
resolved = { ...resolved, aspect_ratio, duration, resolution };
|
|
417
|
+
refField = ref;
|
|
345
418
|
}
|
|
346
|
-
const body = buildPayload(apiId, resolved);
|
|
419
|
+
const body = buildPayload(apiId, resolved, refField, referenceUrls);
|
|
347
420
|
const submitted = await ctx.client.run(apiId, body);
|
|
348
421
|
const result = await pollJobUntilDoneOrHandoff(
|
|
349
422
|
submitted.jobId,
|
|
@@ -376,9 +449,18 @@ var init_generate = __esm({
|
|
|
376
449
|
),
|
|
377
450
|
aspect_ratio: z.string().optional().describe('Aspect ratio supported by the chosen model (e.g. "1:1", "16:9", "9:16").'),
|
|
378
451
|
duration: z.number().int().positive().optional().describe("Video duration in seconds, when applicable."),
|
|
452
|
+
resolution: z.string().optional().describe(
|
|
453
|
+
'Output resolution when the model supports it (e.g. "480p", "720p", "1080p", "4k"). Lower tiers cost fewer credits. Omit to use the model default.'
|
|
454
|
+
),
|
|
379
455
|
image_url: z.string().url().optional().describe("Reference image URL for image-to-image / image-to-video tools."),
|
|
380
456
|
image_path: z.string().min(1).optional().describe(
|
|
381
457
|
"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."
|
|
458
|
+
),
|
|
459
|
+
image_urls: z.array(z.string().url()).optional().describe(
|
|
460
|
+
"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."
|
|
461
|
+
),
|
|
462
|
+
image_paths: z.array(z.string().min(1)).optional().describe(
|
|
463
|
+
"Multiple absolute local file paths. Each is uploaded to PixelDojo (24h temp storage) and used as a reference, same rules as image_urls."
|
|
382
464
|
)
|
|
383
465
|
});
|
|
384
466
|
DEFAULT_MODEL = "google-nano-banana";
|
|
@@ -397,7 +479,21 @@ var init_generate = __esm({
|
|
|
397
479
|
},
|
|
398
480
|
aspect_ratio: { type: "string" },
|
|
399
481
|
duration: { type: "integer" },
|
|
482
|
+
resolution: {
|
|
483
|
+
type: "string",
|
|
484
|
+
description: 'Output resolution when supported (e.g. "480p", "720p", "1080p", "4k"). Lower tiers cost fewer credits.'
|
|
485
|
+
},
|
|
400
486
|
image_url: { type: "string", format: "uri" },
|
|
487
|
+
image_urls: {
|
|
488
|
+
type: "array",
|
|
489
|
+
items: { type: "string", format: "uri" },
|
|
490
|
+
description: "Multiple reference image URLs for models that accept a reference set. Single-reference models use the first."
|
|
491
|
+
},
|
|
492
|
+
image_paths: {
|
|
493
|
+
type: "array",
|
|
494
|
+
items: { type: "string" },
|
|
495
|
+
description: "Multiple absolute local file paths, each uploaded to 24h temp storage and used as a reference."
|
|
496
|
+
},
|
|
401
497
|
image_path: {
|
|
402
498
|
type: "string",
|
|
403
499
|
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."
|
|
@@ -406,7 +502,7 @@ var init_generate = __esm({
|
|
|
406
502
|
required: ["prompt"]
|
|
407
503
|
};
|
|
408
504
|
generateSkill = {
|
|
409
|
-
name: "
|
|
505
|
+
name: "pixeldojo_generate",
|
|
410
506
|
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.",
|
|
411
507
|
inputZod: InputSchema,
|
|
412
508
|
inputJsonSchema: InputJsonSchema,
|
|
@@ -491,14 +587,14 @@ async function run2(input, ctx) {
|
|
|
491
587
|
}
|
|
492
588
|
} catch (err) {
|
|
493
589
|
if (err instanceof SkillUploadError) {
|
|
494
|
-
return { kind: "text", text: `
|
|
590
|
+
return { kind: "text", text: `pixeldojo_edit (upload): ${err.userMessage}` };
|
|
495
591
|
}
|
|
496
592
|
throw err;
|
|
497
593
|
}
|
|
498
594
|
if (!resolved.image_url && (!resolved.image_urls || resolved.image_urls.length === 0)) {
|
|
499
595
|
return {
|
|
500
596
|
kind: "text",
|
|
501
|
-
text: "
|
|
597
|
+
text: "pixeldojo_edit requires at least one reference image. Pass image_url / image_urls (hosted URL) or image_path / image_paths (local file)."
|
|
502
598
|
};
|
|
503
599
|
}
|
|
504
600
|
const apiId = resolved.model ?? DEFAULT_MODEL2;
|
|
@@ -592,7 +688,7 @@ var init_edit = __esm({
|
|
|
592
688
|
required: ["prompt"]
|
|
593
689
|
};
|
|
594
690
|
editSkill = {
|
|
595
|
-
name: "
|
|
691
|
+
name: "pixeldojo_edit",
|
|
596
692
|
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).",
|
|
597
693
|
inputZod: InputSchema2,
|
|
598
694
|
inputJsonSchema: InputJsonSchema2,
|
|
@@ -607,14 +703,14 @@ async function run3(input, ctx) {
|
|
|
607
703
|
if (!ALLOWED.includes(requested)) {
|
|
608
704
|
return {
|
|
609
705
|
kind: "text",
|
|
610
|
-
text: `Unknown model "${requested}" for
|
|
706
|
+
text: `Unknown model "${requested}" for pixeldojo_character. Allowed: ${ALLOWED.join(", ")}.`
|
|
611
707
|
};
|
|
612
708
|
}
|
|
613
709
|
const apiId = requested;
|
|
614
710
|
if (REQUIRES_REFERENCE.has(apiId) && !input.reference_image_url) {
|
|
615
711
|
return {
|
|
616
712
|
kind: "text",
|
|
617
|
-
text: `
|
|
713
|
+
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}".`
|
|
618
714
|
};
|
|
619
715
|
}
|
|
620
716
|
const body = { prompt: input.prompt };
|
|
@@ -676,7 +772,7 @@ var init_character = __esm({
|
|
|
676
772
|
required: ["prompt"]
|
|
677
773
|
};
|
|
678
774
|
characterSkill = {
|
|
679
|
-
name: "
|
|
775
|
+
name: "pixeldojo_character",
|
|
680
776
|
description: "Generate or edit consistent characters across shots. Pass a reference_image_url for identity locking (seedream-5 by default).",
|
|
681
777
|
inputZod: InputSchema3,
|
|
682
778
|
inputJsonSchema: InputJsonSchema3,
|
|
@@ -691,7 +787,7 @@ async function run4(input, ctx) {
|
|
|
691
787
|
if (!ALLOWED2.includes(requested)) {
|
|
692
788
|
return {
|
|
693
789
|
kind: "text",
|
|
694
|
-
text: `Unknown model "${requested}" for
|
|
790
|
+
text: `Unknown model "${requested}" for pixeldojo_storyboard. Allowed: ${ALLOWED2.join(", ")}.`
|
|
695
791
|
};
|
|
696
792
|
}
|
|
697
793
|
const apiId = requested;
|
|
@@ -754,7 +850,7 @@ var init_storyboard = __esm({
|
|
|
754
850
|
required: ["brief"]
|
|
755
851
|
};
|
|
756
852
|
storyboardSkill = {
|
|
757
|
-
name: "
|
|
853
|
+
name: "pixeldojo_storyboard",
|
|
758
854
|
description: "Generate a video storyboard preview from a single brief. v1: single-shot preview; multi-shot orchestration coming.",
|
|
759
855
|
inputZod: InputSchema4,
|
|
760
856
|
inputJsonSchema: InputJsonSchema4,
|
|
@@ -769,7 +865,7 @@ async function run5(input, ctx) {
|
|
|
769
865
|
if (!ALLOWED3.includes(requested)) {
|
|
770
866
|
return {
|
|
771
867
|
kind: "text",
|
|
772
|
-
text: `Unknown model "${requested}" for
|
|
868
|
+
text: `Unknown model "${requested}" for pixeldojo_upscale. Allowed: ${ALLOWED3.join(", ")}.`
|
|
773
869
|
};
|
|
774
870
|
}
|
|
775
871
|
const apiId = requested;
|
|
@@ -831,7 +927,7 @@ var init_upscale = __esm({
|
|
|
831
927
|
required: ["image_url"]
|
|
832
928
|
};
|
|
833
929
|
upscaleSkill = {
|
|
834
|
-
name: "
|
|
930
|
+
name: "pixeldojo_upscale",
|
|
835
931
|
description: "Enhance an image. Magnific (default) supports configurable scale_factor and creativity; face-enhance for portraits.",
|
|
836
932
|
inputZod: InputSchema5,
|
|
837
933
|
inputJsonSchema: InputJsonSchema5,
|
|
@@ -909,14 +1005,14 @@ var InputSchema6, InputJsonSchema6, STUCK_HINT_MS, ABANDON_HINT_MS, statusSkill;
|
|
|
909
1005
|
var init_status = __esm({
|
|
910
1006
|
"src/skills/status.ts"() {
|
|
911
1007
|
InputSchema6 = z.object({
|
|
912
|
-
jobId: z.string().min(1).max(200).describe("Job id returned from a prior
|
|
1008
|
+
jobId: z.string().min(1).max(200).describe("Job id returned from a prior pixeldojo_* skill handoff.")
|
|
913
1009
|
});
|
|
914
1010
|
InputJsonSchema6 = {
|
|
915
1011
|
type: "object",
|
|
916
1012
|
properties: {
|
|
917
1013
|
jobId: {
|
|
918
1014
|
type: "string",
|
|
919
|
-
description: "Job id returned from a prior
|
|
1015
|
+
description: "Job id returned from a prior pixeldojo_* skill handoff."
|
|
920
1016
|
}
|
|
921
1017
|
},
|
|
922
1018
|
required: ["jobId"]
|
|
@@ -924,7 +1020,7 @@ var init_status = __esm({
|
|
|
924
1020
|
STUCK_HINT_MS = 5 * 60 * 1e3;
|
|
925
1021
|
ABANDON_HINT_MS = 12 * 60 * 1e3;
|
|
926
1022
|
statusSkill = {
|
|
927
|
-
name: "
|
|
1023
|
+
name: "pixeldojo_status",
|
|
928
1024
|
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.",
|
|
929
1025
|
inputZod: InputSchema6,
|
|
930
1026
|
inputJsonSchema: InputJsonSchema6,
|
|
@@ -969,8 +1065,8 @@ var init_from_url = __esm({
|
|
|
969
1065
|
required: ["url"]
|
|
970
1066
|
};
|
|
971
1067
|
fromUrlSkill = {
|
|
972
|
-
name: "
|
|
973
|
-
description: "Extract product info (name, description, images) from any product page URL. Use this as the first step before
|
|
1068
|
+
name: "pixeldojo_from_url",
|
|
1069
|
+
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.",
|
|
974
1070
|
inputZod: InputSchema7,
|
|
975
1071
|
inputJsonSchema: InputJsonSchema7,
|
|
976
1072
|
// No model dispatch — this skill calls a server-side scrape, not a generation.
|
|
@@ -1031,7 +1127,7 @@ async function plan(input, ctx) {
|
|
|
1031
1127
|
...sceneLines,
|
|
1032
1128
|
"",
|
|
1033
1129
|
`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.`,
|
|
1034
|
-
`Review or edit at ${ctx.baseUrl}/film-studio/${film.id} \u2014 or call
|
|
1130
|
+
`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.`
|
|
1035
1131
|
].join("\n")
|
|
1036
1132
|
};
|
|
1037
1133
|
}
|
|
@@ -1046,7 +1142,7 @@ async function produce(filmId, ctx) {
|
|
|
1046
1142
|
}
|
|
1047
1143
|
return {
|
|
1048
1144
|
kind: "text",
|
|
1049
|
-
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
|
|
1145
|
+
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.`
|
|
1050
1146
|
};
|
|
1051
1147
|
}
|
|
1052
1148
|
function buildScorePlanBody(film) {
|
|
@@ -1175,13 +1271,13 @@ async function run8(input, ctx) {
|
|
|
1175
1271
|
var LOOKS, InputSchema8, InputJsonSchema8, shots, count, LOOK_MUSIC, ARC, filmSkill;
|
|
1176
1272
|
var init_film = __esm({
|
|
1177
1273
|
"src/skills/film.ts"() {
|
|
1178
|
-
LOOKS = ["live-action", "anime", "3d-animation", "noir", "horror", "sci-fi", "documentary", "western"];
|
|
1274
|
+
LOOKS = ["live-action", "anime", "3d-animation", "noir", "horror", "sci-fi", "documentary", "western", "romance", "romantic-comedy", "comedy"];
|
|
1179
1275
|
InputSchema8 = z.object({
|
|
1180
1276
|
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."),
|
|
1181
1277
|
script: z.string().max(2e4).optional().describe("A full screenplay to plan instead of an idea (sluglines, action lines, dialogue)."),
|
|
1182
1278
|
look: z.enum(LOOKS).default("live-action").describe("Visual style carried through frames, clips, and soundtrack."),
|
|
1183
1279
|
aspect: z.enum(["16:9", "9:16"]).default("16:9"),
|
|
1184
|
-
length: z.enum(["30s", "60s", "2-3min"]).default("60s").describe("Target length; drives the shot count (~6/12/24 shots)."),
|
|
1280
|
+
length: z.enum(["30s", "60s", "2-3min", "5min"]).default("60s").describe("Target length; drives the shot count (~6/12/24/60 shots)."),
|
|
1185
1281
|
film_id: z.string().max(40).optional().describe("Existing film id (from a previous call) for produce/status."),
|
|
1186
1282
|
action: 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."),
|
|
1187
1283
|
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."),
|
|
@@ -1194,7 +1290,7 @@ var init_film = __esm({
|
|
|
1194
1290
|
script: { type: "string", description: "A full screenplay to plan instead of an idea." },
|
|
1195
1291
|
look: { type: "string", enum: [...LOOKS], description: "Visual style. Default live-action." },
|
|
1196
1292
|
aspect: { type: "string", enum: ["16:9", "9:16"], description: "Default 16:9." },
|
|
1197
|
-
length: { type: "string", enum: ["30s", "60s", "2-3min"], description: "Target length (~6/12/24 shots). Default 60s." },
|
|
1293
|
+
length: { type: "string", enum: ["30s", "60s", "2-3min", "5min"], description: "Target length (~6/12/24/60 shots). Default 60s." },
|
|
1198
1294
|
film_id: { type: "string", description: "Existing film id for produce/status." },
|
|
1199
1295
|
action: { type: "string", enum: ["plan", "produce", "status"], description: "plan (free) | produce (charged) | status (poll every ~30s)." },
|
|
1200
1296
|
soundtrack: { type: "string", enum: ["scene-synced", "flat", "none"], description: "Default scene-synced." },
|
|
@@ -1211,7 +1307,10 @@ var init_film = __esm({
|
|
|
1211
1307
|
horror: "sparse horror underscore, low cello drones, dissonant string swells, creeping heartbeat pulse, unsettling quiet",
|
|
1212
1308
|
"sci-fi": "atmospheric science-fiction score, analog synth pads, deep sub-bass pulses, shimmering arpeggios, vast and cold",
|
|
1213
1309
|
documentary: "understated documentary score, felt piano, soft string beds, organic percussion textures, reflective and true",
|
|
1214
|
-
western: "cinematic western score, lonely twangy guitar, dusty strings, distant horns, wide-open desert tension"
|
|
1310
|
+
western: "cinematic western score, lonely twangy guitar, dusty strings, distant horns, wide-open desert tension",
|
|
1311
|
+
romance: "tender romantic score, felt piano and warm strings, soft acoustic guitar, intimate and unhurried, gently swelling",
|
|
1312
|
+
"romantic-comedy": "bright romantic-comedy score, plucky ukulele and handclaps, warm brushed drums, sunny and flirtatious",
|
|
1313
|
+
comedy: "playful comedy score, bouncy pizzicato strings, cheeky woodwinds, light percussion hits punctuating the gags"
|
|
1215
1314
|
};
|
|
1216
1315
|
ARC = {
|
|
1217
1316
|
opening: ["establishing the theme", "restrained", "quiet anticipation"],
|
|
@@ -1219,7 +1318,7 @@ var init_film = __esm({
|
|
|
1219
1318
|
finale: ["climactic", "full arrangement", "emotional peak resolving to stillness"]
|
|
1220
1319
|
};
|
|
1221
1320
|
filmSkill = {
|
|
1222
|
-
name: "
|
|
1321
|
+
name: "pixeldojo_film",
|
|
1223
1322
|
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).",
|
|
1224
1323
|
inputZod: InputSchema8,
|
|
1225
1324
|
inputJsonSchema: InputJsonSchema8,
|
|
@@ -1358,8 +1457,8 @@ var init_ad = __esm({
|
|
|
1358
1457
|
// Zod refine; no root-level oneOf (Grok rejects non-object root unions).
|
|
1359
1458
|
};
|
|
1360
1459
|
adSkill = {
|
|
1361
|
-
name: "
|
|
1362
|
-
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
|
|
1460
|
+
name: "pixeldojo_ad",
|
|
1461
|
+
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.",
|
|
1363
1462
|
inputZod: InputSchema9,
|
|
1364
1463
|
inputJsonSchema: InputJsonSchema9,
|
|
1365
1464
|
// Composition skill — the server routes to the ad engine internally.
|
|
@@ -1391,7 +1490,7 @@ async function run10(input, ctx) {
|
|
|
1391
1490
|
}
|
|
1392
1491
|
return {
|
|
1393
1492
|
kind: "handoff",
|
|
1394
|
-
text: result.message ?? `Campaign ${result.campaignId} queued. Poll via
|
|
1493
|
+
text: result.message ?? `Campaign ${result.campaignId} queued. Poll via pixeldojo_campaign_status.`,
|
|
1395
1494
|
jobId: result.campaignId,
|
|
1396
1495
|
statusUrl: result.statusUrl ?? ctx.buildStatusUrl(result.campaignId)
|
|
1397
1496
|
};
|
|
@@ -1444,8 +1543,8 @@ var init_campaign = __esm({
|
|
|
1444
1543
|
oneOf: [{ required: ["productUrl"] }, { required: ["product"] }]
|
|
1445
1544
|
};
|
|
1446
1545
|
campaignSkill = {
|
|
1447
|
-
name: "
|
|
1448
|
-
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
|
|
1546
|
+
name: "pixeldojo_campaign",
|
|
1547
|
+
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.",
|
|
1449
1548
|
inputZod: InputSchema10,
|
|
1450
1549
|
inputJsonSchema: InputJsonSchema10,
|
|
1451
1550
|
// Composition skill — internal sub-jobs route to per-tool models, no
|
|
@@ -1490,21 +1589,21 @@ var InputSchema11, InputJsonSchema11, campaignStatusSkill;
|
|
|
1490
1589
|
var init_campaign_status = __esm({
|
|
1491
1590
|
"src/skills/campaign-status.ts"() {
|
|
1492
1591
|
InputSchema11 = z.object({
|
|
1493
|
-
campaignId: z.string().min(1).max(200).describe("Campaign id returned from a prior
|
|
1592
|
+
campaignId: z.string().min(1).max(200).describe("Campaign id returned from a prior pixeldojo_campaign handoff.")
|
|
1494
1593
|
});
|
|
1495
1594
|
InputJsonSchema11 = {
|
|
1496
1595
|
type: "object",
|
|
1497
1596
|
properties: {
|
|
1498
1597
|
campaignId: {
|
|
1499
1598
|
type: "string",
|
|
1500
|
-
description: "Campaign id returned from a prior
|
|
1599
|
+
description: "Campaign id returned from a prior pixeldojo_campaign handoff."
|
|
1501
1600
|
}
|
|
1502
1601
|
},
|
|
1503
1602
|
required: ["campaignId"]
|
|
1504
1603
|
};
|
|
1505
1604
|
campaignStatusSkill = {
|
|
1506
|
-
name: "
|
|
1507
|
-
description: "Poll a campaign by campaignId returned from
|
|
1605
|
+
name: "pixeldojo_campaign_status",
|
|
1606
|
+
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.",
|
|
1508
1607
|
inputZod: InputSchema11,
|
|
1509
1608
|
inputJsonSchema: InputJsonSchema11,
|
|
1510
1609
|
allowedModels: [],
|
|
@@ -1519,7 +1618,7 @@ async function run12(input, ctx) {
|
|
|
1519
1618
|
url = await uploadLocalFile(input.path, ctx.client);
|
|
1520
1619
|
} catch (err) {
|
|
1521
1620
|
if (err instanceof SkillUploadError) {
|
|
1522
|
-
return { kind: "text", text: `
|
|
1621
|
+
return { kind: "text", text: `pixeldojo_upload: ${err.userMessage}` };
|
|
1523
1622
|
}
|
|
1524
1623
|
throw err;
|
|
1525
1624
|
}
|
|
@@ -1549,7 +1648,7 @@ var init_upload = __esm({
|
|
|
1549
1648
|
required: ["path"]
|
|
1550
1649
|
};
|
|
1551
1650
|
uploadSkill = {
|
|
1552
|
-
name: "
|
|
1651
|
+
name: "pixeldojo_upload",
|
|
1553
1652
|
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.",
|
|
1554
1653
|
inputZod: InputSchema12,
|
|
1555
1654
|
inputJsonSchema: InputJsonSchema12,
|
|
@@ -1564,7 +1663,7 @@ async function run13(input, ctx) {
|
|
|
1564
1663
|
if (!ALLOWED4.includes(requested)) {
|
|
1565
1664
|
return {
|
|
1566
1665
|
kind: "text",
|
|
1567
|
-
text: `Unknown model "${requested}" for
|
|
1666
|
+
text: `Unknown model "${requested}" for pixeldojo_audio. Allowed: ${ALLOWED4.join(", ")}.`
|
|
1568
1667
|
};
|
|
1569
1668
|
}
|
|
1570
1669
|
const apiId = requested;
|
|
@@ -1575,7 +1674,7 @@ async function run13(input, ctx) {
|
|
|
1575
1674
|
audioUrls = [...audioUrls, ...uploaded].slice(0, 3);
|
|
1576
1675
|
} catch (err) {
|
|
1577
1676
|
if (err instanceof SkillUploadError) {
|
|
1578
|
-
return { kind: "text", text: `
|
|
1677
|
+
return { kind: "text", text: `pixeldojo_audio (upload): ${err.userMessage}` };
|
|
1579
1678
|
}
|
|
1580
1679
|
throw err;
|
|
1581
1680
|
}
|
|
@@ -1641,7 +1740,7 @@ var init_audio = __esm({
|
|
|
1641
1740
|
required: ["prompt"]
|
|
1642
1741
|
};
|
|
1643
1742
|
audioSkill = {
|
|
1644
|
-
name: "
|
|
1743
|
+
name: "pixeldojo_audio",
|
|
1645
1744
|
description: "Generate audio: speech, multi-character dialogue, sound effects, and background music from one prompt (Seed Audio 1.0 by default; plain TTS available).",
|
|
1646
1745
|
inputZod: InputSchema13,
|
|
1647
1746
|
inputJsonSchema: InputJsonSchema13,
|
|
@@ -1661,7 +1760,7 @@ async function run14(input, ctx) {
|
|
|
1661
1760
|
if (res.items.length === 0) {
|
|
1662
1761
|
return {
|
|
1663
1762
|
kind: "text",
|
|
1664
|
-
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
|
|
1763
|
+
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."
|
|
1665
1764
|
};
|
|
1666
1765
|
}
|
|
1667
1766
|
const lines = res.items.map((it) => {
|
|
@@ -1702,8 +1801,8 @@ var init_library = __esm({
|
|
|
1702
1801
|
required: []
|
|
1703
1802
|
};
|
|
1704
1803
|
librarySkill = {
|
|
1705
|
-
name: "
|
|
1706
|
-
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
|
|
1804
|
+
name: "pixeldojo_library",
|
|
1805
|
+
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.",
|
|
1707
1806
|
inputZod: InputSchema14,
|
|
1708
1807
|
inputJsonSchema: InputJsonSchema14,
|
|
1709
1808
|
allowedModels: [],
|
|
@@ -1723,7 +1822,7 @@ async function run15(input, ctx) {
|
|
|
1723
1822
|
return {
|
|
1724
1823
|
kind: "text",
|
|
1725
1824
|
text: `Saved to My Media (permanent): ${res.url}
|
|
1726
|
-
It will appear at https://pixeldojo.ai/my-images and in
|
|
1825
|
+
It will appear at https://pixeldojo.ai/my-images and in pixeldojo_library searches.`
|
|
1727
1826
|
};
|
|
1728
1827
|
}
|
|
1729
1828
|
var InputSchema15, InputJsonSchema15, saveSkill;
|
|
@@ -1748,7 +1847,7 @@ var init_save = __esm({
|
|
|
1748
1847
|
required: ["url"]
|
|
1749
1848
|
};
|
|
1750
1849
|
saveSkill = {
|
|
1751
|
-
name: "
|
|
1850
|
+
name: "pixeldojo_save",
|
|
1752
1851
|
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.",
|
|
1753
1852
|
inputZod: InputSchema15,
|
|
1754
1853
|
inputJsonSchema: InputJsonSchema15,
|
|
@@ -1796,7 +1895,7 @@ async function runSave(input, ctx) {
|
|
|
1796
1895
|
});
|
|
1797
1896
|
return {
|
|
1798
1897
|
kind: "text",
|
|
1799
|
-
text: res.created ? `Saved workflow "${res.name}" (${res.steps} step${res.steps === 1 ? "" : "s"}). Run it with
|
|
1898
|
+
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"}).`
|
|
1800
1899
|
};
|
|
1801
1900
|
}
|
|
1802
1901
|
async function runList(_input, ctx) {
|
|
@@ -1804,7 +1903,7 @@ async function runList(_input, ctx) {
|
|
|
1804
1903
|
if (res.count === 0) {
|
|
1805
1904
|
return {
|
|
1806
1905
|
kind: "text",
|
|
1807
|
-
text: "No saved workflows yet. Save one with
|
|
1906
|
+
text: "No saved workflows yet. Save one with pixeldojo_save_workflow { name, steps: [{ skill, args }] }."
|
|
1808
1907
|
};
|
|
1809
1908
|
}
|
|
1810
1909
|
const lines = res.workflows.map(
|
|
@@ -1873,7 +1972,7 @@ async function runWorkflow(input, ctx) {
|
|
|
1873
1972
|
} else if (result.kind === "handoff") {
|
|
1874
1973
|
return {
|
|
1875
1974
|
kind: "text",
|
|
1876
|
-
text: `Workflow "${workflow.name}" is still on step ${i + 1} (${step.skill}) after an extended wait. Poll with
|
|
1975
|
+
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.`
|
|
1877
1976
|
};
|
|
1878
1977
|
} else {
|
|
1879
1978
|
outputs.push(`step ${i + 1} (${step.skill}): done (no asset URL)`);
|
|
@@ -1892,7 +1991,7 @@ var init_workflows = __esm({
|
|
|
1892
1991
|
init_types();
|
|
1893
1992
|
WORKFLOW_STEP_EXTRA_BUDGET_MS = 6e4;
|
|
1894
1993
|
StepSchema = z.object({
|
|
1895
|
-
skill: z.string().regex(/^pixeldojo:(generate|edit|character|storyboard|upscale|audio|upload|from_url)$/, {
|
|
1994
|
+
skill: z.string().regex(/^pixeldojo[:_](generate|edit|character|storyboard|upscale|audio|upload|from_url)$/, {
|
|
1896
1995
|
message: "Runnable skills only: generate, edit, character, storyboard, upscale, audio, upload, from_url."
|
|
1897
1996
|
}),
|
|
1898
1997
|
args: z.record(z.string(), z.unknown()).optional()
|
|
@@ -1918,7 +2017,7 @@ var init_workflows = __esm({
|
|
|
1918
2017
|
required: ["skill"]
|
|
1919
2018
|
};
|
|
1920
2019
|
saveWorkflowSkill = {
|
|
1921
|
-
name: "
|
|
2020
|
+
name: "pixeldojo_save_workflow",
|
|
1922
2021
|
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.",
|
|
1923
2022
|
inputZod: SaveInputSchema,
|
|
1924
2023
|
inputJsonSchema: {
|
|
@@ -1935,7 +2034,7 @@ var init_workflows = __esm({
|
|
|
1935
2034
|
run: runSave
|
|
1936
2035
|
};
|
|
1937
2036
|
runWorkflowSkill = {
|
|
1938
|
-
name: "
|
|
2037
|
+
name: "pixeldojo_run_workflow",
|
|
1939
2038
|
description: "Run a saved workflow by name. Pass inputs for its {{input.<key>}} placeholders; each step feeds the next via {{prev}}.",
|
|
1940
2039
|
inputZod: RunInputSchema,
|
|
1941
2040
|
inputJsonSchema: {
|
|
@@ -1951,7 +2050,7 @@ var init_workflows = __esm({
|
|
|
1951
2050
|
run: runWorkflow
|
|
1952
2051
|
};
|
|
1953
2052
|
listWorkflowsSkill = {
|
|
1954
|
-
name: "
|
|
2053
|
+
name: "pixeldojo_list_workflows",
|
|
1955
2054
|
description: "List your saved workflows with step counts and run totals.",
|
|
1956
2055
|
inputZod: ListInputSchema,
|
|
1957
2056
|
inputJsonSchema: { type: "object", properties: {}, required: [] },
|
|
@@ -1987,7 +2086,8 @@ __export(skills_exports, {
|
|
|
1987
2086
|
upscaleSkill: () => upscaleSkill
|
|
1988
2087
|
});
|
|
1989
2088
|
function findSkill(name) {
|
|
1990
|
-
|
|
2089
|
+
const wanted = name.replace(/^pixeldojo:/, "pixeldojo_");
|
|
2090
|
+
return ALL_SKILLS.find((s) => s.name === wanted);
|
|
1991
2091
|
}
|
|
1992
2092
|
var ALL_SKILLS;
|
|
1993
2093
|
var init_skills = __esm({
|
|
@@ -2081,7 +2181,7 @@ function makeRedactedLogger(target = console) {
|
|
|
2081
2181
|
}
|
|
2082
2182
|
|
|
2083
2183
|
// src/server.ts
|
|
2084
|
-
var PACKAGE_VERSION = "0.9.
|
|
2184
|
+
var PACKAGE_VERSION = "0.9.1" ;
|
|
2085
2185
|
function buildServer(options) {
|
|
2086
2186
|
if (!options.apiKey) {
|
|
2087
2187
|
throw new Error(
|
|
@@ -2248,19 +2348,19 @@ function printInit() {
|
|
|
2248
2348
|
`# Same shape as above.`,
|
|
2249
2349
|
``,
|
|
2250
2350
|
`# 3. Restart your agent. You'll see these tools:`,
|
|
2251
|
-
`#
|
|
2252
|
-
`#
|
|
2253
|
-
`#
|
|
2254
|
-
`#
|
|
2255
|
-
`#
|
|
2256
|
-
`#
|
|
2257
|
-
`#
|
|
2258
|
-
`#
|
|
2259
|
-
`#
|
|
2260
|
-
`#
|
|
2261
|
-
`#
|
|
2262
|
-
`#
|
|
2263
|
-
`#
|
|
2351
|
+
`# pixeldojo_generate Any prompt \u2192 image or video.`,
|
|
2352
|
+
`# pixeldojo_edit Change an image with a text instruction.`,
|
|
2353
|
+
`# pixeldojo_character Consistent characters across shots.`,
|
|
2354
|
+
`# pixeldojo_storyboard Multi-shot scenes from one brief.`,
|
|
2355
|
+
`# pixeldojo_upscale Enhance any image.`,
|
|
2356
|
+
`# pixeldojo_audio Voices + effects + music in one clip.`,
|
|
2357
|
+
`# pixeldojo_library Search your saved media + generations.`,
|
|
2358
|
+
`# pixeldojo_save Keep a result in My Media permanently.`,
|
|
2359
|
+
`# pixeldojo_*_workflow Save / run / list multi-step chains.`,
|
|
2360
|
+
`# pixeldojo_upload Local file \u2192 temp URL for references.`,
|
|
2361
|
+
`# pixeldojo_from_url Product URL \u2192 structured product info.`,
|
|
2362
|
+
`# pixeldojo_campaign* Product URL \u2192 full launch pack.`,
|
|
2363
|
+
`# pixeldojo_status Poll long-running jobs.`,
|
|
2264
2364
|
``
|
|
2265
2365
|
].join("\n")
|
|
2266
2366
|
);
|