@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/index.cjs
CHANGED
|
@@ -95,7 +95,7 @@ function pollResultToSkillResult(result, successText) {
|
|
|
95
95
|
}
|
|
96
96
|
return {
|
|
97
97
|
kind: "handoff",
|
|
98
|
-
text: `Job ${result.jobId} is still running after the 30s sync window. Use
|
|
98
|
+
text: `Job ${result.jobId} is still running after the 30s sync window. Use pixeldojo_status with this jobId to check completion, or follow ${result.statusUrl}.`,
|
|
99
99
|
jobId: result.jobId,
|
|
100
100
|
statusUrl: result.statusUrl
|
|
101
101
|
};
|
|
@@ -185,18 +185,23 @@ var init_upload_helpers = __esm({
|
|
|
185
185
|
});
|
|
186
186
|
|
|
187
187
|
// src/skills/schema-snap.ts
|
|
188
|
-
function
|
|
188
|
+
function getModelInfo(apiId, client) {
|
|
189
189
|
const cached = schemaCache.get(apiId);
|
|
190
190
|
if (cached) return cached;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
if (typeof client?.getModelSchema !== "function") return Promise.resolve(null);
|
|
192
|
+
const pending = Promise.resolve().then(() => client.getModelSchema(apiId)).then((res) => {
|
|
193
|
+
const body = res;
|
|
194
|
+
return { props: body.schema?.properties ?? {}, media: body.media };
|
|
195
|
+
}).catch(() => {
|
|
194
196
|
schemaCache.delete(apiId);
|
|
195
197
|
return null;
|
|
196
198
|
});
|
|
197
199
|
schemaCache.set(apiId, pending);
|
|
198
200
|
return pending;
|
|
199
201
|
}
|
|
202
|
+
async function getFieldConstraints(apiId, client) {
|
|
203
|
+
return (await getModelInfo(apiId, client))?.props ?? null;
|
|
204
|
+
}
|
|
200
205
|
function parseAspectRatio(ar) {
|
|
201
206
|
const m = /^\s*(\d+)\s*:\s*(\d+)\s*$/.exec(ar);
|
|
202
207
|
if (!m) return null;
|
|
@@ -232,6 +237,55 @@ async function snapAspectRatio(apiId, requested, client, opts = {}) {
|
|
|
232
237
|
if (!field.enum || field.enum.length === 0) return requested;
|
|
233
238
|
return nearestAspectFromEnum(requested, field.enum);
|
|
234
239
|
}
|
|
240
|
+
async function resolveMediaSlot(apiId, kind, client) {
|
|
241
|
+
const info = await getModelInfo(apiId, client);
|
|
242
|
+
if (info === null) return void 0;
|
|
243
|
+
const declared = info.media?.[kind];
|
|
244
|
+
if (declared) {
|
|
245
|
+
return {
|
|
246
|
+
name: declared.key,
|
|
247
|
+
isArray: info.props[declared.key]?.type === "array",
|
|
248
|
+
also: declared.also
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
if (info.media && kind !== "audio") return null;
|
|
252
|
+
for (const name of FALLBACK_KEYS[kind]) {
|
|
253
|
+
const field = info.props[name];
|
|
254
|
+
if (field) return { name, isArray: field.type === "array" };
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
function resolutionHeight(value) {
|
|
259
|
+
const v = value.trim().toLowerCase();
|
|
260
|
+
if (v === "4k") return 2160;
|
|
261
|
+
const n = Number.parseInt(v, 10);
|
|
262
|
+
return Number.isFinite(n) ? n : null;
|
|
263
|
+
}
|
|
264
|
+
async function snapResolution(apiId, requested, client, opts = {}) {
|
|
265
|
+
if (!requested) return void 0;
|
|
266
|
+
const props = await getFieldConstraints(apiId, client);
|
|
267
|
+
if (props === null) {
|
|
268
|
+
return opts.onUnknown === "omit" ? void 0 : requested;
|
|
269
|
+
}
|
|
270
|
+
const field = props.resolution;
|
|
271
|
+
if (!field) return void 0;
|
|
272
|
+
if (!field.enum || field.enum.length === 0) return requested;
|
|
273
|
+
if (field.enum.includes(requested)) return requested;
|
|
274
|
+
const target = resolutionHeight(requested);
|
|
275
|
+
if (target === null) return requested;
|
|
276
|
+
let best = requested;
|
|
277
|
+
let bestDiff = Infinity;
|
|
278
|
+
for (const candidate of field.enum) {
|
|
279
|
+
const h = resolutionHeight(candidate);
|
|
280
|
+
if (h === null) continue;
|
|
281
|
+
const diff = Math.abs(h - target);
|
|
282
|
+
if (diff < bestDiff) {
|
|
283
|
+
bestDiff = diff;
|
|
284
|
+
best = candidate;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return best;
|
|
288
|
+
}
|
|
235
289
|
async function snapDuration(apiId, requested, client, opts = {}) {
|
|
236
290
|
if (requested === void 0) return void 0;
|
|
237
291
|
const props = await getFieldConstraints(apiId, client);
|
|
@@ -256,24 +310,49 @@ async function snapDuration(apiId, requested, client, opts = {}) {
|
|
|
256
310
|
}
|
|
257
311
|
return requested;
|
|
258
312
|
}
|
|
313
|
+
function isVideoUrl(url) {
|
|
314
|
+
return /\.(mp4|mov|webm|m4v)(?:[?#]|$)/i.test(url);
|
|
315
|
+
}
|
|
316
|
+
function isAudioUrl(url) {
|
|
317
|
+
return /\.(mp3|wav|m4a|aac|ogg|flac)(?:[?#]|$)/i.test(url);
|
|
318
|
+
}
|
|
259
319
|
async function resolveImageRefs(apiId, urls, client) {
|
|
260
320
|
if (urls.length === 0) return null;
|
|
261
|
-
const
|
|
262
|
-
if (
|
|
263
|
-
const
|
|
321
|
+
const info = await getModelInfo(apiId, client);
|
|
322
|
+
if (info === null) return null;
|
|
323
|
+
const props = info.props;
|
|
324
|
+
const key = info.media ? info.media.image?.key : REF_KEYS.find((k) => props[k] !== void 0);
|
|
264
325
|
if (!key) return null;
|
|
265
|
-
const
|
|
326
|
+
const also = info.media?.image?.also;
|
|
327
|
+
const field = props[key] ?? {};
|
|
266
328
|
if (field.type === "array") {
|
|
267
329
|
const cap = typeof field.maxItems === "number" ? field.maxItems : urls.length;
|
|
268
330
|
const kept = urls.slice(0, Math.max(1, cap));
|
|
269
|
-
return { key, value: kept, dropped: urls.length - kept.length };
|
|
331
|
+
return { key, value: kept, dropped: urls.length - kept.length, also };
|
|
270
332
|
}
|
|
271
|
-
return { key, value: urls[0], dropped: urls.length - 1 };
|
|
333
|
+
return { key, value: urls[0], dropped: urls.length - 1, also };
|
|
272
334
|
}
|
|
273
|
-
var schemaCache, REF_KEYS;
|
|
335
|
+
var schemaCache, REFERENCE_FIELD_PRIORITY, FALLBACK_KEYS, REF_KEYS;
|
|
274
336
|
var init_schema_snap = __esm({
|
|
275
337
|
"src/skills/schema-snap.ts"() {
|
|
276
338
|
schemaCache = /* @__PURE__ */ new Map();
|
|
339
|
+
REFERENCE_FIELD_PRIORITY = [
|
|
340
|
+
"image_url",
|
|
341
|
+
"start_image_url",
|
|
342
|
+
"input_image",
|
|
343
|
+
"image",
|
|
344
|
+
"reference_images",
|
|
345
|
+
"reference_urls",
|
|
346
|
+
"reference_image_urls",
|
|
347
|
+
"image_urls",
|
|
348
|
+
"image_input",
|
|
349
|
+
"images"
|
|
350
|
+
];
|
|
351
|
+
FALLBACK_KEYS = {
|
|
352
|
+
image: REFERENCE_FIELD_PRIORITY,
|
|
353
|
+
video: ["reference_videos"],
|
|
354
|
+
audio: ["reference_audios", "audio_url"]
|
|
355
|
+
};
|
|
277
356
|
REF_KEYS = [
|
|
278
357
|
"image_urls",
|
|
279
358
|
"images",
|
|
@@ -286,7 +365,51 @@ var init_schema_snap = __esm({
|
|
|
286
365
|
];
|
|
287
366
|
}
|
|
288
367
|
});
|
|
289
|
-
function
|
|
368
|
+
function paramsWithoutModel(params, model) {
|
|
369
|
+
if (!params || Object.keys(params).length === 0) return null;
|
|
370
|
+
if (model) return null;
|
|
371
|
+
return "Those settings are model-specific. Tell me which model to use and I will apply them.";
|
|
372
|
+
}
|
|
373
|
+
function applyParams(body, params, paramsModel, apiId) {
|
|
374
|
+
if (!params || Object.keys(params).length === 0) return void 0;
|
|
375
|
+
if (paramsModel !== apiId) {
|
|
376
|
+
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.`;
|
|
377
|
+
}
|
|
378
|
+
const rest = { ...params };
|
|
379
|
+
delete rest.prompt;
|
|
380
|
+
Object.assign(body, rest);
|
|
381
|
+
return "prompt" in params ? "I kept the prompt from the skill call and ignored the one in params." : void 0;
|
|
382
|
+
}
|
|
383
|
+
function prefixNote(result, note) {
|
|
384
|
+
if (!note) return result;
|
|
385
|
+
return { ...result, text: `${note} ${result.text}`.trim() };
|
|
386
|
+
}
|
|
387
|
+
var ParamsInput, PARAMS_JSON_SCHEMA;
|
|
388
|
+
var init_params = __esm({
|
|
389
|
+
"src/skills/params.ts"() {
|
|
390
|
+
ParamsInput = zod.z.record(zod.z.unknown()).optional();
|
|
391
|
+
PARAMS_JSON_SCHEMA = {
|
|
392
|
+
type: "object",
|
|
393
|
+
additionalProperties: true,
|
|
394
|
+
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.'
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
function fallbackSlot(apiId, kind) {
|
|
399
|
+
return apiId === VIDEO_REFERENCE_MODEL ? VIDEO_REFERENCE_SLOTS[kind] : void 0;
|
|
400
|
+
}
|
|
401
|
+
function sourceMedia(videoUrl, referenceUrls) {
|
|
402
|
+
const clip = videoUrl && !isAudioUrl(videoUrl) ? videoUrl : void 0;
|
|
403
|
+
const stills = referenceUrls.filter((u) => !isAudioUrl(u));
|
|
404
|
+
const url = clip ?? stills[0];
|
|
405
|
+
return {
|
|
406
|
+
url,
|
|
407
|
+
isClip: Boolean(clip) || !!url && isVideoUrl(url),
|
|
408
|
+
audioUrl: [videoUrl, ...referenceUrls].find((u) => u && isAudioUrl(u)),
|
|
409
|
+
stills
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function buildPayload(apiId, input, src, slot, audioSlot) {
|
|
290
413
|
const body = { prompt: input.prompt };
|
|
291
414
|
if (input.aspect_ratio) body.aspect_ratio = input.aspect_ratio;
|
|
292
415
|
if (input.preset) body.preset = input.preset;
|
|
@@ -294,9 +417,9 @@ function buildPayload(apiId, input) {
|
|
|
294
417
|
case "grok-video-extend":
|
|
295
418
|
case "wan-video-edit":
|
|
296
419
|
case "seedance-video-edit":
|
|
297
|
-
if (
|
|
298
|
-
body.video_url =
|
|
299
|
-
body.image_url =
|
|
420
|
+
if (src.url) {
|
|
421
|
+
body.video_url = src.url;
|
|
422
|
+
body.image_url = src.url;
|
|
300
423
|
}
|
|
301
424
|
if (input.duration !== void 0) {
|
|
302
425
|
body.video_duration = input.duration;
|
|
@@ -304,48 +427,90 @@ function buildPayload(apiId, input) {
|
|
|
304
427
|
}
|
|
305
428
|
break;
|
|
306
429
|
default:
|
|
307
|
-
if (
|
|
430
|
+
if (src.url) {
|
|
431
|
+
const refs = src.isClip ? [src.url] : src.stills.length > 0 ? src.stills : [src.url];
|
|
432
|
+
if (slot) {
|
|
433
|
+
body[slot.name] = slot.isArray ? refs : refs[0];
|
|
434
|
+
if (slot.also) Object.assign(body, slot.also);
|
|
435
|
+
} else {
|
|
436
|
+
body.image_url = refs[0];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (src.audioUrl && audioSlot) {
|
|
440
|
+
body[audioSlot.name] = audioSlot.isArray ? [src.audioUrl] : src.audioUrl;
|
|
441
|
+
if (audioSlot.also) Object.assign(body, audioSlot.also);
|
|
442
|
+
}
|
|
308
443
|
if (input.duration !== void 0) body.duration = input.duration;
|
|
444
|
+
if (input.resolution) body.resolution = input.resolution;
|
|
309
445
|
break;
|
|
310
446
|
}
|
|
311
447
|
return body;
|
|
312
448
|
}
|
|
313
449
|
async function run(input, ctx) {
|
|
314
|
-
const
|
|
450
|
+
const paramsGate = paramsWithoutModel(input.params, input.model);
|
|
451
|
+
if (paramsGate) return { kind: "text", text: paramsGate };
|
|
315
452
|
let resolved = input;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
453
|
+
let referenceUrls = [];
|
|
454
|
+
try {
|
|
455
|
+
const singleUrl = input.image_url ?? (input.image_path ? await uploadLocalFile(input.image_path, ctx.client) : void 0);
|
|
456
|
+
const manyUrls = [...input.image_urls ?? []];
|
|
457
|
+
for (const p of input.image_paths ?? []) {
|
|
458
|
+
manyUrls.push(await uploadLocalFile(p, ctx.client));
|
|
459
|
+
}
|
|
460
|
+
referenceUrls = [...singleUrl ? [singleUrl] : [], ...manyUrls].filter(
|
|
461
|
+
(u, i, all) => all.indexOf(u) === i
|
|
462
|
+
);
|
|
463
|
+
if (referenceUrls[0] && referenceUrls[0] !== input.image_url) {
|
|
464
|
+
resolved = { ...input, image_url: referenceUrls[0] };
|
|
465
|
+
}
|
|
466
|
+
} catch (err) {
|
|
467
|
+
if (err instanceof SkillUploadError) {
|
|
468
|
+
return { kind: "text", text: `pixeldojo_generate (upload): ${err.userMessage}` };
|
|
469
|
+
}
|
|
470
|
+
throw err;
|
|
471
|
+
}
|
|
472
|
+
const src = sourceMedia(input.video_url, referenceUrls);
|
|
473
|
+
let apiId = input.model ?? DEFAULT_MODEL;
|
|
474
|
+
let routeNote;
|
|
475
|
+
if ((src.isClip || src.audioUrl) && !VIDEO_INPUT_MODELS.has(apiId)) {
|
|
476
|
+
if (!input.model) {
|
|
477
|
+
apiId = VIDEO_REFERENCE_MODEL;
|
|
478
|
+
} else if (src.isClip && await resolveMediaSlot(input.model, "video", ctx.client) === null) {
|
|
479
|
+
apiId = VIDEO_REFERENCE_MODEL;
|
|
480
|
+
routeNote = `${input.model} can't work from a video clip, so I used ${VIDEO_REFERENCE_MODEL} instead.`;
|
|
325
481
|
}
|
|
326
482
|
}
|
|
327
483
|
if (apiId === "grok-video-extend") {
|
|
328
|
-
if (!
|
|
484
|
+
if (!src.url) {
|
|
329
485
|
return {
|
|
330
486
|
kind: "text",
|
|
331
|
-
text: '
|
|
487
|
+
text: 'pixeldojo_generate with model="grok-video-extend" requires video_url (the source video URL to extend, or image_path for a local file).'
|
|
332
488
|
};
|
|
333
489
|
}
|
|
334
490
|
if (resolved.duration === void 0) {
|
|
335
491
|
return {
|
|
336
492
|
kind: "text",
|
|
337
|
-
text: '
|
|
493
|
+
text: 'pixeldojo_generate with model="grok-video-extend" requires duration (extension length in seconds).'
|
|
338
494
|
};
|
|
339
495
|
}
|
|
340
496
|
}
|
|
497
|
+
let refField;
|
|
498
|
+
let audioSlot;
|
|
341
499
|
if (!VIDEO_INPUT_MODELS.has(apiId)) {
|
|
342
|
-
const [aspect_ratio, duration] = await Promise.all([
|
|
500
|
+
const [aspect_ratio, duration, resolution, ref, audio] = await Promise.all([
|
|
343
501
|
snapAspectRatio(apiId, resolved.aspect_ratio, ctx.client),
|
|
344
|
-
snapDuration(apiId, resolved.duration, ctx.client)
|
|
502
|
+
snapDuration(apiId, resolved.duration, ctx.client),
|
|
503
|
+
snapResolution(apiId, resolved.resolution, ctx.client),
|
|
504
|
+
src.url ? resolveMediaSlot(apiId, src.isClip ? "video" : "image", ctx.client) : Promise.resolve(void 0),
|
|
505
|
+
src.audioUrl ? resolveMediaSlot(apiId, "audio", ctx.client) : Promise.resolve(void 0)
|
|
345
506
|
]);
|
|
346
|
-
resolved = { ...resolved, aspect_ratio, duration };
|
|
507
|
+
resolved = { ...resolved, aspect_ratio, duration, resolution };
|
|
508
|
+
refField = ref === void 0 && src.url ? fallbackSlot(apiId, src.isClip ? "video" : "image") : ref;
|
|
509
|
+
audioSlot = audio === void 0 && src.audioUrl ? fallbackSlot(apiId, "audio") : audio;
|
|
347
510
|
}
|
|
348
|
-
const body = buildPayload(apiId, resolved);
|
|
511
|
+
const body = buildPayload(apiId, resolved, src, refField, audioSlot);
|
|
512
|
+
const paramsNote = applyParams(body, input.params, input.model, apiId);
|
|
513
|
+
const note = [routeNote, paramsNote].filter(Boolean).join(" ");
|
|
349
514
|
const submitted = await ctx.client.run(apiId, body);
|
|
350
515
|
const result = await pollJobUntilDoneOrHandoff(
|
|
351
516
|
submitted.jobId,
|
|
@@ -356,18 +521,22 @@ async function run(input, ctx) {
|
|
|
356
521
|
sleep: ctx.pollOverrides?.sleep
|
|
357
522
|
}
|
|
358
523
|
);
|
|
359
|
-
return
|
|
360
|
-
|
|
361
|
-
|
|
524
|
+
return prefixNote(
|
|
525
|
+
pollResultToSkillResult(
|
|
526
|
+
result,
|
|
527
|
+
(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(", ")}`
|
|
528
|
+
),
|
|
529
|
+
note
|
|
362
530
|
);
|
|
363
531
|
}
|
|
364
|
-
var InputSchema, DEFAULT_MODEL, VIDEO_INPUT_MODELS, InputJsonSchema, generateSkill;
|
|
532
|
+
var InputSchema, DEFAULT_MODEL, VIDEO_REFERENCE_MODEL, VIDEO_REFERENCE_SLOTS, VIDEO_INPUT_MODELS, InputJsonSchema, generateSkill;
|
|
365
533
|
var init_generate = __esm({
|
|
366
534
|
"src/skills/generate.ts"() {
|
|
367
535
|
init_poll();
|
|
368
536
|
init_types();
|
|
369
537
|
init_upload_helpers();
|
|
370
538
|
init_schema_snap();
|
|
539
|
+
init_params();
|
|
371
540
|
InputSchema = zod.z.object({
|
|
372
541
|
prompt: zod.z.string().min(1).max(8e3).describe("Prompt describing what to generate."),
|
|
373
542
|
model: zod.z.string().optional().describe(
|
|
@@ -378,12 +547,31 @@ var init_generate = __esm({
|
|
|
378
547
|
),
|
|
379
548
|
aspect_ratio: zod.z.string().optional().describe('Aspect ratio supported by the chosen model (e.g. "1:1", "16:9", "9:16").'),
|
|
380
549
|
duration: zod.z.number().int().positive().optional().describe("Video duration in seconds, when applicable."),
|
|
550
|
+
resolution: zod.z.string().optional().describe(
|
|
551
|
+
'Output resolution when the model supports it (e.g. "480p", "720p", "1080p", "4k"). Lower tiers cost fewer credits. Omit to use the model default.'
|
|
552
|
+
),
|
|
381
553
|
image_url: zod.z.string().url().optional().describe("Reference image URL for image-to-image / image-to-video tools."),
|
|
554
|
+
video_url: zod.z.string().url().optional().describe(
|
|
555
|
+
"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."
|
|
556
|
+
),
|
|
557
|
+
params: ParamsInput,
|
|
382
558
|
image_path: zod.z.string().min(1).optional().describe(
|
|
383
559
|
"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."
|
|
560
|
+
),
|
|
561
|
+
image_urls: zod.z.array(zod.z.string().url()).optional().describe(
|
|
562
|
+
"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."
|
|
563
|
+
),
|
|
564
|
+
image_paths: zod.z.array(zod.z.string().min(1)).optional().describe(
|
|
565
|
+
"Multiple absolute local file paths. Each is uploaded to PixelDojo (24h temp storage) and used as a reference, same rules as image_urls."
|
|
384
566
|
)
|
|
385
567
|
});
|
|
386
568
|
DEFAULT_MODEL = "google-nano-banana";
|
|
569
|
+
VIDEO_REFERENCE_MODEL = "seedance-2-reference";
|
|
570
|
+
VIDEO_REFERENCE_SLOTS = {
|
|
571
|
+
image: { name: "reference_images", isArray: true },
|
|
572
|
+
video: { name: "reference_videos", isArray: true },
|
|
573
|
+
audio: { name: "reference_audios", isArray: true }
|
|
574
|
+
};
|
|
387
575
|
VIDEO_INPUT_MODELS = /* @__PURE__ */ new Set(["grok-video-extend", "wan-video-edit", "seedance-video-edit"]);
|
|
388
576
|
InputJsonSchema = {
|
|
389
577
|
type: "object",
|
|
@@ -399,7 +587,27 @@ var init_generate = __esm({
|
|
|
399
587
|
},
|
|
400
588
|
aspect_ratio: { type: "string" },
|
|
401
589
|
duration: { type: "integer" },
|
|
590
|
+
resolution: {
|
|
591
|
+
type: "string",
|
|
592
|
+
description: 'Output resolution when supported (e.g. "480p", "720p", "1080p", "4k"). Lower tiers cost fewer credits.'
|
|
593
|
+
},
|
|
402
594
|
image_url: { type: "string", format: "uri" },
|
|
595
|
+
video_url: {
|
|
596
|
+
type: "string",
|
|
597
|
+
format: "uri",
|
|
598
|
+
description: "Source video clip to animate / restyle / extend. Use this for videos; image_url is for still frames."
|
|
599
|
+
},
|
|
600
|
+
params: PARAMS_JSON_SCHEMA,
|
|
601
|
+
image_urls: {
|
|
602
|
+
type: "array",
|
|
603
|
+
items: { type: "string", format: "uri" },
|
|
604
|
+
description: "Multiple reference image URLs for models that accept a reference set. Single-reference models use the first."
|
|
605
|
+
},
|
|
606
|
+
image_paths: {
|
|
607
|
+
type: "array",
|
|
608
|
+
items: { type: "string" },
|
|
609
|
+
description: "Multiple absolute local file paths, each uploaded to 24h temp storage and used as a reference."
|
|
610
|
+
},
|
|
403
611
|
image_path: {
|
|
404
612
|
type: "string",
|
|
405
613
|
description: "Absolute local file path. The skill uploads it to PixelDojo (24h temp storage) and uses the resulting URL as the reference image. Prefer this over image_url when the user has a local file."
|
|
@@ -408,8 +616,8 @@ var init_generate = __esm({
|
|
|
408
616
|
required: ["prompt"]
|
|
409
617
|
};
|
|
410
618
|
generateSkill = {
|
|
411
|
-
name: "
|
|
412
|
-
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
|
|
619
|
+
name: "pixeldojo_generate",
|
|
620
|
+
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`.",
|
|
413
621
|
inputZod: InputSchema,
|
|
414
622
|
inputJsonSchema: InputJsonSchema,
|
|
415
623
|
allowedModels: null,
|
|
@@ -476,9 +684,12 @@ async function buildBody(apiId, input, ctx) {
|
|
|
476
684
|
}
|
|
477
685
|
body.prompt = input.prompt;
|
|
478
686
|
body[refs.key] = refs.value;
|
|
687
|
+
if (refs.also) Object.assign(body, refs.also);
|
|
479
688
|
return { body, dropped: refs.dropped };
|
|
480
689
|
}
|
|
481
690
|
async function run2(input, ctx) {
|
|
691
|
+
const paramsGate = paramsWithoutModel(input.params, input.model);
|
|
692
|
+
if (paramsGate) return { kind: "text", text: paramsGate };
|
|
482
693
|
let resolved = input;
|
|
483
694
|
try {
|
|
484
695
|
if (input.image_paths && input.image_paths.length > 0) {
|
|
@@ -493,14 +704,14 @@ async function run2(input, ctx) {
|
|
|
493
704
|
}
|
|
494
705
|
} catch (err) {
|
|
495
706
|
if (err instanceof SkillUploadError) {
|
|
496
|
-
return { kind: "text", text: `
|
|
707
|
+
return { kind: "text", text: `pixeldojo_edit (upload): ${err.userMessage}` };
|
|
497
708
|
}
|
|
498
709
|
throw err;
|
|
499
710
|
}
|
|
500
711
|
if (!resolved.image_url && (!resolved.image_urls || resolved.image_urls.length === 0)) {
|
|
501
712
|
return {
|
|
502
713
|
kind: "text",
|
|
503
|
-
text: "
|
|
714
|
+
text: "pixeldojo_edit requires at least one reference image. Pass image_url / image_urls (hosted URL) or image_path / image_paths (local file)."
|
|
504
715
|
};
|
|
505
716
|
}
|
|
506
717
|
const apiId = resolved.model ?? DEFAULT_MODEL2;
|
|
@@ -510,6 +721,7 @@ async function run2(input, ctx) {
|
|
|
510
721
|
};
|
|
511
722
|
const { body, dropped } = await buildBody(apiId, resolved, ctx);
|
|
512
723
|
const droppedNote = dropped > 0 ? ` (${dropped} reference${dropped === 1 ? "" : "s"} dropped \u2014 ${apiId} accepts fewer)` : "";
|
|
724
|
+
const paramsNote = applyParams(body, input.params, input.model, apiId);
|
|
513
725
|
const submitted = await ctx.client.run(apiId, body);
|
|
514
726
|
const result = await pollJobUntilDoneOrHandoff(
|
|
515
727
|
submitted.jobId,
|
|
@@ -520,9 +732,12 @@ async function run2(input, ctx) {
|
|
|
520
732
|
sleep: ctx.pollOverrides?.sleep
|
|
521
733
|
}
|
|
522
734
|
);
|
|
523
|
-
return
|
|
524
|
-
|
|
525
|
-
|
|
735
|
+
return prefixNote(
|
|
736
|
+
pollResultToSkillResult(
|
|
737
|
+
result,
|
|
738
|
+
(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(", ")}`
|
|
739
|
+
),
|
|
740
|
+
paramsNote
|
|
526
741
|
);
|
|
527
742
|
}
|
|
528
743
|
var DEFAULT_MODEL2, ALLOWED_MODELS, InputSchema2, InputJsonSchema2, editSkill;
|
|
@@ -532,6 +747,7 @@ var init_edit = __esm({
|
|
|
532
747
|
init_types();
|
|
533
748
|
init_upload_helpers();
|
|
534
749
|
init_schema_snap();
|
|
750
|
+
init_params();
|
|
535
751
|
DEFAULT_MODEL2 = "wan-2.7-image-edit";
|
|
536
752
|
ALLOWED_MODELS = [
|
|
537
753
|
"wan-2.7-image-edit",
|
|
@@ -555,7 +771,8 @@ var init_edit = __esm({
|
|
|
555
771
|
model: zod.z.enum(ALLOWED_MODELS).optional().describe(
|
|
556
772
|
"Override the default edit model. Defaults to wan-2.7-image-edit (most popular)."
|
|
557
773
|
),
|
|
558
|
-
aspect_ratio: zod.z.string().optional().describe('Aspect ratio when supported (e.g. "1:1", "16:9", "match_input_image").')
|
|
774
|
+
aspect_ratio: zod.z.string().optional().describe('Aspect ratio when supported (e.g. "1:1", "16:9", "match_input_image").'),
|
|
775
|
+
params: ParamsInput
|
|
559
776
|
});
|
|
560
777
|
InputJsonSchema2 = {
|
|
561
778
|
type: "object",
|
|
@@ -589,12 +806,13 @@ var init_edit = __esm({
|
|
|
589
806
|
enum: [...ALLOWED_MODELS],
|
|
590
807
|
description: "Override edit model. Default: wan-2.7-image-edit."
|
|
591
808
|
},
|
|
592
|
-
aspect_ratio: { type: "string" }
|
|
809
|
+
aspect_ratio: { type: "string" },
|
|
810
|
+
params: PARAMS_JSON_SCHEMA
|
|
593
811
|
},
|
|
594
812
|
required: ["prompt"]
|
|
595
813
|
};
|
|
596
814
|
editSkill = {
|
|
597
|
-
name: "
|
|
815
|
+
name: "pixeldojo_edit",
|
|
598
816
|
description: "Edit an existing image with a text instruction. Defaults to wan-2.7-image-edit; pass `model` to pick another edit-capable apiId. Supports up to 9 reference images for multi-image composition. For reference images: pass image_url(s) for hosted URLs or image_path(s) for absolute local file paths (skill uploads to 24h temp storage automatically).",
|
|
599
817
|
inputZod: InputSchema2,
|
|
600
818
|
inputJsonSchema: InputJsonSchema2,
|
|
@@ -605,32 +823,50 @@ var init_edit = __esm({
|
|
|
605
823
|
}
|
|
606
824
|
});
|
|
607
825
|
async function run3(input, ctx) {
|
|
826
|
+
const paramsGate = paramsWithoutModel(input.params, input.model);
|
|
827
|
+
if (paramsGate) return { kind: "text", text: paramsGate };
|
|
608
828
|
const requested = input.model ?? DEFAULT_MODEL3;
|
|
609
829
|
if (!ALLOWED.includes(requested)) {
|
|
610
830
|
return {
|
|
611
831
|
kind: "text",
|
|
612
|
-
text: `Unknown model "${requested}" for
|
|
832
|
+
text: `Unknown model "${requested}" for pixeldojo_character. Allowed: ${ALLOWED.join(", ")}.`
|
|
613
833
|
};
|
|
614
834
|
}
|
|
615
835
|
const apiId = requested;
|
|
616
|
-
|
|
836
|
+
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);
|
|
837
|
+
const wrongKind = refUrls.find((u) => isVideoUrl(u) || isAudioUrl(u));
|
|
838
|
+
if (wrongKind) {
|
|
839
|
+
return {
|
|
840
|
+
kind: "text",
|
|
841
|
+
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.`
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
if (REQUIRES_REFERENCE.has(apiId) && refUrls.length === 0) {
|
|
617
845
|
return {
|
|
618
846
|
kind: "text",
|
|
619
|
-
text: `
|
|
847
|
+
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}".`
|
|
620
848
|
};
|
|
621
849
|
}
|
|
622
850
|
const body = { prompt: input.prompt };
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
851
|
+
let dropped = 0;
|
|
852
|
+
if (refUrls.length > 0) {
|
|
853
|
+
const refs = await resolveImageRefs(apiId, refUrls, ctx.client);
|
|
854
|
+
if (refs) {
|
|
855
|
+
body[refs.key] = refs.value;
|
|
856
|
+
if (refs.also) Object.assign(body, refs.also);
|
|
857
|
+
dropped = refs.dropped;
|
|
626
858
|
} else {
|
|
627
|
-
body
|
|
859
|
+
body[FALLBACK_REF_KEY[apiId]] = refUrls;
|
|
628
860
|
}
|
|
629
861
|
}
|
|
630
862
|
const snappedAspect = await snapAspectRatio(apiId, input.aspect_ratio, ctx.client, {
|
|
631
863
|
onUnknown: "omit"
|
|
632
864
|
});
|
|
633
865
|
if (snappedAspect !== void 0) body.aspect_ratio = snappedAspect;
|
|
866
|
+
const note = [
|
|
867
|
+
dropped > 0 ? `${dropped} reference${dropped === 1 ? "" : "s"} dropped \u2014 ${apiId} accepts fewer.` : void 0,
|
|
868
|
+
applyParams(body, input.params, input.model, apiId)
|
|
869
|
+
].filter(Boolean).join(" ");
|
|
634
870
|
const submitted = await ctx.client.run(apiId, body);
|
|
635
871
|
const result = await pollJobUntilDoneOrHandoff(
|
|
636
872
|
submitted.jobId,
|
|
@@ -641,27 +877,49 @@ async function run3(input, ctx) {
|
|
|
641
877
|
sleep: ctx.pollOverrides?.sleep
|
|
642
878
|
}
|
|
643
879
|
);
|
|
644
|
-
return
|
|
645
|
-
|
|
646
|
-
|
|
880
|
+
return prefixNote(
|
|
881
|
+
pollResultToSkillResult(
|
|
882
|
+
result,
|
|
883
|
+
(urls) => urls.length === 0 ? `Job ${submitted.jobId} completed but returned no assets.` : `Character shot ready. Asset: ${urls[0]}`
|
|
884
|
+
),
|
|
885
|
+
note
|
|
647
886
|
);
|
|
648
887
|
}
|
|
649
|
-
var ALLOWED, DEFAULT_MODEL3, REQUIRES_REFERENCE, InputSchema3, InputJsonSchema3, characterSkill;
|
|
888
|
+
var ALLOWED, DEFAULT_MODEL3, MAX_REFERENCES, REQUIRES_REFERENCE, FALLBACK_REF_KEY, InputSchema3, InputJsonSchema3, characterSkill;
|
|
650
889
|
var init_character = __esm({
|
|
651
890
|
"src/skills/character.ts"() {
|
|
652
891
|
init_poll();
|
|
653
892
|
init_types();
|
|
654
893
|
init_schema_snap();
|
|
655
|
-
|
|
894
|
+
init_params();
|
|
895
|
+
ALLOWED = [
|
|
896
|
+
"seedream-5",
|
|
897
|
+
"seedream-4",
|
|
898
|
+
"google-nano-banana",
|
|
899
|
+
"wan-2.7-image-edit",
|
|
900
|
+
"qwen-image-2.0-pro"
|
|
901
|
+
];
|
|
656
902
|
DEFAULT_MODEL3 = "seedream-5";
|
|
657
|
-
|
|
903
|
+
MAX_REFERENCES = 9;
|
|
904
|
+
REQUIRES_REFERENCE = /* @__PURE__ */ new Set(["wan-2.7-image-edit"]);
|
|
905
|
+
FALLBACK_REF_KEY = {
|
|
906
|
+
"seedream-5": "image_urls",
|
|
907
|
+
"seedream-4": "image_urls",
|
|
908
|
+
"google-nano-banana": "image_input",
|
|
909
|
+
"wan-2.7-image-edit": "image_urls",
|
|
910
|
+
"qwen-image-2.0-pro": "reference_images"
|
|
911
|
+
};
|
|
658
912
|
InputSchema3 = zod.z.object({
|
|
659
913
|
prompt: zod.z.string().min(1).max(8e3),
|
|
660
914
|
model: zod.z.string().optional().describe(`One of: ${ALLOWED.join(", ")}. Defaults to ${DEFAULT_MODEL3}.`),
|
|
661
915
|
reference_image_url: zod.z.string().url().optional().describe(
|
|
662
|
-
"
|
|
916
|
+
"Single reference image of the character. Prefer reference_image_urls \u2014 more angles lock the face better."
|
|
663
917
|
),
|
|
664
|
-
|
|
918
|
+
reference_image_urls: zod.z.array(zod.z.string().url()).min(1).max(MAX_REFERENCES).optional().describe(
|
|
919
|
+
`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.`
|
|
920
|
+
),
|
|
921
|
+
aspect_ratio: zod.z.string().optional().describe("Honored where the model declares the field."),
|
|
922
|
+
params: ParamsInput
|
|
665
923
|
});
|
|
666
924
|
InputJsonSchema3 = {
|
|
667
925
|
type: "object",
|
|
@@ -672,14 +930,26 @@ var init_character = __esm({
|
|
|
672
930
|
enum: ALLOWED,
|
|
673
931
|
description: `One of: ${ALLOWED.join(", ")}. Defaults to ${DEFAULT_MODEL3}.`
|
|
674
932
|
},
|
|
675
|
-
reference_image_url: {
|
|
676
|
-
|
|
933
|
+
reference_image_url: {
|
|
934
|
+
type: "string",
|
|
935
|
+
format: "uri",
|
|
936
|
+
description: "Single reference. Prefer reference_image_urls."
|
|
937
|
+
},
|
|
938
|
+
reference_image_urls: {
|
|
939
|
+
type: "array",
|
|
940
|
+
items: { type: "string", format: "uri" },
|
|
941
|
+
minItems: 1,
|
|
942
|
+
maxItems: MAX_REFERENCES,
|
|
943
|
+
description: "All references for this shot, most important first. Multiple angles of the same person lock the face far better than one photo."
|
|
944
|
+
},
|
|
945
|
+
aspect_ratio: { type: "string" },
|
|
946
|
+
params: PARAMS_JSON_SCHEMA
|
|
677
947
|
},
|
|
678
948
|
required: ["prompt"]
|
|
679
949
|
};
|
|
680
950
|
characterSkill = {
|
|
681
|
-
name: "
|
|
682
|
-
description: "Generate or edit consistent characters across shots. Pass
|
|
951
|
+
name: "pixeldojo_character",
|
|
952
|
+
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.",
|
|
683
953
|
inputZod: InputSchema3,
|
|
684
954
|
inputJsonSchema: InputJsonSchema3,
|
|
685
955
|
allowedModels: [...ALLOWED],
|
|
@@ -689,13 +959,29 @@ var init_character = __esm({
|
|
|
689
959
|
}
|
|
690
960
|
});
|
|
691
961
|
async function run4(input, ctx) {
|
|
962
|
+
const paramsGate = paramsWithoutModel(input.params, input.model);
|
|
963
|
+
if (paramsGate) return { kind: "text", text: paramsGate };
|
|
692
964
|
const requested = input.model ?? DEFAULT_MODEL4;
|
|
693
965
|
if (!ALLOWED2.includes(requested)) {
|
|
694
966
|
return {
|
|
695
967
|
kind: "text",
|
|
696
|
-
text: `Unknown model "${requested}" for
|
|
968
|
+
text: `Unknown model "${requested}" for pixeldojo_storyboard. Allowed: ${ALLOWED2.join(", ")}.`
|
|
697
969
|
};
|
|
698
970
|
}
|
|
971
|
+
if (input.image_url || input.video_url) {
|
|
972
|
+
return generateSkill.run(
|
|
973
|
+
{
|
|
974
|
+
prompt: input.brief,
|
|
975
|
+
model: input.model ?? VIDEO_REFERENCE_MODEL,
|
|
976
|
+
image_url: input.image_url,
|
|
977
|
+
video_url: input.video_url,
|
|
978
|
+
aspect_ratio: input.aspect_ratio,
|
|
979
|
+
duration: input.duration,
|
|
980
|
+
params: input.params
|
|
981
|
+
},
|
|
982
|
+
ctx
|
|
983
|
+
);
|
|
984
|
+
}
|
|
699
985
|
const apiId = requested;
|
|
700
986
|
const [snappedAspect, snappedDuration] = await Promise.all([
|
|
701
987
|
snapAspectRatio(apiId, input.aspect_ratio, ctx.client),
|
|
@@ -704,6 +990,7 @@ async function run4(input, ctx) {
|
|
|
704
990
|
const body = { prompt: input.brief };
|
|
705
991
|
if (snappedAspect !== void 0) body.aspect_ratio = snappedAspect;
|
|
706
992
|
if (snappedDuration !== void 0) body.duration = snappedDuration;
|
|
993
|
+
const note = applyParams(body, input.params, input.model, apiId);
|
|
707
994
|
const submitted = await ctx.client.run(apiId, body);
|
|
708
995
|
const result = await pollJobUntilDoneOrHandoff(
|
|
709
996
|
submitted.jobId,
|
|
@@ -714,9 +1001,12 @@ async function run4(input, ctx) {
|
|
|
714
1001
|
sleep: ctx.pollOverrides?.sleep
|
|
715
1002
|
}
|
|
716
1003
|
);
|
|
717
|
-
return
|
|
718
|
-
|
|
719
|
-
|
|
1004
|
+
return prefixNote(
|
|
1005
|
+
pollResultToSkillResult(
|
|
1006
|
+
result,
|
|
1007
|
+
(urls) => urls.length === 0 ? `Job ${submitted.jobId} completed but returned no assets.` : `Storyboard preview ready. Asset: ${urls[0]}`
|
|
1008
|
+
),
|
|
1009
|
+
note
|
|
720
1010
|
);
|
|
721
1011
|
}
|
|
722
1012
|
var ALLOWED2, DEFAULT_MODEL4, InputSchema4, InputJsonSchema4, storyboardSkill;
|
|
@@ -725,18 +1015,24 @@ var init_storyboard = __esm({
|
|
|
725
1015
|
init_poll();
|
|
726
1016
|
init_types();
|
|
727
1017
|
init_schema_snap();
|
|
1018
|
+
init_generate();
|
|
1019
|
+
init_params();
|
|
728
1020
|
ALLOWED2 = [
|
|
729
1021
|
"veo-3.1-fast",
|
|
730
1022
|
"veo-3.1-standard",
|
|
731
1023
|
"kling-video-v3-standard-text",
|
|
732
|
-
"seedance-2-high"
|
|
1024
|
+
"seedance-2-high",
|
|
1025
|
+
VIDEO_REFERENCE_MODEL
|
|
733
1026
|
];
|
|
734
1027
|
DEFAULT_MODEL4 = "veo-3.1-fast";
|
|
735
1028
|
InputSchema4 = zod.z.object({
|
|
736
1029
|
brief: zod.z.string().min(1).max(8e3).describe("Storyboard brief: scene, shots, mood, action."),
|
|
737
1030
|
model: zod.z.string().optional().describe(`One of: ${ALLOWED2.join(", ")}. Defaults to ${DEFAULT_MODEL4}.`),
|
|
738
1031
|
aspect_ratio: zod.z.string().optional(),
|
|
739
|
-
duration: zod.z.number().int().positive().optional()
|
|
1032
|
+
duration: zod.z.number().int().positive().optional(),
|
|
1033
|
+
image_url: zod.z.string().url().optional().describe("Reference still to build the shot from (character, product, location)."),
|
|
1034
|
+
video_url: zod.z.string().url().optional().describe("Source clip to work from. Use this for videos; image_url is for still frames."),
|
|
1035
|
+
params: ParamsInput
|
|
740
1036
|
});
|
|
741
1037
|
InputJsonSchema4 = {
|
|
742
1038
|
type: "object",
|
|
@@ -751,13 +1047,24 @@ var init_storyboard = __esm({
|
|
|
751
1047
|
description: `One of: ${ALLOWED2.join(", ")}. Defaults to ${DEFAULT_MODEL4}.`
|
|
752
1048
|
},
|
|
753
1049
|
aspect_ratio: { type: "string" },
|
|
754
|
-
duration: { type: "integer" }
|
|
1050
|
+
duration: { type: "integer" },
|
|
1051
|
+
image_url: {
|
|
1052
|
+
type: "string",
|
|
1053
|
+
format: "uri",
|
|
1054
|
+
description: "Reference still to build the shot from."
|
|
1055
|
+
},
|
|
1056
|
+
video_url: {
|
|
1057
|
+
type: "string",
|
|
1058
|
+
format: "uri",
|
|
1059
|
+
description: "Source clip to work from. Use this for videos; image_url is for stills."
|
|
1060
|
+
},
|
|
1061
|
+
params: PARAMS_JSON_SCHEMA
|
|
755
1062
|
},
|
|
756
1063
|
required: ["brief"]
|
|
757
1064
|
};
|
|
758
1065
|
storyboardSkill = {
|
|
759
|
-
name: "
|
|
760
|
-
description: "Generate a video storyboard preview from a single brief. v1: single-shot preview; multi-shot orchestration coming.",
|
|
1066
|
+
name: "pixeldojo_storyboard",
|
|
1067
|
+
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.",
|
|
761
1068
|
inputZod: InputSchema4,
|
|
762
1069
|
inputJsonSchema: InputJsonSchema4,
|
|
763
1070
|
allowedModels: [...ALLOWED2],
|
|
@@ -771,7 +1078,7 @@ async function run5(input, ctx) {
|
|
|
771
1078
|
if (!ALLOWED3.includes(requested)) {
|
|
772
1079
|
return {
|
|
773
1080
|
kind: "text",
|
|
774
|
-
text: `Unknown model "${requested}" for
|
|
1081
|
+
text: `Unknown model "${requested}" for pixeldojo_upscale. Allowed: ${ALLOWED3.join(", ")}.`
|
|
775
1082
|
};
|
|
776
1083
|
}
|
|
777
1084
|
const apiId = requested;
|
|
@@ -833,7 +1140,7 @@ var init_upscale = __esm({
|
|
|
833
1140
|
required: ["image_url"]
|
|
834
1141
|
};
|
|
835
1142
|
upscaleSkill = {
|
|
836
|
-
name: "
|
|
1143
|
+
name: "pixeldojo_upscale",
|
|
837
1144
|
description: "Enhance an image. Magnific (default) supports configurable scale_factor and creativity; face-enhance for portraits.",
|
|
838
1145
|
inputZod: InputSchema5,
|
|
839
1146
|
inputJsonSchema: InputJsonSchema5,
|
|
@@ -911,14 +1218,14 @@ var InputSchema6, InputJsonSchema6, STUCK_HINT_MS, ABANDON_HINT_MS, statusSkill;
|
|
|
911
1218
|
var init_status = __esm({
|
|
912
1219
|
"src/skills/status.ts"() {
|
|
913
1220
|
InputSchema6 = zod.z.object({
|
|
914
|
-
jobId: zod.z.string().min(1).max(200).describe("Job id returned from a prior
|
|
1221
|
+
jobId: zod.z.string().min(1).max(200).describe("Job id returned from a prior pixeldojo_* skill handoff.")
|
|
915
1222
|
});
|
|
916
1223
|
InputJsonSchema6 = {
|
|
917
1224
|
type: "object",
|
|
918
1225
|
properties: {
|
|
919
1226
|
jobId: {
|
|
920
1227
|
type: "string",
|
|
921
|
-
description: "Job id returned from a prior
|
|
1228
|
+
description: "Job id returned from a prior pixeldojo_* skill handoff."
|
|
922
1229
|
}
|
|
923
1230
|
},
|
|
924
1231
|
required: ["jobId"]
|
|
@@ -926,7 +1233,7 @@ var init_status = __esm({
|
|
|
926
1233
|
STUCK_HINT_MS = 5 * 60 * 1e3;
|
|
927
1234
|
ABANDON_HINT_MS = 12 * 60 * 1e3;
|
|
928
1235
|
statusSkill = {
|
|
929
|
-
name: "
|
|
1236
|
+
name: "pixeldojo_status",
|
|
930
1237
|
description: "Check the status of a long-running job after a 30s sync handoff. Returns assets when ready, failure reason if failed, or current state if still running.",
|
|
931
1238
|
inputZod: InputSchema6,
|
|
932
1239
|
inputJsonSchema: InputJsonSchema6,
|
|
@@ -971,8 +1278,8 @@ var init_from_url = __esm({
|
|
|
971
1278
|
required: ["url"]
|
|
972
1279
|
};
|
|
973
1280
|
fromUrlSkill = {
|
|
974
|
-
name: "
|
|
975
|
-
description: "Extract product info (name, description, images) from any product page URL. Use this as the first step before
|
|
1281
|
+
name: "pixeldojo_from_url",
|
|
1282
|
+
description: "Extract product info (name, description, images) from any product page URL. Use this as the first step before pixeldojo_campaign / :ad / :lookbook so the agent has structured product data instead of a free-text description.",
|
|
976
1283
|
inputZod: InputSchema7,
|
|
977
1284
|
inputJsonSchema: InputJsonSchema7,
|
|
978
1285
|
// No model dispatch — this skill calls a server-side scrape, not a generation.
|
|
@@ -1033,7 +1340,7 @@ async function plan(input, ctx) {
|
|
|
1033
1340
|
...sceneLines,
|
|
1034
1341
|
"",
|
|
1035
1342
|
`Nothing has been charged yet. Producing renders every shot (roughly 20-25 credits per shot at default engines, charged as each renders) plus a few credits for the soundtrack.`,
|
|
1036
|
-
`Review or edit at ${ctx.baseUrl}/film-studio/${film.id} \u2014 or call
|
|
1343
|
+
`Review or edit at ${ctx.baseUrl}/film-studio/${film.id} \u2014 or call pixeldojo_film with {"film_id": "${film.id}", "action": "produce"} to start production.`
|
|
1037
1344
|
].join("\n")
|
|
1038
1345
|
};
|
|
1039
1346
|
}
|
|
@@ -1048,7 +1355,7 @@ async function produce(filmId, ctx) {
|
|
|
1048
1355
|
}
|
|
1049
1356
|
return {
|
|
1050
1357
|
kind: "text",
|
|
1051
|
-
text: `Production started on "${data.film.title}": ${fired} frame(s) dispatched (each is a normal frame charge; the server's Frame check reviews every one). Poll
|
|
1358
|
+
text: `Production started on "${data.film.title}": ${fired} frame(s) dispatched (each is a normal frame charge; the server's Frame check reviews every one). Poll pixeldojo_film with {"film_id": "${filmId}", "action": "status"} every ~30 seconds \u2014 each poll advances the pipeline.`
|
|
1052
1359
|
};
|
|
1053
1360
|
}
|
|
1054
1361
|
function buildScorePlanBody(film) {
|
|
@@ -1177,13 +1484,13 @@ async function run8(input, ctx) {
|
|
|
1177
1484
|
var LOOKS, InputSchema8, InputJsonSchema8, shots, count, LOOK_MUSIC, ARC, filmSkill;
|
|
1178
1485
|
var init_film = __esm({
|
|
1179
1486
|
"src/skills/film.ts"() {
|
|
1180
|
-
LOOKS = ["live-action", "anime", "3d-animation", "noir", "horror", "sci-fi", "documentary", "western"];
|
|
1487
|
+
LOOKS = ["live-action", "anime", "3d-animation", "noir", "horror", "sci-fi", "documentary", "western", "romance", "romantic-comedy", "comedy"];
|
|
1181
1488
|
InputSchema8 = zod.z.object({
|
|
1182
1489
|
idea: zod.z.string().max(2e3).optional().describe("One-to-three sentence film idea. The studio writes the screenplay (free). Provide idea OR script for planning."),
|
|
1183
1490
|
script: zod.z.string().max(2e4).optional().describe("A full screenplay to plan instead of an idea (sluglines, action lines, dialogue)."),
|
|
1184
1491
|
look: zod.z.enum(LOOKS).default("live-action").describe("Visual style carried through frames, clips, and soundtrack."),
|
|
1185
1492
|
aspect: zod.z.enum(["16:9", "9:16"]).default("16:9"),
|
|
1186
|
-
length: zod.z.enum(["30s", "60s", "2-3min"]).default("60s").describe("Target length; drives the shot count (~6/12/24 shots)."),
|
|
1493
|
+
length: zod.z.enum(["30s", "60s", "2-3min", "5min"]).default("60s").describe("Target length; drives the shot count (~6/12/24/60 shots)."),
|
|
1187
1494
|
film_id: zod.z.string().max(40).optional().describe("Existing film id (from a previous call) for produce/status."),
|
|
1188
1495
|
action: zod.z.enum(["plan", "produce", "status"]).default("plan").describe("'plan' creates + storyboards (free). 'produce' starts charged generation. 'status' advances production and reports progress; poll it every ~30s until it returns the finished film."),
|
|
1189
1496
|
soundtrack: zod.z.enum(["scene-synced", "flat", "none"]).default("scene-synced").describe("scene-synced composes one musical section per scene, pinned to the scene lengths (default). flat is a single cohesive bed. none skips music."),
|
|
@@ -1196,7 +1503,7 @@ var init_film = __esm({
|
|
|
1196
1503
|
script: { type: "string", description: "A full screenplay to plan instead of an idea." },
|
|
1197
1504
|
look: { type: "string", enum: [...LOOKS], description: "Visual style. Default live-action." },
|
|
1198
1505
|
aspect: { type: "string", enum: ["16:9", "9:16"], description: "Default 16:9." },
|
|
1199
|
-
length: { type: "string", enum: ["30s", "60s", "2-3min"], description: "Target length (~6/12/24 shots). Default 60s." },
|
|
1506
|
+
length: { type: "string", enum: ["30s", "60s", "2-3min", "5min"], description: "Target length (~6/12/24/60 shots). Default 60s." },
|
|
1200
1507
|
film_id: { type: "string", description: "Existing film id for produce/status." },
|
|
1201
1508
|
action: { type: "string", enum: ["plan", "produce", "status"], description: "plan (free) | produce (charged) | status (poll every ~30s)." },
|
|
1202
1509
|
soundtrack: { type: "string", enum: ["scene-synced", "flat", "none"], description: "Default scene-synced." },
|
|
@@ -1213,7 +1520,10 @@ var init_film = __esm({
|
|
|
1213
1520
|
horror: "sparse horror underscore, low cello drones, dissonant string swells, creeping heartbeat pulse, unsettling quiet",
|
|
1214
1521
|
"sci-fi": "atmospheric science-fiction score, analog synth pads, deep sub-bass pulses, shimmering arpeggios, vast and cold",
|
|
1215
1522
|
documentary: "understated documentary score, felt piano, soft string beds, organic percussion textures, reflective and true",
|
|
1216
|
-
western: "cinematic western score, lonely twangy guitar, dusty strings, distant horns, wide-open desert tension"
|
|
1523
|
+
western: "cinematic western score, lonely twangy guitar, dusty strings, distant horns, wide-open desert tension",
|
|
1524
|
+
romance: "tender romantic score, felt piano and warm strings, soft acoustic guitar, intimate and unhurried, gently swelling",
|
|
1525
|
+
"romantic-comedy": "bright romantic-comedy score, plucky ukulele and handclaps, warm brushed drums, sunny and flirtatious",
|
|
1526
|
+
comedy: "playful comedy score, bouncy pizzicato strings, cheeky woodwinds, light percussion hits punctuating the gags"
|
|
1217
1527
|
};
|
|
1218
1528
|
ARC = {
|
|
1219
1529
|
opening: ["establishing the theme", "restrained", "quiet anticipation"],
|
|
@@ -1221,7 +1531,7 @@ var init_film = __esm({
|
|
|
1221
1531
|
finale: ["climactic", "full arrangement", "emotional peak resolving to stillness"]
|
|
1222
1532
|
};
|
|
1223
1533
|
filmSkill = {
|
|
1224
|
-
name: "
|
|
1534
|
+
name: "pixeldojo_film",
|
|
1225
1535
|
description: "Make a finished short film from one idea: screenplay, storyboard frames (AI-reviewed), video clips with spoken dialogue, scene-synced soundtrack, stitched export. Phased: plan is free and returns a cost estimate; produce starts charged generation; status advances production (poll every ~30s).",
|
|
1226
1536
|
inputZod: InputSchema8,
|
|
1227
1537
|
inputJsonSchema: InputJsonSchema8,
|
|
@@ -1360,8 +1670,8 @@ var init_ad = __esm({
|
|
|
1360
1670
|
// Zod refine; no root-level oneOf (Grok rejects non-object root unions).
|
|
1361
1671
|
};
|
|
1362
1672
|
adSkill = {
|
|
1363
|
-
name: "
|
|
1364
|
-
description: "Marketing Studio: turn any product into a video ad. Pass a product URL (auto-extracted) or product images, pick an ad style (UGC testimonial, unboxing, try-on, showcase, TV spot, and more via GET /api/v1/marketing/presets), optionally add presenter reference images, a location, a hook, and the exact spoken script. Renders with voiceover; returns the video or a statusUrl handoff to poll via
|
|
1673
|
+
name: "pixeldojo_ad",
|
|
1674
|
+
description: "Marketing Studio: turn any product into a video ad. Pass a product URL (auto-extracted) or product images, pick an ad style (UGC testimonial, unboxing, try-on, showcase, TV spot, and more via GET /api/v1/marketing/presets), optionally add presenter reference images, a location, a hook, and the exact spoken script. Renders with voiceover; returns the video or a statusUrl handoff to poll via pixeldojo_status. Ideal for automated generate-and-post workflows.",
|
|
1365
1675
|
inputZod: InputSchema9,
|
|
1366
1676
|
inputJsonSchema: InputJsonSchema9,
|
|
1367
1677
|
// Composition skill — the server routes to the ad engine internally.
|
|
@@ -1393,7 +1703,7 @@ async function run10(input, ctx) {
|
|
|
1393
1703
|
}
|
|
1394
1704
|
return {
|
|
1395
1705
|
kind: "handoff",
|
|
1396
|
-
text: result.message ?? `Campaign ${result.campaignId} queued. Poll via
|
|
1706
|
+
text: result.message ?? `Campaign ${result.campaignId} queued. Poll via pixeldojo_campaign_status.`,
|
|
1397
1707
|
jobId: result.campaignId,
|
|
1398
1708
|
statusUrl: result.statusUrl ?? ctx.buildStatusUrl(result.campaignId)
|
|
1399
1709
|
};
|
|
@@ -1446,8 +1756,8 @@ var init_campaign = __esm({
|
|
|
1446
1756
|
oneOf: [{ required: ["productUrl"] }, { required: ["product"] }]
|
|
1447
1757
|
};
|
|
1448
1758
|
campaignSkill = {
|
|
1449
|
-
name: "
|
|
1450
|
-
description: "Generate a coherent product launch package (hero image + N lifestyle variants + optional vertical video) from one URL or product profile. Composes hero / lifestyle / video models server-side; returns assets in one call or a handoff with campaignId to poll via
|
|
1759
|
+
name: "pixeldojo_campaign",
|
|
1760
|
+
description: "Generate a coherent product launch package (hero image + N lifestyle variants + optional vertical video) from one URL or product profile. Composes hero / lifestyle / video models server-side; returns assets in one call or a handoff with campaignId to poll via pixeldojo_campaign_status.",
|
|
1451
1761
|
inputZod: InputSchema10,
|
|
1452
1762
|
inputJsonSchema: InputJsonSchema10,
|
|
1453
1763
|
// Composition skill — internal sub-jobs route to per-tool models, no
|
|
@@ -1492,21 +1802,21 @@ var InputSchema11, InputJsonSchema11, campaignStatusSkill;
|
|
|
1492
1802
|
var init_campaign_status = __esm({
|
|
1493
1803
|
"src/skills/campaign-status.ts"() {
|
|
1494
1804
|
InputSchema11 = zod.z.object({
|
|
1495
|
-
campaignId: zod.z.string().min(1).max(200).describe("Campaign id returned from a prior
|
|
1805
|
+
campaignId: zod.z.string().min(1).max(200).describe("Campaign id returned from a prior pixeldojo_campaign handoff.")
|
|
1496
1806
|
});
|
|
1497
1807
|
InputJsonSchema11 = {
|
|
1498
1808
|
type: "object",
|
|
1499
1809
|
properties: {
|
|
1500
1810
|
campaignId: {
|
|
1501
1811
|
type: "string",
|
|
1502
|
-
description: "Campaign id returned from a prior
|
|
1812
|
+
description: "Campaign id returned from a prior pixeldojo_campaign handoff."
|
|
1503
1813
|
}
|
|
1504
1814
|
},
|
|
1505
1815
|
required: ["campaignId"]
|
|
1506
1816
|
};
|
|
1507
1817
|
campaignStatusSkill = {
|
|
1508
|
-
name: "
|
|
1509
|
-
description: "Poll a campaign by campaignId returned from
|
|
1818
|
+
name: "pixeldojo_campaign_status",
|
|
1819
|
+
description: "Poll a campaign by campaignId returned from pixeldojo_campaign. Returns assets when every sub-job is done, or a handoff describing what is still in flight.",
|
|
1510
1820
|
inputZod: InputSchema11,
|
|
1511
1821
|
inputJsonSchema: InputJsonSchema11,
|
|
1512
1822
|
allowedModels: [],
|
|
@@ -1521,7 +1831,7 @@ async function run12(input, ctx) {
|
|
|
1521
1831
|
url = await uploadLocalFile(input.path, ctx.client);
|
|
1522
1832
|
} catch (err) {
|
|
1523
1833
|
if (err instanceof SkillUploadError) {
|
|
1524
|
-
return { kind: "text", text: `
|
|
1834
|
+
return { kind: "text", text: `pixeldojo_upload: ${err.userMessage}` };
|
|
1525
1835
|
}
|
|
1526
1836
|
throw err;
|
|
1527
1837
|
}
|
|
@@ -1551,7 +1861,7 @@ var init_upload = __esm({
|
|
|
1551
1861
|
required: ["path"]
|
|
1552
1862
|
};
|
|
1553
1863
|
uploadSkill = {
|
|
1554
|
-
name: "
|
|
1864
|
+
name: "pixeldojo_upload",
|
|
1555
1865
|
description: "Upload a local image or video file and get back a public URL you can pass to :generate or :edit as image_url. Use when the user has a reference image on disk (drag-dropped into Claude Code, screenshot, etc.) and you need a URL. Storage auto-expires after 24 hours.",
|
|
1556
1866
|
inputZod: InputSchema12,
|
|
1557
1867
|
inputJsonSchema: InputJsonSchema12,
|
|
@@ -1566,7 +1876,7 @@ async function run13(input, ctx) {
|
|
|
1566
1876
|
if (!ALLOWED4.includes(requested)) {
|
|
1567
1877
|
return {
|
|
1568
1878
|
kind: "text",
|
|
1569
|
-
text: `Unknown model "${requested}" for
|
|
1879
|
+
text: `Unknown model "${requested}" for pixeldojo_audio. Allowed: ${ALLOWED4.join(", ")}.`
|
|
1570
1880
|
};
|
|
1571
1881
|
}
|
|
1572
1882
|
const apiId = requested;
|
|
@@ -1577,7 +1887,7 @@ async function run13(input, ctx) {
|
|
|
1577
1887
|
audioUrls = [...audioUrls, ...uploaded].slice(0, 3);
|
|
1578
1888
|
} catch (err) {
|
|
1579
1889
|
if (err instanceof SkillUploadError) {
|
|
1580
|
-
return { kind: "text", text: `
|
|
1890
|
+
return { kind: "text", text: `pixeldojo_audio (upload): ${err.userMessage}` };
|
|
1581
1891
|
}
|
|
1582
1892
|
throw err;
|
|
1583
1893
|
}
|
|
@@ -1643,7 +1953,7 @@ var init_audio = __esm({
|
|
|
1643
1953
|
required: ["prompt"]
|
|
1644
1954
|
};
|
|
1645
1955
|
audioSkill = {
|
|
1646
|
-
name: "
|
|
1956
|
+
name: "pixeldojo_audio",
|
|
1647
1957
|
description: "Generate audio: speech, multi-character dialogue, sound effects, and background music from one prompt (Seed Audio 1.0 by default; plain TTS available).",
|
|
1648
1958
|
inputZod: InputSchema13,
|
|
1649
1959
|
inputJsonSchema: InputJsonSchema13,
|
|
@@ -1663,7 +1973,7 @@ async function run14(input, ctx) {
|
|
|
1663
1973
|
if (res.items.length === 0) {
|
|
1664
1974
|
return {
|
|
1665
1975
|
kind: "text",
|
|
1666
|
-
text: input.q ? `No library items match "${input.q}". Try a broader query, or omit q to list recent generations.` : "Your library is empty. Generate something with
|
|
1976
|
+
text: input.q ? `No library items match "${input.q}". Try a broader query, or omit q to list recent generations.` : "Your library is empty. Generate something with pixeldojo_generate first."
|
|
1667
1977
|
};
|
|
1668
1978
|
}
|
|
1669
1979
|
const lines = res.items.map((it) => {
|
|
@@ -1704,8 +2014,8 @@ var init_library = __esm({
|
|
|
1704
2014
|
required: []
|
|
1705
2015
|
};
|
|
1706
2016
|
librarySkill = {
|
|
1707
|
-
name: "
|
|
1708
|
-
description: "Search your media library: saved My Media items (permanent) and recent API/MCP generations (expire after 24h), newest first. Returns asset URLs to reuse as reference inputs. Keep a result forever with
|
|
2017
|
+
name: "pixeldojo_library",
|
|
2018
|
+
description: "Search your media library: saved My Media items (permanent) and recent API/MCP generations (expire after 24h), newest first. Returns asset URLs to reuse as reference inputs. Keep a result forever with pixeldojo_save.",
|
|
1709
2019
|
inputZod: InputSchema14,
|
|
1710
2020
|
inputJsonSchema: InputJsonSchema14,
|
|
1711
2021
|
allowedModels: [],
|
|
@@ -1725,7 +2035,7 @@ async function run15(input, ctx) {
|
|
|
1725
2035
|
return {
|
|
1726
2036
|
kind: "text",
|
|
1727
2037
|
text: `Saved to My Media (permanent): ${res.url}
|
|
1728
|
-
It will appear at https://pixeldojo.ai/my-images and in
|
|
2038
|
+
It will appear at https://pixeldojo.ai/my-images and in pixeldojo_library searches.`
|
|
1729
2039
|
};
|
|
1730
2040
|
}
|
|
1731
2041
|
var InputSchema15, InputJsonSchema15, saveSkill;
|
|
@@ -1750,7 +2060,7 @@ var init_save = __esm({
|
|
|
1750
2060
|
required: ["url"]
|
|
1751
2061
|
};
|
|
1752
2062
|
saveSkill = {
|
|
1753
|
-
name: "
|
|
2063
|
+
name: "pixeldojo_save",
|
|
1754
2064
|
description: "Save a generated image or video into the user's My Media library permanently. Generation outputs otherwise expire after 24 hours \u2014 call this when the user wants to keep a result.",
|
|
1755
2065
|
inputZod: InputSchema15,
|
|
1756
2066
|
inputJsonSchema: InputJsonSchema15,
|
|
@@ -1798,7 +2108,7 @@ async function runSave(input, ctx) {
|
|
|
1798
2108
|
});
|
|
1799
2109
|
return {
|
|
1800
2110
|
kind: "text",
|
|
1801
|
-
text: res.created ? `Saved workflow "${res.name}" (${res.steps} step${res.steps === 1 ? "" : "s"}). Run it with
|
|
2111
|
+
text: res.created ? `Saved workflow "${res.name}" (${res.steps} step${res.steps === 1 ? "" : "s"}). Run it with pixeldojo_run_workflow { name: "${res.name}" }.` : `Overwrote workflow "${res.name}" (${res.steps} step${res.steps === 1 ? "" : "s"}).`
|
|
1802
2112
|
};
|
|
1803
2113
|
}
|
|
1804
2114
|
async function runList(_input, ctx) {
|
|
@@ -1806,7 +2116,7 @@ async function runList(_input, ctx) {
|
|
|
1806
2116
|
if (res.count === 0) {
|
|
1807
2117
|
return {
|
|
1808
2118
|
kind: "text",
|
|
1809
|
-
text: "No saved workflows yet. Save one with
|
|
2119
|
+
text: "No saved workflows yet. Save one with pixeldojo_save_workflow { name, steps: [{ skill, args }] }."
|
|
1810
2120
|
};
|
|
1811
2121
|
}
|
|
1812
2122
|
const lines = res.workflows.map(
|
|
@@ -1875,7 +2185,7 @@ async function runWorkflow(input, ctx) {
|
|
|
1875
2185
|
} else if (result.kind === "handoff") {
|
|
1876
2186
|
return {
|
|
1877
2187
|
kind: "text",
|
|
1878
|
-
text: `Workflow "${workflow.name}" is still on step ${i + 1} (${step.skill}) after an extended wait. Poll with
|
|
2188
|
+
text: `Workflow "${workflow.name}" is still on step ${i + 1} (${step.skill}) after an extended wait. Poll with pixeldojo_status { jobId: "${result.jobId}" }, then re-run the workflow once it completes.`
|
|
1879
2189
|
};
|
|
1880
2190
|
} else {
|
|
1881
2191
|
outputs.push(`step ${i + 1} (${step.skill}): done (no asset URL)`);
|
|
@@ -1894,7 +2204,7 @@ var init_workflows = __esm({
|
|
|
1894
2204
|
init_types();
|
|
1895
2205
|
WORKFLOW_STEP_EXTRA_BUDGET_MS = 6e4;
|
|
1896
2206
|
StepSchema = zod.z.object({
|
|
1897
|
-
skill: zod.z.string().regex(/^pixeldojo:(generate|edit|character|storyboard|upscale|audio|upload|from_url)$/, {
|
|
2207
|
+
skill: zod.z.string().regex(/^pixeldojo[:_](generate|edit|character|storyboard|upscale|audio|upload|from_url)$/, {
|
|
1898
2208
|
message: "Runnable skills only: generate, edit, character, storyboard, upscale, audio, upload, from_url."
|
|
1899
2209
|
}),
|
|
1900
2210
|
args: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
@@ -1920,7 +2230,7 @@ var init_workflows = __esm({
|
|
|
1920
2230
|
required: ["skill"]
|
|
1921
2231
|
};
|
|
1922
2232
|
saveWorkflowSkill = {
|
|
1923
|
-
name: "
|
|
2233
|
+
name: "pixeldojo_save_workflow",
|
|
1924
2234
|
description: "Save a named multi-step skill chain (generate \u2192 edit \u2192 upscale \u2026). String args may contain {{input.<key>}} and {{prev}} placeholders. Saving to an existing name overwrites it.",
|
|
1925
2235
|
inputZod: SaveInputSchema,
|
|
1926
2236
|
inputJsonSchema: {
|
|
@@ -1937,7 +2247,7 @@ var init_workflows = __esm({
|
|
|
1937
2247
|
run: runSave
|
|
1938
2248
|
};
|
|
1939
2249
|
runWorkflowSkill = {
|
|
1940
|
-
name: "
|
|
2250
|
+
name: "pixeldojo_run_workflow",
|
|
1941
2251
|
description: "Run a saved workflow by name. Pass inputs for its {{input.<key>}} placeholders; each step feeds the next via {{prev}}.",
|
|
1942
2252
|
inputZod: RunInputSchema,
|
|
1943
2253
|
inputJsonSchema: {
|
|
@@ -1953,7 +2263,7 @@ var init_workflows = __esm({
|
|
|
1953
2263
|
run: runWorkflow
|
|
1954
2264
|
};
|
|
1955
2265
|
listWorkflowsSkill = {
|
|
1956
|
-
name: "
|
|
2266
|
+
name: "pixeldojo_list_workflows",
|
|
1957
2267
|
description: "List your saved workflows with step counts and run totals.",
|
|
1958
2268
|
inputZod: ListInputSchema,
|
|
1959
2269
|
inputJsonSchema: { type: "object", properties: {}, required: [] },
|
|
@@ -1989,7 +2299,8 @@ __export(skills_exports, {
|
|
|
1989
2299
|
upscaleSkill: () => upscaleSkill
|
|
1990
2300
|
});
|
|
1991
2301
|
function findSkill(name) {
|
|
1992
|
-
|
|
2302
|
+
const wanted = name.replace(/^pixeldojo:/, "pixeldojo_");
|
|
2303
|
+
return exports.ALL_SKILLS.find((s) => s.name === wanted);
|
|
1993
2304
|
}
|
|
1994
2305
|
exports.ALL_SKILLS = void 0;
|
|
1995
2306
|
var init_skills = __esm({
|
|
@@ -2083,7 +2394,7 @@ function makeRedactedLogger(target = console) {
|
|
|
2083
2394
|
}
|
|
2084
2395
|
|
|
2085
2396
|
// src/server.ts
|
|
2086
|
-
var PACKAGE_VERSION = "0.
|
|
2397
|
+
var PACKAGE_VERSION = "0.10.0" ;
|
|
2087
2398
|
function buildServer(options) {
|
|
2088
2399
|
if (!options.apiKey) {
|
|
2089
2400
|
throw new Error(
|