@pixeldojo/mcp 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -15
- package/dist/cli.cjs +442 -131
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +442 -131
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +429 -118
- 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 +429 -118
- 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
|
};
|
|
@@ -183,18 +183,23 @@ var init_upload_helpers = __esm({
|
|
|
183
183
|
});
|
|
184
184
|
|
|
185
185
|
// src/skills/schema-snap.ts
|
|
186
|
-
function
|
|
186
|
+
function getModelInfo(apiId, client) {
|
|
187
187
|
const cached = schemaCache.get(apiId);
|
|
188
188
|
if (cached) return cached;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
if (typeof client?.getModelSchema !== "function") return Promise.resolve(null);
|
|
190
|
+
const pending = Promise.resolve().then(() => client.getModelSchema(apiId)).then((res) => {
|
|
191
|
+
const body = res;
|
|
192
|
+
return { props: body.schema?.properties ?? {}, media: body.media };
|
|
193
|
+
}).catch(() => {
|
|
192
194
|
schemaCache.delete(apiId);
|
|
193
195
|
return null;
|
|
194
196
|
});
|
|
195
197
|
schemaCache.set(apiId, pending);
|
|
196
198
|
return pending;
|
|
197
199
|
}
|
|
200
|
+
async function getFieldConstraints(apiId, client) {
|
|
201
|
+
return (await getModelInfo(apiId, client))?.props ?? null;
|
|
202
|
+
}
|
|
198
203
|
function parseAspectRatio(ar) {
|
|
199
204
|
const m = /^\s*(\d+)\s*:\s*(\d+)\s*$/.exec(ar);
|
|
200
205
|
if (!m) return null;
|
|
@@ -230,6 +235,55 @@ async function snapAspectRatio(apiId, requested, client, opts = {}) {
|
|
|
230
235
|
if (!field.enum || field.enum.length === 0) return requested;
|
|
231
236
|
return nearestAspectFromEnum(requested, field.enum);
|
|
232
237
|
}
|
|
238
|
+
async function resolveMediaSlot(apiId, kind, client) {
|
|
239
|
+
const info = await getModelInfo(apiId, client);
|
|
240
|
+
if (info === null) return void 0;
|
|
241
|
+
const declared = info.media?.[kind];
|
|
242
|
+
if (declared) {
|
|
243
|
+
return {
|
|
244
|
+
name: declared.key,
|
|
245
|
+
isArray: info.props[declared.key]?.type === "array",
|
|
246
|
+
also: declared.also
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (info.media && kind !== "audio") return null;
|
|
250
|
+
for (const name of FALLBACK_KEYS[kind]) {
|
|
251
|
+
const field = info.props[name];
|
|
252
|
+
if (field) return { name, isArray: field.type === "array" };
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
function resolutionHeight(value) {
|
|
257
|
+
const v = value.trim().toLowerCase();
|
|
258
|
+
if (v === "4k") return 2160;
|
|
259
|
+
const n = Number.parseInt(v, 10);
|
|
260
|
+
return Number.isFinite(n) ? n : null;
|
|
261
|
+
}
|
|
262
|
+
async function snapResolution(apiId, requested, client, opts = {}) {
|
|
263
|
+
if (!requested) return void 0;
|
|
264
|
+
const props = await getFieldConstraints(apiId, client);
|
|
265
|
+
if (props === null) {
|
|
266
|
+
return opts.onUnknown === "omit" ? void 0 : requested;
|
|
267
|
+
}
|
|
268
|
+
const field = props.resolution;
|
|
269
|
+
if (!field) return void 0;
|
|
270
|
+
if (!field.enum || field.enum.length === 0) return requested;
|
|
271
|
+
if (field.enum.includes(requested)) return requested;
|
|
272
|
+
const target = resolutionHeight(requested);
|
|
273
|
+
if (target === null) return requested;
|
|
274
|
+
let best = requested;
|
|
275
|
+
let bestDiff = Infinity;
|
|
276
|
+
for (const candidate of field.enum) {
|
|
277
|
+
const h = resolutionHeight(candidate);
|
|
278
|
+
if (h === null) continue;
|
|
279
|
+
const diff = Math.abs(h - target);
|
|
280
|
+
if (diff < bestDiff) {
|
|
281
|
+
bestDiff = diff;
|
|
282
|
+
best = candidate;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return best;
|
|
286
|
+
}
|
|
233
287
|
async function snapDuration(apiId, requested, client, opts = {}) {
|
|
234
288
|
if (requested === void 0) return void 0;
|
|
235
289
|
const props = await getFieldConstraints(apiId, client);
|
|
@@ -254,24 +308,49 @@ async function snapDuration(apiId, requested, client, opts = {}) {
|
|
|
254
308
|
}
|
|
255
309
|
return requested;
|
|
256
310
|
}
|
|
311
|
+
function isVideoUrl(url) {
|
|
312
|
+
return /\.(mp4|mov|webm|m4v)(?:[?#]|$)/i.test(url);
|
|
313
|
+
}
|
|
314
|
+
function isAudioUrl(url) {
|
|
315
|
+
return /\.(mp3|wav|m4a|aac|ogg|flac)(?:[?#]|$)/i.test(url);
|
|
316
|
+
}
|
|
257
317
|
async function resolveImageRefs(apiId, urls, client) {
|
|
258
318
|
if (urls.length === 0) return null;
|
|
259
|
-
const
|
|
260
|
-
if (
|
|
261
|
-
const
|
|
319
|
+
const info = await getModelInfo(apiId, client);
|
|
320
|
+
if (info === null) return null;
|
|
321
|
+
const props = info.props;
|
|
322
|
+
const key = info.media ? info.media.image?.key : REF_KEYS.find((k) => props[k] !== void 0);
|
|
262
323
|
if (!key) return null;
|
|
263
|
-
const
|
|
324
|
+
const also = info.media?.image?.also;
|
|
325
|
+
const field = props[key] ?? {};
|
|
264
326
|
if (field.type === "array") {
|
|
265
327
|
const cap = typeof field.maxItems === "number" ? field.maxItems : urls.length;
|
|
266
328
|
const kept = urls.slice(0, Math.max(1, cap));
|
|
267
|
-
return { key, value: kept, dropped: urls.length - kept.length };
|
|
329
|
+
return { key, value: kept, dropped: urls.length - kept.length, also };
|
|
268
330
|
}
|
|
269
|
-
return { key, value: urls[0], dropped: urls.length - 1 };
|
|
331
|
+
return { key, value: urls[0], dropped: urls.length - 1, also };
|
|
270
332
|
}
|
|
271
|
-
var schemaCache, REF_KEYS;
|
|
333
|
+
var schemaCache, REFERENCE_FIELD_PRIORITY, FALLBACK_KEYS, REF_KEYS;
|
|
272
334
|
var init_schema_snap = __esm({
|
|
273
335
|
"src/skills/schema-snap.ts"() {
|
|
274
336
|
schemaCache = /* @__PURE__ */ new Map();
|
|
337
|
+
REFERENCE_FIELD_PRIORITY = [
|
|
338
|
+
"image_url",
|
|
339
|
+
"start_image_url",
|
|
340
|
+
"input_image",
|
|
341
|
+
"image",
|
|
342
|
+
"reference_images",
|
|
343
|
+
"reference_urls",
|
|
344
|
+
"reference_image_urls",
|
|
345
|
+
"image_urls",
|
|
346
|
+
"image_input",
|
|
347
|
+
"images"
|
|
348
|
+
];
|
|
349
|
+
FALLBACK_KEYS = {
|
|
350
|
+
image: REFERENCE_FIELD_PRIORITY,
|
|
351
|
+
video: ["reference_videos"],
|
|
352
|
+
audio: ["reference_audios", "audio_url"]
|
|
353
|
+
};
|
|
275
354
|
REF_KEYS = [
|
|
276
355
|
"image_urls",
|
|
277
356
|
"images",
|
|
@@ -284,7 +363,51 @@ var init_schema_snap = __esm({
|
|
|
284
363
|
];
|
|
285
364
|
}
|
|
286
365
|
});
|
|
287
|
-
function
|
|
366
|
+
function paramsWithoutModel(params, model) {
|
|
367
|
+
if (!params || Object.keys(params).length === 0) return null;
|
|
368
|
+
if (model) return null;
|
|
369
|
+
return "Those settings are model-specific. Tell me which model to use and I will apply them.";
|
|
370
|
+
}
|
|
371
|
+
function applyParams(body, params, paramsModel, apiId) {
|
|
372
|
+
if (!params || Object.keys(params).length === 0) return void 0;
|
|
373
|
+
if (paramsModel !== apiId) {
|
|
374
|
+
return `This ran on ${apiId}, so I left those settings off: they are ${paramsModel}'s fields. Ask again naming ${apiId} if you want them applied.`;
|
|
375
|
+
}
|
|
376
|
+
const rest = { ...params };
|
|
377
|
+
delete rest.prompt;
|
|
378
|
+
Object.assign(body, rest);
|
|
379
|
+
return "prompt" in params ? "I kept the prompt from the skill call and ignored the one in params." : void 0;
|
|
380
|
+
}
|
|
381
|
+
function prefixNote(result, note) {
|
|
382
|
+
if (!note) return result;
|
|
383
|
+
return { ...result, text: `${note} ${result.text}`.trim() };
|
|
384
|
+
}
|
|
385
|
+
var ParamsInput, PARAMS_JSON_SCHEMA;
|
|
386
|
+
var init_params = __esm({
|
|
387
|
+
"src/skills/params.ts"() {
|
|
388
|
+
ParamsInput = z.record(z.unknown()).optional();
|
|
389
|
+
PARAMS_JSON_SCHEMA = {
|
|
390
|
+
type: "object",
|
|
391
|
+
additionalProperties: true,
|
|
392
|
+
description: 'Advanced settings for the model that runs this, named exactly as that model documents them (e.g. {"resolution": "1080p", "seed": 42}). Set `model` in the same call: field names differ per model, so params without one are refused. Applied last, so these override anything derived from the other inputs. An invalid field comes back named with its allowed values and nothing is charged, so fix it and call again.'
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
function fallbackSlot(apiId, kind) {
|
|
397
|
+
return apiId === VIDEO_REFERENCE_MODEL ? VIDEO_REFERENCE_SLOTS[kind] : void 0;
|
|
398
|
+
}
|
|
399
|
+
function sourceMedia(videoUrl, referenceUrls) {
|
|
400
|
+
const clip = videoUrl && !isAudioUrl(videoUrl) ? videoUrl : void 0;
|
|
401
|
+
const stills = referenceUrls.filter((u) => !isAudioUrl(u));
|
|
402
|
+
const url = clip ?? stills[0];
|
|
403
|
+
return {
|
|
404
|
+
url,
|
|
405
|
+
isClip: Boolean(clip) || !!url && isVideoUrl(url),
|
|
406
|
+
audioUrl: [videoUrl, ...referenceUrls].find((u) => u && isAudioUrl(u)),
|
|
407
|
+
stills
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function buildPayload(apiId, input, src, slot, audioSlot) {
|
|
288
411
|
const body = { prompt: input.prompt };
|
|
289
412
|
if (input.aspect_ratio) body.aspect_ratio = input.aspect_ratio;
|
|
290
413
|
if (input.preset) body.preset = input.preset;
|
|
@@ -292,9 +415,9 @@ function buildPayload(apiId, input) {
|
|
|
292
415
|
case "grok-video-extend":
|
|
293
416
|
case "wan-video-edit":
|
|
294
417
|
case "seedance-video-edit":
|
|
295
|
-
if (
|
|
296
|
-
body.video_url =
|
|
297
|
-
body.image_url =
|
|
418
|
+
if (src.url) {
|
|
419
|
+
body.video_url = src.url;
|
|
420
|
+
body.image_url = src.url;
|
|
298
421
|
}
|
|
299
422
|
if (input.duration !== void 0) {
|
|
300
423
|
body.video_duration = input.duration;
|
|
@@ -302,48 +425,90 @@ function buildPayload(apiId, input) {
|
|
|
302
425
|
}
|
|
303
426
|
break;
|
|
304
427
|
default:
|
|
305
|
-
if (
|
|
428
|
+
if (src.url) {
|
|
429
|
+
const refs = src.isClip ? [src.url] : src.stills.length > 0 ? src.stills : [src.url];
|
|
430
|
+
if (slot) {
|
|
431
|
+
body[slot.name] = slot.isArray ? refs : refs[0];
|
|
432
|
+
if (slot.also) Object.assign(body, slot.also);
|
|
433
|
+
} else {
|
|
434
|
+
body.image_url = refs[0];
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (src.audioUrl && audioSlot) {
|
|
438
|
+
body[audioSlot.name] = audioSlot.isArray ? [src.audioUrl] : src.audioUrl;
|
|
439
|
+
if (audioSlot.also) Object.assign(body, audioSlot.also);
|
|
440
|
+
}
|
|
306
441
|
if (input.duration !== void 0) body.duration = input.duration;
|
|
442
|
+
if (input.resolution) body.resolution = input.resolution;
|
|
307
443
|
break;
|
|
308
444
|
}
|
|
309
445
|
return body;
|
|
310
446
|
}
|
|
311
447
|
async function run(input, ctx) {
|
|
312
|
-
const
|
|
448
|
+
const paramsGate = paramsWithoutModel(input.params, input.model);
|
|
449
|
+
if (paramsGate) return { kind: "text", text: paramsGate };
|
|
313
450
|
let resolved = input;
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
451
|
+
let referenceUrls = [];
|
|
452
|
+
try {
|
|
453
|
+
const singleUrl = input.image_url ?? (input.image_path ? await uploadLocalFile(input.image_path, ctx.client) : void 0);
|
|
454
|
+
const manyUrls = [...input.image_urls ?? []];
|
|
455
|
+
for (const p of input.image_paths ?? []) {
|
|
456
|
+
manyUrls.push(await uploadLocalFile(p, ctx.client));
|
|
457
|
+
}
|
|
458
|
+
referenceUrls = [...singleUrl ? [singleUrl] : [], ...manyUrls].filter(
|
|
459
|
+
(u, i, all) => all.indexOf(u) === i
|
|
460
|
+
);
|
|
461
|
+
if (referenceUrls[0] && referenceUrls[0] !== input.image_url) {
|
|
462
|
+
resolved = { ...input, image_url: referenceUrls[0] };
|
|
463
|
+
}
|
|
464
|
+
} catch (err) {
|
|
465
|
+
if (err instanceof SkillUploadError) {
|
|
466
|
+
return { kind: "text", text: `pixeldojo_generate (upload): ${err.userMessage}` };
|
|
467
|
+
}
|
|
468
|
+
throw err;
|
|
469
|
+
}
|
|
470
|
+
const src = sourceMedia(input.video_url, referenceUrls);
|
|
471
|
+
let apiId = input.model ?? DEFAULT_MODEL;
|
|
472
|
+
let routeNote;
|
|
473
|
+
if ((src.isClip || src.audioUrl) && !VIDEO_INPUT_MODELS.has(apiId)) {
|
|
474
|
+
if (!input.model) {
|
|
475
|
+
apiId = VIDEO_REFERENCE_MODEL;
|
|
476
|
+
} else if (src.isClip && await resolveMediaSlot(input.model, "video", ctx.client) === null) {
|
|
477
|
+
apiId = VIDEO_REFERENCE_MODEL;
|
|
478
|
+
routeNote = `${input.model} can't work from a video clip, so I used ${VIDEO_REFERENCE_MODEL} instead.`;
|
|
323
479
|
}
|
|
324
480
|
}
|
|
325
481
|
if (apiId === "grok-video-extend") {
|
|
326
|
-
if (!
|
|
482
|
+
if (!src.url) {
|
|
327
483
|
return {
|
|
328
484
|
kind: "text",
|
|
329
|
-
text: '
|
|
485
|
+
text: 'pixeldojo_generate with model="grok-video-extend" requires video_url (the source video URL to extend, or image_path for a local file).'
|
|
330
486
|
};
|
|
331
487
|
}
|
|
332
488
|
if (resolved.duration === void 0) {
|
|
333
489
|
return {
|
|
334
490
|
kind: "text",
|
|
335
|
-
text: '
|
|
491
|
+
text: 'pixeldojo_generate with model="grok-video-extend" requires duration (extension length in seconds).'
|
|
336
492
|
};
|
|
337
493
|
}
|
|
338
494
|
}
|
|
495
|
+
let refField;
|
|
496
|
+
let audioSlot;
|
|
339
497
|
if (!VIDEO_INPUT_MODELS.has(apiId)) {
|
|
340
|
-
const [aspect_ratio, duration] = await Promise.all([
|
|
498
|
+
const [aspect_ratio, duration, resolution, ref, audio] = await Promise.all([
|
|
341
499
|
snapAspectRatio(apiId, resolved.aspect_ratio, ctx.client),
|
|
342
|
-
snapDuration(apiId, resolved.duration, ctx.client)
|
|
500
|
+
snapDuration(apiId, resolved.duration, ctx.client),
|
|
501
|
+
snapResolution(apiId, resolved.resolution, ctx.client),
|
|
502
|
+
src.url ? resolveMediaSlot(apiId, src.isClip ? "video" : "image", ctx.client) : Promise.resolve(void 0),
|
|
503
|
+
src.audioUrl ? resolveMediaSlot(apiId, "audio", ctx.client) : Promise.resolve(void 0)
|
|
343
504
|
]);
|
|
344
|
-
resolved = { ...resolved, aspect_ratio, duration };
|
|
505
|
+
resolved = { ...resolved, aspect_ratio, duration, resolution };
|
|
506
|
+
refField = ref === void 0 && src.url ? fallbackSlot(apiId, src.isClip ? "video" : "image") : ref;
|
|
507
|
+
audioSlot = audio === void 0 && src.audioUrl ? fallbackSlot(apiId, "audio") : audio;
|
|
345
508
|
}
|
|
346
|
-
const body = buildPayload(apiId, resolved);
|
|
509
|
+
const body = buildPayload(apiId, resolved, src, refField, audioSlot);
|
|
510
|
+
const paramsNote = applyParams(body, input.params, input.model, apiId);
|
|
511
|
+
const note = [routeNote, paramsNote].filter(Boolean).join(" ");
|
|
347
512
|
const submitted = await ctx.client.run(apiId, body);
|
|
348
513
|
const result = await pollJobUntilDoneOrHandoff(
|
|
349
514
|
submitted.jobId,
|
|
@@ -354,18 +519,22 @@ async function run(input, ctx) {
|
|
|
354
519
|
sleep: ctx.pollOverrides?.sleep
|
|
355
520
|
}
|
|
356
521
|
);
|
|
357
|
-
return
|
|
358
|
-
|
|
359
|
-
|
|
522
|
+
return prefixNote(
|
|
523
|
+
pollResultToSkillResult(
|
|
524
|
+
result,
|
|
525
|
+
(urls) => urls.length === 0 ? `Job ${submitted.jobId} completed but returned no assets.` : urls.length === 1 ? `Generated. Asset: ${urls[0]}` : `Generated ${urls.length} assets. URLs: ${urls.join(", ")}`
|
|
526
|
+
),
|
|
527
|
+
note
|
|
360
528
|
);
|
|
361
529
|
}
|
|
362
|
-
var InputSchema, DEFAULT_MODEL, VIDEO_INPUT_MODELS, InputJsonSchema, generateSkill;
|
|
530
|
+
var InputSchema, DEFAULT_MODEL, VIDEO_REFERENCE_MODEL, VIDEO_REFERENCE_SLOTS, VIDEO_INPUT_MODELS, InputJsonSchema, generateSkill;
|
|
363
531
|
var init_generate = __esm({
|
|
364
532
|
"src/skills/generate.ts"() {
|
|
365
533
|
init_poll();
|
|
366
534
|
init_types();
|
|
367
535
|
init_upload_helpers();
|
|
368
536
|
init_schema_snap();
|
|
537
|
+
init_params();
|
|
369
538
|
InputSchema = z.object({
|
|
370
539
|
prompt: z.string().min(1).max(8e3).describe("Prompt describing what to generate."),
|
|
371
540
|
model: z.string().optional().describe(
|
|
@@ -376,12 +545,31 @@ var init_generate = __esm({
|
|
|
376
545
|
),
|
|
377
546
|
aspect_ratio: z.string().optional().describe('Aspect ratio supported by the chosen model (e.g. "1:1", "16:9", "9:16").'),
|
|
378
547
|
duration: z.number().int().positive().optional().describe("Video duration in seconds, when applicable."),
|
|
548
|
+
resolution: z.string().optional().describe(
|
|
549
|
+
'Output resolution when the model supports it (e.g. "480p", "720p", "1080p", "4k"). Lower tiers cost fewer credits. Omit to use the model default.'
|
|
550
|
+
),
|
|
379
551
|
image_url: z.string().url().optional().describe("Reference image URL for image-to-image / image-to-video tools."),
|
|
552
|
+
video_url: z.string().url().optional().describe(
|
|
553
|
+
"Source video clip to animate / restyle / extend. Use this for videos; image_url is for still frames. A clip routes to a model that can actually read one."
|
|
554
|
+
),
|
|
555
|
+
params: ParamsInput,
|
|
380
556
|
image_path: z.string().min(1).optional().describe(
|
|
381
557
|
"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."
|
|
558
|
+
),
|
|
559
|
+
image_urls: z.array(z.string().url()).optional().describe(
|
|
560
|
+
"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."
|
|
561
|
+
),
|
|
562
|
+
image_paths: z.array(z.string().min(1)).optional().describe(
|
|
563
|
+
"Multiple absolute local file paths. Each is uploaded to PixelDojo (24h temp storage) and used as a reference, same rules as image_urls."
|
|
382
564
|
)
|
|
383
565
|
});
|
|
384
566
|
DEFAULT_MODEL = "google-nano-banana";
|
|
567
|
+
VIDEO_REFERENCE_MODEL = "seedance-2-reference";
|
|
568
|
+
VIDEO_REFERENCE_SLOTS = {
|
|
569
|
+
image: { name: "reference_images", isArray: true },
|
|
570
|
+
video: { name: "reference_videos", isArray: true },
|
|
571
|
+
audio: { name: "reference_audios", isArray: true }
|
|
572
|
+
};
|
|
385
573
|
VIDEO_INPUT_MODELS = /* @__PURE__ */ new Set(["grok-video-extend", "wan-video-edit", "seedance-video-edit"]);
|
|
386
574
|
InputJsonSchema = {
|
|
387
575
|
type: "object",
|
|
@@ -397,7 +585,27 @@ var init_generate = __esm({
|
|
|
397
585
|
},
|
|
398
586
|
aspect_ratio: { type: "string" },
|
|
399
587
|
duration: { type: "integer" },
|
|
588
|
+
resolution: {
|
|
589
|
+
type: "string",
|
|
590
|
+
description: 'Output resolution when supported (e.g. "480p", "720p", "1080p", "4k"). Lower tiers cost fewer credits.'
|
|
591
|
+
},
|
|
400
592
|
image_url: { type: "string", format: "uri" },
|
|
593
|
+
video_url: {
|
|
594
|
+
type: "string",
|
|
595
|
+
format: "uri",
|
|
596
|
+
description: "Source video clip to animate / restyle / extend. Use this for videos; image_url is for still frames."
|
|
597
|
+
},
|
|
598
|
+
params: PARAMS_JSON_SCHEMA,
|
|
599
|
+
image_urls: {
|
|
600
|
+
type: "array",
|
|
601
|
+
items: { type: "string", format: "uri" },
|
|
602
|
+
description: "Multiple reference image URLs for models that accept a reference set. Single-reference models use the first."
|
|
603
|
+
},
|
|
604
|
+
image_paths: {
|
|
605
|
+
type: "array",
|
|
606
|
+
items: { type: "string" },
|
|
607
|
+
description: "Multiple absolute local file paths, each uploaded to 24h temp storage and used as a reference."
|
|
608
|
+
},
|
|
401
609
|
image_path: {
|
|
402
610
|
type: "string",
|
|
403
611
|
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,8 +614,8 @@ var init_generate = __esm({
|
|
|
406
614
|
required: ["prompt"]
|
|
407
615
|
};
|
|
408
616
|
generateSkill = {
|
|
409
|
-
name: "
|
|
410
|
-
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
|
|
617
|
+
name: "pixeldojo_generate",
|
|
618
|
+
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 flows: pass `image_url` for a still, `video_url` for a source clip, or `image_path` (absolute local path) and the skill uploads it to 24h temp storage automatically. A clip or an audio reference routes to an engine that can read one. Model-specific settings go in `params` alongside an explicit `model`.",
|
|
411
619
|
inputZod: InputSchema,
|
|
412
620
|
inputJsonSchema: InputJsonSchema,
|
|
413
621
|
allowedModels: null,
|
|
@@ -474,9 +682,12 @@ async function buildBody(apiId, input, ctx) {
|
|
|
474
682
|
}
|
|
475
683
|
body.prompt = input.prompt;
|
|
476
684
|
body[refs.key] = refs.value;
|
|
685
|
+
if (refs.also) Object.assign(body, refs.also);
|
|
477
686
|
return { body, dropped: refs.dropped };
|
|
478
687
|
}
|
|
479
688
|
async function run2(input, ctx) {
|
|
689
|
+
const paramsGate = paramsWithoutModel(input.params, input.model);
|
|
690
|
+
if (paramsGate) return { kind: "text", text: paramsGate };
|
|
480
691
|
let resolved = input;
|
|
481
692
|
try {
|
|
482
693
|
if (input.image_paths && input.image_paths.length > 0) {
|
|
@@ -491,14 +702,14 @@ async function run2(input, ctx) {
|
|
|
491
702
|
}
|
|
492
703
|
} catch (err) {
|
|
493
704
|
if (err instanceof SkillUploadError) {
|
|
494
|
-
return { kind: "text", text: `
|
|
705
|
+
return { kind: "text", text: `pixeldojo_edit (upload): ${err.userMessage}` };
|
|
495
706
|
}
|
|
496
707
|
throw err;
|
|
497
708
|
}
|
|
498
709
|
if (!resolved.image_url && (!resolved.image_urls || resolved.image_urls.length === 0)) {
|
|
499
710
|
return {
|
|
500
711
|
kind: "text",
|
|
501
|
-
text: "
|
|
712
|
+
text: "pixeldojo_edit requires at least one reference image. Pass image_url / image_urls (hosted URL) or image_path / image_paths (local file)."
|
|
502
713
|
};
|
|
503
714
|
}
|
|
504
715
|
const apiId = resolved.model ?? DEFAULT_MODEL2;
|
|
@@ -508,6 +719,7 @@ async function run2(input, ctx) {
|
|
|
508
719
|
};
|
|
509
720
|
const { body, dropped } = await buildBody(apiId, resolved, ctx);
|
|
510
721
|
const droppedNote = dropped > 0 ? ` (${dropped} reference${dropped === 1 ? "" : "s"} dropped \u2014 ${apiId} accepts fewer)` : "";
|
|
722
|
+
const paramsNote = applyParams(body, input.params, input.model, apiId);
|
|
511
723
|
const submitted = await ctx.client.run(apiId, body);
|
|
512
724
|
const result = await pollJobUntilDoneOrHandoff(
|
|
513
725
|
submitted.jobId,
|
|
@@ -518,9 +730,12 @@ async function run2(input, ctx) {
|
|
|
518
730
|
sleep: ctx.pollOverrides?.sleep
|
|
519
731
|
}
|
|
520
732
|
);
|
|
521
|
-
return
|
|
522
|
-
|
|
523
|
-
|
|
733
|
+
return prefixNote(
|
|
734
|
+
pollResultToSkillResult(
|
|
735
|
+
result,
|
|
736
|
+
(urls) => urls.length === 0 ? `Edit job ${submitted.jobId} completed but returned no assets.` : urls.length === 1 ? `Edit complete${droppedNote}. Asset: ${urls[0]}` : `Edit produced ${urls.length} assets${droppedNote}. URLs: ${urls.join(", ")}`
|
|
737
|
+
),
|
|
738
|
+
paramsNote
|
|
524
739
|
);
|
|
525
740
|
}
|
|
526
741
|
var DEFAULT_MODEL2, ALLOWED_MODELS, InputSchema2, InputJsonSchema2, editSkill;
|
|
@@ -530,6 +745,7 @@ var init_edit = __esm({
|
|
|
530
745
|
init_types();
|
|
531
746
|
init_upload_helpers();
|
|
532
747
|
init_schema_snap();
|
|
748
|
+
init_params();
|
|
533
749
|
DEFAULT_MODEL2 = "wan-2.7-image-edit";
|
|
534
750
|
ALLOWED_MODELS = [
|
|
535
751
|
"wan-2.7-image-edit",
|
|
@@ -553,7 +769,8 @@ var init_edit = __esm({
|
|
|
553
769
|
model: z.enum(ALLOWED_MODELS).optional().describe(
|
|
554
770
|
"Override the default edit model. Defaults to wan-2.7-image-edit (most popular)."
|
|
555
771
|
),
|
|
556
|
-
aspect_ratio: z.string().optional().describe('Aspect ratio when supported (e.g. "1:1", "16:9", "match_input_image").')
|
|
772
|
+
aspect_ratio: z.string().optional().describe('Aspect ratio when supported (e.g. "1:1", "16:9", "match_input_image").'),
|
|
773
|
+
params: ParamsInput
|
|
557
774
|
});
|
|
558
775
|
InputJsonSchema2 = {
|
|
559
776
|
type: "object",
|
|
@@ -587,12 +804,13 @@ var init_edit = __esm({
|
|
|
587
804
|
enum: [...ALLOWED_MODELS],
|
|
588
805
|
description: "Override edit model. Default: wan-2.7-image-edit."
|
|
589
806
|
},
|
|
590
|
-
aspect_ratio: { type: "string" }
|
|
807
|
+
aspect_ratio: { type: "string" },
|
|
808
|
+
params: PARAMS_JSON_SCHEMA
|
|
591
809
|
},
|
|
592
810
|
required: ["prompt"]
|
|
593
811
|
};
|
|
594
812
|
editSkill = {
|
|
595
|
-
name: "
|
|
813
|
+
name: "pixeldojo_edit",
|
|
596
814
|
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
815
|
inputZod: InputSchema2,
|
|
598
816
|
inputJsonSchema: InputJsonSchema2,
|
|
@@ -603,32 +821,50 @@ var init_edit = __esm({
|
|
|
603
821
|
}
|
|
604
822
|
});
|
|
605
823
|
async function run3(input, ctx) {
|
|
824
|
+
const paramsGate = paramsWithoutModel(input.params, input.model);
|
|
825
|
+
if (paramsGate) return { kind: "text", text: paramsGate };
|
|
606
826
|
const requested = input.model ?? DEFAULT_MODEL3;
|
|
607
827
|
if (!ALLOWED.includes(requested)) {
|
|
608
828
|
return {
|
|
609
829
|
kind: "text",
|
|
610
|
-
text: `Unknown model "${requested}" for
|
|
830
|
+
text: `Unknown model "${requested}" for pixeldojo_character. Allowed: ${ALLOWED.join(", ")}.`
|
|
611
831
|
};
|
|
612
832
|
}
|
|
613
833
|
const apiId = requested;
|
|
614
|
-
|
|
834
|
+
const refUrls = (input.reference_image_urls && input.reference_image_urls.length > 0 ? input.reference_image_urls : input.reference_image_url ? [input.reference_image_url] : []).filter((u, i, all) => all.indexOf(u) === i);
|
|
835
|
+
const wrongKind = refUrls.find((u) => isVideoUrl(u) || isAudioUrl(u));
|
|
836
|
+
if (wrongKind) {
|
|
837
|
+
return {
|
|
838
|
+
kind: "text",
|
|
839
|
+
text: `pixeldojo_character works on still images, and ${wrongKind} is not one. Use pixeldojo_generate or pixeldojo_storyboard with video_url to work from a clip (or image_url for an audio track \u2014 they route to the reference engine). For this treatment, give me a still frame.`
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
if (REQUIRES_REFERENCE.has(apiId) && refUrls.length === 0) {
|
|
615
843
|
return {
|
|
616
844
|
kind: "text",
|
|
617
|
-
text: `
|
|
845
|
+
text: `pixeldojo_character with model="${apiId}" requires a reference image (reference_image_url / reference_image_urls \u2014 the source image of the character to edit). For generation from a prompt alone, use model="${DEFAULT_MODEL3}".`
|
|
618
846
|
};
|
|
619
847
|
}
|
|
620
848
|
const body = { prompt: input.prompt };
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
849
|
+
let dropped = 0;
|
|
850
|
+
if (refUrls.length > 0) {
|
|
851
|
+
const refs = await resolveImageRefs(apiId, refUrls, ctx.client);
|
|
852
|
+
if (refs) {
|
|
853
|
+
body[refs.key] = refs.value;
|
|
854
|
+
if (refs.also) Object.assign(body, refs.also);
|
|
855
|
+
dropped = refs.dropped;
|
|
624
856
|
} else {
|
|
625
|
-
body
|
|
857
|
+
body[FALLBACK_REF_KEY[apiId]] = refUrls;
|
|
626
858
|
}
|
|
627
859
|
}
|
|
628
860
|
const snappedAspect = await snapAspectRatio(apiId, input.aspect_ratio, ctx.client, {
|
|
629
861
|
onUnknown: "omit"
|
|
630
862
|
});
|
|
631
863
|
if (snappedAspect !== void 0) body.aspect_ratio = snappedAspect;
|
|
864
|
+
const note = [
|
|
865
|
+
dropped > 0 ? `${dropped} reference${dropped === 1 ? "" : "s"} dropped \u2014 ${apiId} accepts fewer.` : void 0,
|
|
866
|
+
applyParams(body, input.params, input.model, apiId)
|
|
867
|
+
].filter(Boolean).join(" ");
|
|
632
868
|
const submitted = await ctx.client.run(apiId, body);
|
|
633
869
|
const result = await pollJobUntilDoneOrHandoff(
|
|
634
870
|
submitted.jobId,
|
|
@@ -639,27 +875,49 @@ async function run3(input, ctx) {
|
|
|
639
875
|
sleep: ctx.pollOverrides?.sleep
|
|
640
876
|
}
|
|
641
877
|
);
|
|
642
|
-
return
|
|
643
|
-
|
|
644
|
-
|
|
878
|
+
return prefixNote(
|
|
879
|
+
pollResultToSkillResult(
|
|
880
|
+
result,
|
|
881
|
+
(urls) => urls.length === 0 ? `Job ${submitted.jobId} completed but returned no assets.` : `Character shot ready. Asset: ${urls[0]}`
|
|
882
|
+
),
|
|
883
|
+
note
|
|
645
884
|
);
|
|
646
885
|
}
|
|
647
|
-
var ALLOWED, DEFAULT_MODEL3, REQUIRES_REFERENCE, InputSchema3, InputJsonSchema3, characterSkill;
|
|
886
|
+
var ALLOWED, DEFAULT_MODEL3, MAX_REFERENCES, REQUIRES_REFERENCE, FALLBACK_REF_KEY, InputSchema3, InputJsonSchema3, characterSkill;
|
|
648
887
|
var init_character = __esm({
|
|
649
888
|
"src/skills/character.ts"() {
|
|
650
889
|
init_poll();
|
|
651
890
|
init_types();
|
|
652
891
|
init_schema_snap();
|
|
653
|
-
|
|
892
|
+
init_params();
|
|
893
|
+
ALLOWED = [
|
|
894
|
+
"seedream-5",
|
|
895
|
+
"seedream-4",
|
|
896
|
+
"google-nano-banana",
|
|
897
|
+
"wan-2.7-image-edit",
|
|
898
|
+
"qwen-image-2.0-pro"
|
|
899
|
+
];
|
|
654
900
|
DEFAULT_MODEL3 = "seedream-5";
|
|
655
|
-
|
|
901
|
+
MAX_REFERENCES = 9;
|
|
902
|
+
REQUIRES_REFERENCE = /* @__PURE__ */ new Set(["wan-2.7-image-edit"]);
|
|
903
|
+
FALLBACK_REF_KEY = {
|
|
904
|
+
"seedream-5": "image_urls",
|
|
905
|
+
"seedream-4": "image_urls",
|
|
906
|
+
"google-nano-banana": "image_input",
|
|
907
|
+
"wan-2.7-image-edit": "image_urls",
|
|
908
|
+
"qwen-image-2.0-pro": "reference_images"
|
|
909
|
+
};
|
|
656
910
|
InputSchema3 = z.object({
|
|
657
911
|
prompt: z.string().min(1).max(8e3),
|
|
658
912
|
model: z.string().optional().describe(`One of: ${ALLOWED.join(", ")}. Defaults to ${DEFAULT_MODEL3}.`),
|
|
659
913
|
reference_image_url: z.string().url().optional().describe(
|
|
660
|
-
"
|
|
914
|
+
"Single reference image of the character. Prefer reference_image_urls \u2014 more angles lock the face better."
|
|
661
915
|
),
|
|
662
|
-
|
|
916
|
+
reference_image_urls: z.array(z.string().url()).min(1).max(MAX_REFERENCES).optional().describe(
|
|
917
|
+
`All references for this shot (1-${MAX_REFERENCES}), most important first. Multiple angles of the same person lock the face far better than one photo. Models that take fewer are clamped to their own cap.`
|
|
918
|
+
),
|
|
919
|
+
aspect_ratio: z.string().optional().describe("Honored where the model declares the field."),
|
|
920
|
+
params: ParamsInput
|
|
663
921
|
});
|
|
664
922
|
InputJsonSchema3 = {
|
|
665
923
|
type: "object",
|
|
@@ -670,14 +928,26 @@ var init_character = __esm({
|
|
|
670
928
|
enum: ALLOWED,
|
|
671
929
|
description: `One of: ${ALLOWED.join(", ")}. Defaults to ${DEFAULT_MODEL3}.`
|
|
672
930
|
},
|
|
673
|
-
reference_image_url: {
|
|
674
|
-
|
|
931
|
+
reference_image_url: {
|
|
932
|
+
type: "string",
|
|
933
|
+
format: "uri",
|
|
934
|
+
description: "Single reference. Prefer reference_image_urls."
|
|
935
|
+
},
|
|
936
|
+
reference_image_urls: {
|
|
937
|
+
type: "array",
|
|
938
|
+
items: { type: "string", format: "uri" },
|
|
939
|
+
minItems: 1,
|
|
940
|
+
maxItems: MAX_REFERENCES,
|
|
941
|
+
description: "All references for this shot, most important first. Multiple angles of the same person lock the face far better than one photo."
|
|
942
|
+
},
|
|
943
|
+
aspect_ratio: { type: "string" },
|
|
944
|
+
params: PARAMS_JSON_SCHEMA
|
|
675
945
|
},
|
|
676
946
|
required: ["prompt"]
|
|
677
947
|
};
|
|
678
948
|
characterSkill = {
|
|
679
|
-
name: "
|
|
680
|
-
description: "Generate or edit consistent characters across shots. Pass
|
|
949
|
+
name: "pixeldojo_character",
|
|
950
|
+
description: "Generate or edit consistent characters across shots. Pass reference_image_urls (up to 9) to lock identity \u2014 send every photo of the person you have, since more angles hold the face far better than one. Defaults to seedream-5.",
|
|
681
951
|
inputZod: InputSchema3,
|
|
682
952
|
inputJsonSchema: InputJsonSchema3,
|
|
683
953
|
allowedModels: [...ALLOWED],
|
|
@@ -687,13 +957,29 @@ var init_character = __esm({
|
|
|
687
957
|
}
|
|
688
958
|
});
|
|
689
959
|
async function run4(input, ctx) {
|
|
960
|
+
const paramsGate = paramsWithoutModel(input.params, input.model);
|
|
961
|
+
if (paramsGate) return { kind: "text", text: paramsGate };
|
|
690
962
|
const requested = input.model ?? DEFAULT_MODEL4;
|
|
691
963
|
if (!ALLOWED2.includes(requested)) {
|
|
692
964
|
return {
|
|
693
965
|
kind: "text",
|
|
694
|
-
text: `Unknown model "${requested}" for
|
|
966
|
+
text: `Unknown model "${requested}" for pixeldojo_storyboard. Allowed: ${ALLOWED2.join(", ")}.`
|
|
695
967
|
};
|
|
696
968
|
}
|
|
969
|
+
if (input.image_url || input.video_url) {
|
|
970
|
+
return generateSkill.run(
|
|
971
|
+
{
|
|
972
|
+
prompt: input.brief,
|
|
973
|
+
model: input.model ?? VIDEO_REFERENCE_MODEL,
|
|
974
|
+
image_url: input.image_url,
|
|
975
|
+
video_url: input.video_url,
|
|
976
|
+
aspect_ratio: input.aspect_ratio,
|
|
977
|
+
duration: input.duration,
|
|
978
|
+
params: input.params
|
|
979
|
+
},
|
|
980
|
+
ctx
|
|
981
|
+
);
|
|
982
|
+
}
|
|
697
983
|
const apiId = requested;
|
|
698
984
|
const [snappedAspect, snappedDuration] = await Promise.all([
|
|
699
985
|
snapAspectRatio(apiId, input.aspect_ratio, ctx.client),
|
|
@@ -702,6 +988,7 @@ async function run4(input, ctx) {
|
|
|
702
988
|
const body = { prompt: input.brief };
|
|
703
989
|
if (snappedAspect !== void 0) body.aspect_ratio = snappedAspect;
|
|
704
990
|
if (snappedDuration !== void 0) body.duration = snappedDuration;
|
|
991
|
+
const note = applyParams(body, input.params, input.model, apiId);
|
|
705
992
|
const submitted = await ctx.client.run(apiId, body);
|
|
706
993
|
const result = await pollJobUntilDoneOrHandoff(
|
|
707
994
|
submitted.jobId,
|
|
@@ -712,9 +999,12 @@ async function run4(input, ctx) {
|
|
|
712
999
|
sleep: ctx.pollOverrides?.sleep
|
|
713
1000
|
}
|
|
714
1001
|
);
|
|
715
|
-
return
|
|
716
|
-
|
|
717
|
-
|
|
1002
|
+
return prefixNote(
|
|
1003
|
+
pollResultToSkillResult(
|
|
1004
|
+
result,
|
|
1005
|
+
(urls) => urls.length === 0 ? `Job ${submitted.jobId} completed but returned no assets.` : `Storyboard preview ready. Asset: ${urls[0]}`
|
|
1006
|
+
),
|
|
1007
|
+
note
|
|
718
1008
|
);
|
|
719
1009
|
}
|
|
720
1010
|
var ALLOWED2, DEFAULT_MODEL4, InputSchema4, InputJsonSchema4, storyboardSkill;
|
|
@@ -723,18 +1013,24 @@ var init_storyboard = __esm({
|
|
|
723
1013
|
init_poll();
|
|
724
1014
|
init_types();
|
|
725
1015
|
init_schema_snap();
|
|
1016
|
+
init_generate();
|
|
1017
|
+
init_params();
|
|
726
1018
|
ALLOWED2 = [
|
|
727
1019
|
"veo-3.1-fast",
|
|
728
1020
|
"veo-3.1-standard",
|
|
729
1021
|
"kling-video-v3-standard-text",
|
|
730
|
-
"seedance-2-high"
|
|
1022
|
+
"seedance-2-high",
|
|
1023
|
+
VIDEO_REFERENCE_MODEL
|
|
731
1024
|
];
|
|
732
1025
|
DEFAULT_MODEL4 = "veo-3.1-fast";
|
|
733
1026
|
InputSchema4 = z.object({
|
|
734
1027
|
brief: z.string().min(1).max(8e3).describe("Storyboard brief: scene, shots, mood, action."),
|
|
735
1028
|
model: z.string().optional().describe(`One of: ${ALLOWED2.join(", ")}. Defaults to ${DEFAULT_MODEL4}.`),
|
|
736
1029
|
aspect_ratio: z.string().optional(),
|
|
737
|
-
duration: z.number().int().positive().optional()
|
|
1030
|
+
duration: z.number().int().positive().optional(),
|
|
1031
|
+
image_url: z.string().url().optional().describe("Reference still to build the shot from (character, product, location)."),
|
|
1032
|
+
video_url: z.string().url().optional().describe("Source clip to work from. Use this for videos; image_url is for still frames."),
|
|
1033
|
+
params: ParamsInput
|
|
738
1034
|
});
|
|
739
1035
|
InputJsonSchema4 = {
|
|
740
1036
|
type: "object",
|
|
@@ -749,13 +1045,24 @@ var init_storyboard = __esm({
|
|
|
749
1045
|
description: `One of: ${ALLOWED2.join(", ")}. Defaults to ${DEFAULT_MODEL4}.`
|
|
750
1046
|
},
|
|
751
1047
|
aspect_ratio: { type: "string" },
|
|
752
|
-
duration: { type: "integer" }
|
|
1048
|
+
duration: { type: "integer" },
|
|
1049
|
+
image_url: {
|
|
1050
|
+
type: "string",
|
|
1051
|
+
format: "uri",
|
|
1052
|
+
description: "Reference still to build the shot from."
|
|
1053
|
+
},
|
|
1054
|
+
video_url: {
|
|
1055
|
+
type: "string",
|
|
1056
|
+
format: "uri",
|
|
1057
|
+
description: "Source clip to work from. Use this for videos; image_url is for stills."
|
|
1058
|
+
},
|
|
1059
|
+
params: PARAMS_JSON_SCHEMA
|
|
753
1060
|
},
|
|
754
1061
|
required: ["brief"]
|
|
755
1062
|
};
|
|
756
1063
|
storyboardSkill = {
|
|
757
|
-
name: "
|
|
758
|
-
description: "Generate a video storyboard preview from a single brief. v1: single-shot preview; multi-shot orchestration coming.",
|
|
1064
|
+
name: "pixeldojo_storyboard",
|
|
1065
|
+
description: "Generate a video storyboard preview from a single brief. Pass image_url for a reference still or video_url for a source clip. v1: single-shot preview; multi-shot orchestration coming.",
|
|
759
1066
|
inputZod: InputSchema4,
|
|
760
1067
|
inputJsonSchema: InputJsonSchema4,
|
|
761
1068
|
allowedModels: [...ALLOWED2],
|
|
@@ -769,7 +1076,7 @@ async function run5(input, ctx) {
|
|
|
769
1076
|
if (!ALLOWED3.includes(requested)) {
|
|
770
1077
|
return {
|
|
771
1078
|
kind: "text",
|
|
772
|
-
text: `Unknown model "${requested}" for
|
|
1079
|
+
text: `Unknown model "${requested}" for pixeldojo_upscale. Allowed: ${ALLOWED3.join(", ")}.`
|
|
773
1080
|
};
|
|
774
1081
|
}
|
|
775
1082
|
const apiId = requested;
|
|
@@ -831,7 +1138,7 @@ var init_upscale = __esm({
|
|
|
831
1138
|
required: ["image_url"]
|
|
832
1139
|
};
|
|
833
1140
|
upscaleSkill = {
|
|
834
|
-
name: "
|
|
1141
|
+
name: "pixeldojo_upscale",
|
|
835
1142
|
description: "Enhance an image. Magnific (default) supports configurable scale_factor and creativity; face-enhance for portraits.",
|
|
836
1143
|
inputZod: InputSchema5,
|
|
837
1144
|
inputJsonSchema: InputJsonSchema5,
|
|
@@ -909,14 +1216,14 @@ var InputSchema6, InputJsonSchema6, STUCK_HINT_MS, ABANDON_HINT_MS, statusSkill;
|
|
|
909
1216
|
var init_status = __esm({
|
|
910
1217
|
"src/skills/status.ts"() {
|
|
911
1218
|
InputSchema6 = z.object({
|
|
912
|
-
jobId: z.string().min(1).max(200).describe("Job id returned from a prior
|
|
1219
|
+
jobId: z.string().min(1).max(200).describe("Job id returned from a prior pixeldojo_* skill handoff.")
|
|
913
1220
|
});
|
|
914
1221
|
InputJsonSchema6 = {
|
|
915
1222
|
type: "object",
|
|
916
1223
|
properties: {
|
|
917
1224
|
jobId: {
|
|
918
1225
|
type: "string",
|
|
919
|
-
description: "Job id returned from a prior
|
|
1226
|
+
description: "Job id returned from a prior pixeldojo_* skill handoff."
|
|
920
1227
|
}
|
|
921
1228
|
},
|
|
922
1229
|
required: ["jobId"]
|
|
@@ -924,7 +1231,7 @@ var init_status = __esm({
|
|
|
924
1231
|
STUCK_HINT_MS = 5 * 60 * 1e3;
|
|
925
1232
|
ABANDON_HINT_MS = 12 * 60 * 1e3;
|
|
926
1233
|
statusSkill = {
|
|
927
|
-
name: "
|
|
1234
|
+
name: "pixeldojo_status",
|
|
928
1235
|
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
1236
|
inputZod: InputSchema6,
|
|
930
1237
|
inputJsonSchema: InputJsonSchema6,
|
|
@@ -969,8 +1276,8 @@ var init_from_url = __esm({
|
|
|
969
1276
|
required: ["url"]
|
|
970
1277
|
};
|
|
971
1278
|
fromUrlSkill = {
|
|
972
|
-
name: "
|
|
973
|
-
description: "Extract product info (name, description, images) from any product page URL. Use this as the first step before
|
|
1279
|
+
name: "pixeldojo_from_url",
|
|
1280
|
+
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
1281
|
inputZod: InputSchema7,
|
|
975
1282
|
inputJsonSchema: InputJsonSchema7,
|
|
976
1283
|
// No model dispatch — this skill calls a server-side scrape, not a generation.
|
|
@@ -1031,7 +1338,7 @@ async function plan(input, ctx) {
|
|
|
1031
1338
|
...sceneLines,
|
|
1032
1339
|
"",
|
|
1033
1340
|
`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
|
|
1341
|
+
`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
1342
|
].join("\n")
|
|
1036
1343
|
};
|
|
1037
1344
|
}
|
|
@@ -1046,7 +1353,7 @@ async function produce(filmId, ctx) {
|
|
|
1046
1353
|
}
|
|
1047
1354
|
return {
|
|
1048
1355
|
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
|
|
1356
|
+
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
1357
|
};
|
|
1051
1358
|
}
|
|
1052
1359
|
function buildScorePlanBody(film) {
|
|
@@ -1175,13 +1482,13 @@ async function run8(input, ctx) {
|
|
|
1175
1482
|
var LOOKS, InputSchema8, InputJsonSchema8, shots, count, LOOK_MUSIC, ARC, filmSkill;
|
|
1176
1483
|
var init_film = __esm({
|
|
1177
1484
|
"src/skills/film.ts"() {
|
|
1178
|
-
LOOKS = ["live-action", "anime", "3d-animation", "noir", "horror", "sci-fi", "documentary", "western"];
|
|
1485
|
+
LOOKS = ["live-action", "anime", "3d-animation", "noir", "horror", "sci-fi", "documentary", "western", "romance", "romantic-comedy", "comedy"];
|
|
1179
1486
|
InputSchema8 = z.object({
|
|
1180
1487
|
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
1488
|
script: z.string().max(2e4).optional().describe("A full screenplay to plan instead of an idea (sluglines, action lines, dialogue)."),
|
|
1182
1489
|
look: z.enum(LOOKS).default("live-action").describe("Visual style carried through frames, clips, and soundtrack."),
|
|
1183
1490
|
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)."),
|
|
1491
|
+
length: z.enum(["30s", "60s", "2-3min", "5min"]).default("60s").describe("Target length; drives the shot count (~6/12/24/60 shots)."),
|
|
1185
1492
|
film_id: z.string().max(40).optional().describe("Existing film id (from a previous call) for produce/status."),
|
|
1186
1493
|
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
1494
|
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 +1501,7 @@ var init_film = __esm({
|
|
|
1194
1501
|
script: { type: "string", description: "A full screenplay to plan instead of an idea." },
|
|
1195
1502
|
look: { type: "string", enum: [...LOOKS], description: "Visual style. Default live-action." },
|
|
1196
1503
|
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." },
|
|
1504
|
+
length: { type: "string", enum: ["30s", "60s", "2-3min", "5min"], description: "Target length (~6/12/24/60 shots). Default 60s." },
|
|
1198
1505
|
film_id: { type: "string", description: "Existing film id for produce/status." },
|
|
1199
1506
|
action: { type: "string", enum: ["plan", "produce", "status"], description: "plan (free) | produce (charged) | status (poll every ~30s)." },
|
|
1200
1507
|
soundtrack: { type: "string", enum: ["scene-synced", "flat", "none"], description: "Default scene-synced." },
|
|
@@ -1211,7 +1518,10 @@ var init_film = __esm({
|
|
|
1211
1518
|
horror: "sparse horror underscore, low cello drones, dissonant string swells, creeping heartbeat pulse, unsettling quiet",
|
|
1212
1519
|
"sci-fi": "atmospheric science-fiction score, analog synth pads, deep sub-bass pulses, shimmering arpeggios, vast and cold",
|
|
1213
1520
|
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"
|
|
1521
|
+
western: "cinematic western score, lonely twangy guitar, dusty strings, distant horns, wide-open desert tension",
|
|
1522
|
+
romance: "tender romantic score, felt piano and warm strings, soft acoustic guitar, intimate and unhurried, gently swelling",
|
|
1523
|
+
"romantic-comedy": "bright romantic-comedy score, plucky ukulele and handclaps, warm brushed drums, sunny and flirtatious",
|
|
1524
|
+
comedy: "playful comedy score, bouncy pizzicato strings, cheeky woodwinds, light percussion hits punctuating the gags"
|
|
1215
1525
|
};
|
|
1216
1526
|
ARC = {
|
|
1217
1527
|
opening: ["establishing the theme", "restrained", "quiet anticipation"],
|
|
@@ -1219,7 +1529,7 @@ var init_film = __esm({
|
|
|
1219
1529
|
finale: ["climactic", "full arrangement", "emotional peak resolving to stillness"]
|
|
1220
1530
|
};
|
|
1221
1531
|
filmSkill = {
|
|
1222
|
-
name: "
|
|
1532
|
+
name: "pixeldojo_film",
|
|
1223
1533
|
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
1534
|
inputZod: InputSchema8,
|
|
1225
1535
|
inputJsonSchema: InputJsonSchema8,
|
|
@@ -1358,8 +1668,8 @@ var init_ad = __esm({
|
|
|
1358
1668
|
// Zod refine; no root-level oneOf (Grok rejects non-object root unions).
|
|
1359
1669
|
};
|
|
1360
1670
|
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
|
|
1671
|
+
name: "pixeldojo_ad",
|
|
1672
|
+
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
1673
|
inputZod: InputSchema9,
|
|
1364
1674
|
inputJsonSchema: InputJsonSchema9,
|
|
1365
1675
|
// Composition skill — the server routes to the ad engine internally.
|
|
@@ -1391,7 +1701,7 @@ async function run10(input, ctx) {
|
|
|
1391
1701
|
}
|
|
1392
1702
|
return {
|
|
1393
1703
|
kind: "handoff",
|
|
1394
|
-
text: result.message ?? `Campaign ${result.campaignId} queued. Poll via
|
|
1704
|
+
text: result.message ?? `Campaign ${result.campaignId} queued. Poll via pixeldojo_campaign_status.`,
|
|
1395
1705
|
jobId: result.campaignId,
|
|
1396
1706
|
statusUrl: result.statusUrl ?? ctx.buildStatusUrl(result.campaignId)
|
|
1397
1707
|
};
|
|
@@ -1444,8 +1754,8 @@ var init_campaign = __esm({
|
|
|
1444
1754
|
oneOf: [{ required: ["productUrl"] }, { required: ["product"] }]
|
|
1445
1755
|
};
|
|
1446
1756
|
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
|
|
1757
|
+
name: "pixeldojo_campaign",
|
|
1758
|
+
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
1759
|
inputZod: InputSchema10,
|
|
1450
1760
|
inputJsonSchema: InputJsonSchema10,
|
|
1451
1761
|
// Composition skill — internal sub-jobs route to per-tool models, no
|
|
@@ -1490,21 +1800,21 @@ var InputSchema11, InputJsonSchema11, campaignStatusSkill;
|
|
|
1490
1800
|
var init_campaign_status = __esm({
|
|
1491
1801
|
"src/skills/campaign-status.ts"() {
|
|
1492
1802
|
InputSchema11 = z.object({
|
|
1493
|
-
campaignId: z.string().min(1).max(200).describe("Campaign id returned from a prior
|
|
1803
|
+
campaignId: z.string().min(1).max(200).describe("Campaign id returned from a prior pixeldojo_campaign handoff.")
|
|
1494
1804
|
});
|
|
1495
1805
|
InputJsonSchema11 = {
|
|
1496
1806
|
type: "object",
|
|
1497
1807
|
properties: {
|
|
1498
1808
|
campaignId: {
|
|
1499
1809
|
type: "string",
|
|
1500
|
-
description: "Campaign id returned from a prior
|
|
1810
|
+
description: "Campaign id returned from a prior pixeldojo_campaign handoff."
|
|
1501
1811
|
}
|
|
1502
1812
|
},
|
|
1503
1813
|
required: ["campaignId"]
|
|
1504
1814
|
};
|
|
1505
1815
|
campaignStatusSkill = {
|
|
1506
|
-
name: "
|
|
1507
|
-
description: "Poll a campaign by campaignId returned from
|
|
1816
|
+
name: "pixeldojo_campaign_status",
|
|
1817
|
+
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
1818
|
inputZod: InputSchema11,
|
|
1509
1819
|
inputJsonSchema: InputJsonSchema11,
|
|
1510
1820
|
allowedModels: [],
|
|
@@ -1519,7 +1829,7 @@ async function run12(input, ctx) {
|
|
|
1519
1829
|
url = await uploadLocalFile(input.path, ctx.client);
|
|
1520
1830
|
} catch (err) {
|
|
1521
1831
|
if (err instanceof SkillUploadError) {
|
|
1522
|
-
return { kind: "text", text: `
|
|
1832
|
+
return { kind: "text", text: `pixeldojo_upload: ${err.userMessage}` };
|
|
1523
1833
|
}
|
|
1524
1834
|
throw err;
|
|
1525
1835
|
}
|
|
@@ -1549,7 +1859,7 @@ var init_upload = __esm({
|
|
|
1549
1859
|
required: ["path"]
|
|
1550
1860
|
};
|
|
1551
1861
|
uploadSkill = {
|
|
1552
|
-
name: "
|
|
1862
|
+
name: "pixeldojo_upload",
|
|
1553
1863
|
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
1864
|
inputZod: InputSchema12,
|
|
1555
1865
|
inputJsonSchema: InputJsonSchema12,
|
|
@@ -1564,7 +1874,7 @@ async function run13(input, ctx) {
|
|
|
1564
1874
|
if (!ALLOWED4.includes(requested)) {
|
|
1565
1875
|
return {
|
|
1566
1876
|
kind: "text",
|
|
1567
|
-
text: `Unknown model "${requested}" for
|
|
1877
|
+
text: `Unknown model "${requested}" for pixeldojo_audio. Allowed: ${ALLOWED4.join(", ")}.`
|
|
1568
1878
|
};
|
|
1569
1879
|
}
|
|
1570
1880
|
const apiId = requested;
|
|
@@ -1575,7 +1885,7 @@ async function run13(input, ctx) {
|
|
|
1575
1885
|
audioUrls = [...audioUrls, ...uploaded].slice(0, 3);
|
|
1576
1886
|
} catch (err) {
|
|
1577
1887
|
if (err instanceof SkillUploadError) {
|
|
1578
|
-
return { kind: "text", text: `
|
|
1888
|
+
return { kind: "text", text: `pixeldojo_audio (upload): ${err.userMessage}` };
|
|
1579
1889
|
}
|
|
1580
1890
|
throw err;
|
|
1581
1891
|
}
|
|
@@ -1641,7 +1951,7 @@ var init_audio = __esm({
|
|
|
1641
1951
|
required: ["prompt"]
|
|
1642
1952
|
};
|
|
1643
1953
|
audioSkill = {
|
|
1644
|
-
name: "
|
|
1954
|
+
name: "pixeldojo_audio",
|
|
1645
1955
|
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
1956
|
inputZod: InputSchema13,
|
|
1647
1957
|
inputJsonSchema: InputJsonSchema13,
|
|
@@ -1661,7 +1971,7 @@ async function run14(input, ctx) {
|
|
|
1661
1971
|
if (res.items.length === 0) {
|
|
1662
1972
|
return {
|
|
1663
1973
|
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
|
|
1974
|
+
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
1975
|
};
|
|
1666
1976
|
}
|
|
1667
1977
|
const lines = res.items.map((it) => {
|
|
@@ -1702,8 +2012,8 @@ var init_library = __esm({
|
|
|
1702
2012
|
required: []
|
|
1703
2013
|
};
|
|
1704
2014
|
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
|
|
2015
|
+
name: "pixeldojo_library",
|
|
2016
|
+
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
2017
|
inputZod: InputSchema14,
|
|
1708
2018
|
inputJsonSchema: InputJsonSchema14,
|
|
1709
2019
|
allowedModels: [],
|
|
@@ -1723,7 +2033,7 @@ async function run15(input, ctx) {
|
|
|
1723
2033
|
return {
|
|
1724
2034
|
kind: "text",
|
|
1725
2035
|
text: `Saved to My Media (permanent): ${res.url}
|
|
1726
|
-
It will appear at https://pixeldojo.ai/my-images and in
|
|
2036
|
+
It will appear at https://pixeldojo.ai/my-images and in pixeldojo_library searches.`
|
|
1727
2037
|
};
|
|
1728
2038
|
}
|
|
1729
2039
|
var InputSchema15, InputJsonSchema15, saveSkill;
|
|
@@ -1748,7 +2058,7 @@ var init_save = __esm({
|
|
|
1748
2058
|
required: ["url"]
|
|
1749
2059
|
};
|
|
1750
2060
|
saveSkill = {
|
|
1751
|
-
name: "
|
|
2061
|
+
name: "pixeldojo_save",
|
|
1752
2062
|
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
2063
|
inputZod: InputSchema15,
|
|
1754
2064
|
inputJsonSchema: InputJsonSchema15,
|
|
@@ -1796,7 +2106,7 @@ async function runSave(input, ctx) {
|
|
|
1796
2106
|
});
|
|
1797
2107
|
return {
|
|
1798
2108
|
kind: "text",
|
|
1799
|
-
text: res.created ? `Saved workflow "${res.name}" (${res.steps} step${res.steps === 1 ? "" : "s"}). Run it with
|
|
2109
|
+
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
2110
|
};
|
|
1801
2111
|
}
|
|
1802
2112
|
async function runList(_input, ctx) {
|
|
@@ -1804,7 +2114,7 @@ async function runList(_input, ctx) {
|
|
|
1804
2114
|
if (res.count === 0) {
|
|
1805
2115
|
return {
|
|
1806
2116
|
kind: "text",
|
|
1807
|
-
text: "No saved workflows yet. Save one with
|
|
2117
|
+
text: "No saved workflows yet. Save one with pixeldojo_save_workflow { name, steps: [{ skill, args }] }."
|
|
1808
2118
|
};
|
|
1809
2119
|
}
|
|
1810
2120
|
const lines = res.workflows.map(
|
|
@@ -1873,7 +2183,7 @@ async function runWorkflow(input, ctx) {
|
|
|
1873
2183
|
} else if (result.kind === "handoff") {
|
|
1874
2184
|
return {
|
|
1875
2185
|
kind: "text",
|
|
1876
|
-
text: `Workflow "${workflow.name}" is still on step ${i + 1} (${step.skill}) after an extended wait. Poll with
|
|
2186
|
+
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
2187
|
};
|
|
1878
2188
|
} else {
|
|
1879
2189
|
outputs.push(`step ${i + 1} (${step.skill}): done (no asset URL)`);
|
|
@@ -1892,7 +2202,7 @@ var init_workflows = __esm({
|
|
|
1892
2202
|
init_types();
|
|
1893
2203
|
WORKFLOW_STEP_EXTRA_BUDGET_MS = 6e4;
|
|
1894
2204
|
StepSchema = z.object({
|
|
1895
|
-
skill: z.string().regex(/^pixeldojo:(generate|edit|character|storyboard|upscale|audio|upload|from_url)$/, {
|
|
2205
|
+
skill: z.string().regex(/^pixeldojo[:_](generate|edit|character|storyboard|upscale|audio|upload|from_url)$/, {
|
|
1896
2206
|
message: "Runnable skills only: generate, edit, character, storyboard, upscale, audio, upload, from_url."
|
|
1897
2207
|
}),
|
|
1898
2208
|
args: z.record(z.string(), z.unknown()).optional()
|
|
@@ -1918,7 +2228,7 @@ var init_workflows = __esm({
|
|
|
1918
2228
|
required: ["skill"]
|
|
1919
2229
|
};
|
|
1920
2230
|
saveWorkflowSkill = {
|
|
1921
|
-
name: "
|
|
2231
|
+
name: "pixeldojo_save_workflow",
|
|
1922
2232
|
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
2233
|
inputZod: SaveInputSchema,
|
|
1924
2234
|
inputJsonSchema: {
|
|
@@ -1935,7 +2245,7 @@ var init_workflows = __esm({
|
|
|
1935
2245
|
run: runSave
|
|
1936
2246
|
};
|
|
1937
2247
|
runWorkflowSkill = {
|
|
1938
|
-
name: "
|
|
2248
|
+
name: "pixeldojo_run_workflow",
|
|
1939
2249
|
description: "Run a saved workflow by name. Pass inputs for its {{input.<key>}} placeholders; each step feeds the next via {{prev}}.",
|
|
1940
2250
|
inputZod: RunInputSchema,
|
|
1941
2251
|
inputJsonSchema: {
|
|
@@ -1951,7 +2261,7 @@ var init_workflows = __esm({
|
|
|
1951
2261
|
run: runWorkflow
|
|
1952
2262
|
};
|
|
1953
2263
|
listWorkflowsSkill = {
|
|
1954
|
-
name: "
|
|
2264
|
+
name: "pixeldojo_list_workflows",
|
|
1955
2265
|
description: "List your saved workflows with step counts and run totals.",
|
|
1956
2266
|
inputZod: ListInputSchema,
|
|
1957
2267
|
inputJsonSchema: { type: "object", properties: {}, required: [] },
|
|
@@ -1987,7 +2297,8 @@ __export(skills_exports, {
|
|
|
1987
2297
|
upscaleSkill: () => upscaleSkill
|
|
1988
2298
|
});
|
|
1989
2299
|
function findSkill(name) {
|
|
1990
|
-
|
|
2300
|
+
const wanted = name.replace(/^pixeldojo:/, "pixeldojo_");
|
|
2301
|
+
return ALL_SKILLS.find((s) => s.name === wanted);
|
|
1991
2302
|
}
|
|
1992
2303
|
var ALL_SKILLS;
|
|
1993
2304
|
var init_skills = __esm({
|
|
@@ -2081,7 +2392,7 @@ function makeRedactedLogger(target = console) {
|
|
|
2081
2392
|
}
|
|
2082
2393
|
|
|
2083
2394
|
// src/server.ts
|
|
2084
|
-
var PACKAGE_VERSION = "0.
|
|
2395
|
+
var PACKAGE_VERSION = "0.10.0" ;
|
|
2085
2396
|
function buildServer(options) {
|
|
2086
2397
|
if (!options.apiKey) {
|
|
2087
2398
|
throw new Error(
|
|
@@ -2248,19 +2559,19 @@ function printInit() {
|
|
|
2248
2559
|
`# Same shape as above.`,
|
|
2249
2560
|
``,
|
|
2250
2561
|
`# 3. Restart your agent. You'll see these tools:`,
|
|
2251
|
-
`#
|
|
2252
|
-
`#
|
|
2253
|
-
`#
|
|
2254
|
-
`#
|
|
2255
|
-
`#
|
|
2256
|
-
`#
|
|
2257
|
-
`#
|
|
2258
|
-
`#
|
|
2259
|
-
`#
|
|
2260
|
-
`#
|
|
2261
|
-
`#
|
|
2262
|
-
`#
|
|
2263
|
-
`#
|
|
2562
|
+
`# pixeldojo_generate Any prompt \u2192 image or video.`,
|
|
2563
|
+
`# pixeldojo_edit Change an image with a text instruction.`,
|
|
2564
|
+
`# pixeldojo_character Consistent characters across shots.`,
|
|
2565
|
+
`# pixeldojo_storyboard Multi-shot scenes from one brief.`,
|
|
2566
|
+
`# pixeldojo_upscale Enhance any image.`,
|
|
2567
|
+
`# pixeldojo_audio Voices + effects + music in one clip.`,
|
|
2568
|
+
`# pixeldojo_library Search your saved media + generations.`,
|
|
2569
|
+
`# pixeldojo_save Keep a result in My Media permanently.`,
|
|
2570
|
+
`# pixeldojo_*_workflow Save / run / list multi-step chains.`,
|
|
2571
|
+
`# pixeldojo_upload Local file \u2192 temp URL for references.`,
|
|
2572
|
+
`# pixeldojo_from_url Product URL \u2192 structured product info.`,
|
|
2573
|
+
`# pixeldojo_campaign* Product URL \u2192 full launch pack.`,
|
|
2574
|
+
`# pixeldojo_status Poll long-running jobs.`,
|
|
2264
2575
|
``
|
|
2265
2576
|
].join("\n")
|
|
2266
2577
|
);
|