@pixeldojo/mcp 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +383 -63
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +383 -63
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +383 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +383 -63
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,8 @@ var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
|
5
5
|
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
6
6
|
var sdk = require('@pixeldojo/sdk');
|
|
7
7
|
var zod = require('zod');
|
|
8
|
+
var promises = require('fs/promises');
|
|
9
|
+
var path = require('path');
|
|
8
10
|
|
|
9
11
|
// src/server.ts
|
|
10
12
|
function makeAttributedClient(options) {
|
|
@@ -108,6 +110,71 @@ function extractAssetUrls(job) {
|
|
|
108
110
|
if (output?.video) return [output.video];
|
|
109
111
|
return [];
|
|
110
112
|
}
|
|
113
|
+
var MAX_IMAGE_BYTES = 50 * 1024 * 1024;
|
|
114
|
+
var MAX_VIDEO_BYTES = 200 * 1024 * 1024;
|
|
115
|
+
var EXT_TO_MIME = {
|
|
116
|
+
".jpg": "image/jpeg",
|
|
117
|
+
".jpeg": "image/jpeg",
|
|
118
|
+
".png": "image/png",
|
|
119
|
+
".webp": "image/webp",
|
|
120
|
+
".gif": "image/gif",
|
|
121
|
+
".mp4": "video/mp4",
|
|
122
|
+
".webm": "video/webm",
|
|
123
|
+
".mov": "video/quicktime"
|
|
124
|
+
};
|
|
125
|
+
var SkillUploadError = class extends Error {
|
|
126
|
+
constructor(userMessage) {
|
|
127
|
+
super(userMessage);
|
|
128
|
+
this.userMessage = userMessage;
|
|
129
|
+
this.name = "SkillUploadError";
|
|
130
|
+
}
|
|
131
|
+
userMessage;
|
|
132
|
+
};
|
|
133
|
+
async function uploadLocalFile(path$1, client) {
|
|
134
|
+
const ext = path.extname(path$1).toLowerCase();
|
|
135
|
+
const mime = EXT_TO_MIME[ext];
|
|
136
|
+
if (!mime) {
|
|
137
|
+
throw new SkillUploadError(
|
|
138
|
+
`Unsupported file extension "${ext || "(none)"}" for "${path$1}". Allowed: ${Object.keys(EXT_TO_MIME).join(", ")}.`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
let size;
|
|
142
|
+
try {
|
|
143
|
+
const info = await promises.stat(path$1);
|
|
144
|
+
if (!info.isFile()) {
|
|
145
|
+
throw new SkillUploadError(`"${path$1}" is not a file.`);
|
|
146
|
+
}
|
|
147
|
+
size = info.size;
|
|
148
|
+
} catch (err) {
|
|
149
|
+
if (err instanceof SkillUploadError) throw err;
|
|
150
|
+
const code = err?.code;
|
|
151
|
+
if (code === "ENOENT") {
|
|
152
|
+
throw new SkillUploadError(
|
|
153
|
+
`File not found at "${path$1}". Pass an absolute path.`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
throw new SkillUploadError(
|
|
157
|
+
`Cannot read "${path$1}": ${err instanceof Error ? err.message : String(err)}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
const isImage = mime.startsWith("image/");
|
|
161
|
+
const limit = isImage ? MAX_IMAGE_BYTES : MAX_VIDEO_BYTES;
|
|
162
|
+
if (size > limit) {
|
|
163
|
+
const gotMB = (size / 1024 / 1024).toFixed(1);
|
|
164
|
+
const limitMB = (limit / 1024 / 1024).toFixed(0);
|
|
165
|
+
throw new SkillUploadError(
|
|
166
|
+
`"${path.basename(path$1)}" is ${gotMB}MB; limit for ${isImage ? "images" : "videos"} is ${limitMB}MB. Downscale or re-encode and try again.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
const buffer = await promises.readFile(path$1);
|
|
170
|
+
const blob = new Blob([new Uint8Array(buffer)], { type: mime });
|
|
171
|
+
const filename = path.basename(path$1);
|
|
172
|
+
const response = await client.uploadImage(blob, filename);
|
|
173
|
+
return response.url;
|
|
174
|
+
}
|
|
175
|
+
async function uploadLocalFiles(paths, client) {
|
|
176
|
+
return Promise.all(paths.map((p) => uploadLocalFile(p, client)));
|
|
177
|
+
}
|
|
111
178
|
|
|
112
179
|
// src/skills/generate.ts
|
|
113
180
|
var InputSchema = zod.z.object({
|
|
@@ -120,7 +187,10 @@ var InputSchema = zod.z.object({
|
|
|
120
187
|
),
|
|
121
188
|
aspect_ratio: zod.z.string().optional().describe('Aspect ratio supported by the chosen model (e.g. "1:1", "16:9", "9:16").'),
|
|
122
189
|
duration: zod.z.number().int().positive().optional().describe("Video duration in seconds, when applicable."),
|
|
123
|
-
image_url: zod.z.string().url().optional().describe("Reference image URL for image-to-image / image-to-video tools.")
|
|
190
|
+
image_url: zod.z.string().url().optional().describe("Reference image URL for image-to-image / image-to-video tools."),
|
|
191
|
+
image_path: zod.z.string().min(1).optional().describe(
|
|
192
|
+
"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."
|
|
193
|
+
)
|
|
124
194
|
});
|
|
125
195
|
var DEFAULT_MODEL = "google-nano-banana";
|
|
126
196
|
var InputJsonSchema = {
|
|
@@ -137,17 +207,67 @@ var InputJsonSchema = {
|
|
|
137
207
|
},
|
|
138
208
|
aspect_ratio: { type: "string" },
|
|
139
209
|
duration: { type: "integer" },
|
|
140
|
-
image_url: { type: "string", format: "uri" }
|
|
210
|
+
image_url: { type: "string", format: "uri" },
|
|
211
|
+
image_path: {
|
|
212
|
+
type: "string",
|
|
213
|
+
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."
|
|
214
|
+
}
|
|
141
215
|
},
|
|
142
216
|
required: ["prompt"]
|
|
143
217
|
};
|
|
144
|
-
|
|
145
|
-
const apiId = input.model ?? DEFAULT_MODEL;
|
|
218
|
+
function buildPayload(apiId, input) {
|
|
146
219
|
const body = { prompt: input.prompt };
|
|
147
220
|
if (input.aspect_ratio) body.aspect_ratio = input.aspect_ratio;
|
|
148
|
-
if (input.duration !== void 0) body.duration = input.duration;
|
|
149
|
-
if (input.image_url) body.image_url = input.image_url;
|
|
150
221
|
if (input.preset) body.preset = input.preset;
|
|
222
|
+
switch (apiId) {
|
|
223
|
+
case "grok-video-extend":
|
|
224
|
+
case "wan-video-edit":
|
|
225
|
+
case "seedance-video-edit":
|
|
226
|
+
if (input.image_url) {
|
|
227
|
+
body.video_url = input.image_url;
|
|
228
|
+
body.image_url = input.image_url;
|
|
229
|
+
}
|
|
230
|
+
if (input.duration !== void 0) {
|
|
231
|
+
body.video_duration = input.duration;
|
|
232
|
+
body.duration = input.duration;
|
|
233
|
+
}
|
|
234
|
+
break;
|
|
235
|
+
default:
|
|
236
|
+
if (input.image_url) body.image_url = input.image_url;
|
|
237
|
+
if (input.duration !== void 0) body.duration = input.duration;
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
return body;
|
|
241
|
+
}
|
|
242
|
+
async function run(input, ctx) {
|
|
243
|
+
const apiId = input.model ?? DEFAULT_MODEL;
|
|
244
|
+
let resolved = input;
|
|
245
|
+
if (!input.image_url && input.image_path) {
|
|
246
|
+
try {
|
|
247
|
+
const uploadedUrl = await uploadLocalFile(input.image_path, ctx.client);
|
|
248
|
+
resolved = { ...input, image_url: uploadedUrl };
|
|
249
|
+
} catch (err) {
|
|
250
|
+
if (err instanceof SkillUploadError) {
|
|
251
|
+
return { kind: "text", text: `pixeldojo:generate (upload): ${err.userMessage}` };
|
|
252
|
+
}
|
|
253
|
+
throw err;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (apiId === "grok-video-extend") {
|
|
257
|
+
if (!resolved.image_url) {
|
|
258
|
+
return {
|
|
259
|
+
kind: "text",
|
|
260
|
+
text: 'pixeldojo:generate with model="grok-video-extend" requires image_url (the source video URL to extend, or image_path for a local file).'
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (resolved.duration === void 0) {
|
|
264
|
+
return {
|
|
265
|
+
kind: "text",
|
|
266
|
+
text: 'pixeldojo:generate with model="grok-video-extend" requires duration (extension length in seconds).'
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const body = buildPayload(apiId, resolved);
|
|
151
271
|
const submitted = await ctx.client.run(apiId, body);
|
|
152
272
|
const result = await pollJobUntilDoneOrHandoff(
|
|
153
273
|
submitted.jobId,
|
|
@@ -165,39 +285,191 @@ async function run(input, ctx) {
|
|
|
165
285
|
}
|
|
166
286
|
var generateSkill = {
|
|
167
287
|
name: "pixeldojo:generate",
|
|
168
|
-
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.",
|
|
288
|
+
description: "Generate any image or video from a prompt. Auto-routes to a sensible default model; pass `model` to pick a specific apiId from the catalog. For reference-image / image-to-video flows: pass `image_url` if you already have a URL, or `image_path` (absolute local path) and the skill uploads it to 24h temp storage automatically.",
|
|
169
289
|
inputZod: InputSchema,
|
|
170
290
|
inputJsonSchema: InputJsonSchema,
|
|
171
291
|
allowedModels: null,
|
|
172
292
|
defaultModel: DEFAULT_MODEL,
|
|
173
293
|
run
|
|
174
294
|
};
|
|
175
|
-
var
|
|
176
|
-
var
|
|
295
|
+
var DEFAULT_MODEL2 = "wan-2.7-image-edit";
|
|
296
|
+
var ALLOWED_MODELS = [
|
|
297
|
+
"wan-2.7-image-edit",
|
|
298
|
+
"wan-2.6-image-edit",
|
|
299
|
+
"flux-edit",
|
|
300
|
+
"xai-image-edit",
|
|
301
|
+
"p-image-edit",
|
|
302
|
+
"gpt-image-2-edit",
|
|
303
|
+
"hidream-edit",
|
|
304
|
+
"google-nano-banana"
|
|
305
|
+
];
|
|
177
306
|
var InputSchema2 = zod.z.object({
|
|
307
|
+
prompt: zod.z.string().min(1).max(5e3).describe("Edit instruction."),
|
|
308
|
+
image_url: zod.z.string().url().optional().describe("Single reference image to edit. Pass image_urls for multi-image edits."),
|
|
309
|
+
image_urls: zod.z.array(zod.z.string().url()).min(1).max(9).optional().describe("Multiple reference images (1-9). Used for composition/multi-ref edits."),
|
|
310
|
+
image_path: zod.z.string().min(1).optional().describe(
|
|
311
|
+
"Absolute local file path. Skill uploads it (24h temp storage) and edits it. Prefer over image_url when the user has the file on disk."
|
|
312
|
+
),
|
|
313
|
+
image_paths: zod.z.array(zod.z.string().min(1)).min(1).max(9).optional().describe(
|
|
314
|
+
"Multiple absolute local file paths (1-9) for multi-image edits. Each uploaded to 24h temp storage before dispatch."
|
|
315
|
+
),
|
|
316
|
+
model: zod.z.enum(ALLOWED_MODELS).optional().describe(
|
|
317
|
+
"Override the default edit model. Defaults to wan-2.7-image-edit (most popular)."
|
|
318
|
+
),
|
|
319
|
+
aspect_ratio: zod.z.string().optional().describe('Aspect ratio when supported (e.g. "1:1", "16:9", "match_input_image").')
|
|
320
|
+
});
|
|
321
|
+
var InputJsonSchema2 = {
|
|
322
|
+
type: "object",
|
|
323
|
+
properties: {
|
|
324
|
+
prompt: { type: "string", description: "Edit instruction." },
|
|
325
|
+
image_url: {
|
|
326
|
+
type: "string",
|
|
327
|
+
format: "uri",
|
|
328
|
+
description: "Single reference image. Pass image_urls for multi-image edits."
|
|
329
|
+
},
|
|
330
|
+
image_urls: {
|
|
331
|
+
type: "array",
|
|
332
|
+
items: { type: "string", format: "uri" },
|
|
333
|
+
minItems: 1,
|
|
334
|
+
maxItems: 9,
|
|
335
|
+
description: "Multiple reference images (1-9)."
|
|
336
|
+
},
|
|
337
|
+
image_path: {
|
|
338
|
+
type: "string",
|
|
339
|
+
description: "Absolute local file path. Skill uploads it (24h temp storage) and edits the resulting URL. Prefer over image_url when the user has a local file."
|
|
340
|
+
},
|
|
341
|
+
image_paths: {
|
|
342
|
+
type: "array",
|
|
343
|
+
items: { type: "string" },
|
|
344
|
+
minItems: 1,
|
|
345
|
+
maxItems: 9,
|
|
346
|
+
description: "Multiple absolute local file paths (1-9). Each uploaded before dispatch."
|
|
347
|
+
},
|
|
348
|
+
model: {
|
|
349
|
+
type: "string",
|
|
350
|
+
enum: [...ALLOWED_MODELS],
|
|
351
|
+
description: "Override edit model. Default: wan-2.7-image-edit."
|
|
352
|
+
},
|
|
353
|
+
aspect_ratio: { type: "string" }
|
|
354
|
+
},
|
|
355
|
+
required: ["prompt"]
|
|
356
|
+
};
|
|
357
|
+
function buildPayload2(apiId, input) {
|
|
358
|
+
const urls = input.image_urls && input.image_urls.length > 0 ? input.image_urls : input.image_url ? [input.image_url] : [];
|
|
359
|
+
switch (apiId) {
|
|
360
|
+
case "wan-2.7-image-edit":
|
|
361
|
+
case "wan-2.6-image-edit":
|
|
362
|
+
return {
|
|
363
|
+
prompt: input.prompt,
|
|
364
|
+
image_urls: urls
|
|
365
|
+
// wan-edit uses `size` not aspect_ratio. Default 2K, let model defaults handle it otherwise.
|
|
366
|
+
};
|
|
367
|
+
case "p-image-edit":
|
|
368
|
+
return {
|
|
369
|
+
prompt: input.prompt,
|
|
370
|
+
images: urls.slice(0, 5),
|
|
371
|
+
...input.aspect_ratio ? { aspect_ratio: input.aspect_ratio } : {}
|
|
372
|
+
};
|
|
373
|
+
case "flux-edit":
|
|
374
|
+
case "xai-image-edit":
|
|
375
|
+
case "gpt-image-2-edit":
|
|
376
|
+
case "hidream-edit":
|
|
377
|
+
return {
|
|
378
|
+
prompt: input.prompt,
|
|
379
|
+
image_url: urls[0],
|
|
380
|
+
...urls.length > 1 ? { image_urls: urls } : {},
|
|
381
|
+
...input.aspect_ratio ? { aspect_ratio: input.aspect_ratio } : {}
|
|
382
|
+
};
|
|
383
|
+
case "google-nano-banana":
|
|
384
|
+
return {
|
|
385
|
+
prompt: input.prompt,
|
|
386
|
+
image_input: urls,
|
|
387
|
+
...input.aspect_ratio ? { aspect_ratio: input.aspect_ratio } : {}
|
|
388
|
+
};
|
|
389
|
+
default:
|
|
390
|
+
return {
|
|
391
|
+
prompt: input.prompt,
|
|
392
|
+
image_url: urls[0]
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function run2(input, ctx) {
|
|
397
|
+
let resolved = input;
|
|
398
|
+
try {
|
|
399
|
+
if (input.image_paths && input.image_paths.length > 0) {
|
|
400
|
+
const urls = await uploadLocalFiles(input.image_paths, ctx.client);
|
|
401
|
+
resolved = {
|
|
402
|
+
...input,
|
|
403
|
+
image_urls: [...input.image_urls ?? [], ...urls]
|
|
404
|
+
};
|
|
405
|
+
} else if (input.image_path && !input.image_url) {
|
|
406
|
+
const url = await uploadLocalFile(input.image_path, ctx.client);
|
|
407
|
+
resolved = { ...input, image_url: url };
|
|
408
|
+
}
|
|
409
|
+
} catch (err) {
|
|
410
|
+
if (err instanceof SkillUploadError) {
|
|
411
|
+
return { kind: "text", text: `pixeldojo:edit (upload): ${err.userMessage}` };
|
|
412
|
+
}
|
|
413
|
+
throw err;
|
|
414
|
+
}
|
|
415
|
+
if (!resolved.image_url && (!resolved.image_urls || resolved.image_urls.length === 0)) {
|
|
416
|
+
return {
|
|
417
|
+
kind: "text",
|
|
418
|
+
text: "pixeldojo:edit requires at least one reference image. Pass image_url / image_urls (hosted URL) or image_path / image_paths (local file)."
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
const apiId = resolved.model ?? DEFAULT_MODEL2;
|
|
422
|
+
const body = buildPayload2(apiId, resolved);
|
|
423
|
+
const submitted = await ctx.client.run(apiId, body);
|
|
424
|
+
const result = await pollJobUntilDoneOrHandoff(
|
|
425
|
+
submitted.jobId,
|
|
426
|
+
(id) => ctx.client.getJob(id),
|
|
427
|
+
ctx.buildStatusUrl,
|
|
428
|
+
{
|
|
429
|
+
now: ctx.pollOverrides?.now,
|
|
430
|
+
sleep: ctx.pollOverrides?.sleep
|
|
431
|
+
}
|
|
432
|
+
);
|
|
433
|
+
return pollResultToSkillResult(
|
|
434
|
+
result,
|
|
435
|
+
(urls) => urls.length === 0 ? `Edit job ${submitted.jobId} completed but returned no assets.` : urls.length === 1 ? `Edit complete. Asset: ${urls[0]}` : `Edit produced ${urls.length} assets. URLs: ${urls.join(", ")}`
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
var editSkill = {
|
|
439
|
+
name: "pixeldojo:edit",
|
|
440
|
+
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).",
|
|
441
|
+
inputZod: InputSchema2,
|
|
442
|
+
inputJsonSchema: InputJsonSchema2,
|
|
443
|
+
allowedModels: [...ALLOWED_MODELS],
|
|
444
|
+
defaultModel: DEFAULT_MODEL2,
|
|
445
|
+
run: run2
|
|
446
|
+
};
|
|
447
|
+
var ALLOWED = ["ideogram-character", "flux-edit", "gpt-image-2-edit"];
|
|
448
|
+
var DEFAULT_MODEL3 = "ideogram-character";
|
|
449
|
+
var InputSchema3 = zod.z.object({
|
|
178
450
|
prompt: zod.z.string().min(1).max(8e3),
|
|
179
|
-
model: zod.z.string().optional().describe(`One of: ${ALLOWED.join(", ")}. Defaults to ${
|
|
451
|
+
model: zod.z.string().optional().describe(`One of: ${ALLOWED.join(", ")}. Defaults to ${DEFAULT_MODEL3}.`),
|
|
180
452
|
reference_image_url: zod.z.string().url().optional().describe(
|
|
181
453
|
"Reference image of the character. Required by flux-edit and gpt-image-2-edit; optional for ideogram-character."
|
|
182
454
|
),
|
|
183
455
|
aspect_ratio: zod.z.string().optional()
|
|
184
456
|
});
|
|
185
|
-
var
|
|
457
|
+
var InputJsonSchema3 = {
|
|
186
458
|
type: "object",
|
|
187
459
|
properties: {
|
|
188
460
|
prompt: { type: "string" },
|
|
189
461
|
model: {
|
|
190
462
|
type: "string",
|
|
191
463
|
enum: ALLOWED,
|
|
192
|
-
description: `One of: ${ALLOWED.join(", ")}. Defaults to ${
|
|
464
|
+
description: `One of: ${ALLOWED.join(", ")}. Defaults to ${DEFAULT_MODEL3}.`
|
|
193
465
|
},
|
|
194
466
|
reference_image_url: { type: "string", format: "uri" },
|
|
195
467
|
aspect_ratio: { type: "string" }
|
|
196
468
|
},
|
|
197
469
|
required: ["prompt"]
|
|
198
470
|
};
|
|
199
|
-
async function
|
|
200
|
-
const requested = input.model ??
|
|
471
|
+
async function run3(input, ctx) {
|
|
472
|
+
const requested = input.model ?? DEFAULT_MODEL3;
|
|
201
473
|
if (!ALLOWED.includes(requested)) {
|
|
202
474
|
return {
|
|
203
475
|
kind: "text",
|
|
@@ -205,6 +477,12 @@ async function run2(input, ctx) {
|
|
|
205
477
|
};
|
|
206
478
|
}
|
|
207
479
|
const apiId = requested;
|
|
480
|
+
if ((apiId === "flux-edit" || apiId === "gpt-image-2-edit") && !input.reference_image_url) {
|
|
481
|
+
return {
|
|
482
|
+
kind: "text",
|
|
483
|
+
text: `pixeldojo:character with model="${apiId}" requires reference_image_url (the source image of the character to edit). For generation from a prompt alone, use model="ideogram-character".`
|
|
484
|
+
};
|
|
485
|
+
}
|
|
208
486
|
const body = { prompt: input.prompt };
|
|
209
487
|
if (input.aspect_ratio) body.aspect_ratio = input.aspect_ratio;
|
|
210
488
|
if (input.reference_image_url) {
|
|
@@ -229,21 +507,21 @@ async function run2(input, ctx) {
|
|
|
229
507
|
var characterSkill = {
|
|
230
508
|
name: "pixeldojo:character",
|
|
231
509
|
description: "Generate or edit consistent characters across shots. Pass a reference_image_url for edit-style models.",
|
|
232
|
-
inputZod:
|
|
233
|
-
inputJsonSchema:
|
|
510
|
+
inputZod: InputSchema3,
|
|
511
|
+
inputJsonSchema: InputJsonSchema3,
|
|
234
512
|
allowedModels: [...ALLOWED],
|
|
235
|
-
defaultModel:
|
|
236
|
-
run:
|
|
513
|
+
defaultModel: DEFAULT_MODEL3,
|
|
514
|
+
run: run3
|
|
237
515
|
};
|
|
238
516
|
var ALLOWED2 = ["veo-3.1", "kling-video", "seedance-2"];
|
|
239
|
-
var
|
|
240
|
-
var
|
|
517
|
+
var DEFAULT_MODEL4 = "veo-3.1";
|
|
518
|
+
var InputSchema4 = zod.z.object({
|
|
241
519
|
brief: zod.z.string().min(1).max(8e3).describe("Storyboard brief: scene, shots, mood, action."),
|
|
242
|
-
model: zod.z.string().optional().describe(`One of: ${ALLOWED2.join(", ")}. Defaults to ${
|
|
520
|
+
model: zod.z.string().optional().describe(`One of: ${ALLOWED2.join(", ")}. Defaults to ${DEFAULT_MODEL4}.`),
|
|
243
521
|
aspect_ratio: zod.z.string().optional(),
|
|
244
522
|
duration: zod.z.number().int().positive().optional()
|
|
245
523
|
});
|
|
246
|
-
var
|
|
524
|
+
var InputJsonSchema4 = {
|
|
247
525
|
type: "object",
|
|
248
526
|
properties: {
|
|
249
527
|
brief: {
|
|
@@ -253,15 +531,15 @@ var InputJsonSchema3 = {
|
|
|
253
531
|
model: {
|
|
254
532
|
type: "string",
|
|
255
533
|
enum: ALLOWED2,
|
|
256
|
-
description: `One of: ${ALLOWED2.join(", ")}. Defaults to ${
|
|
534
|
+
description: `One of: ${ALLOWED2.join(", ")}. Defaults to ${DEFAULT_MODEL4}.`
|
|
257
535
|
},
|
|
258
536
|
aspect_ratio: { type: "string" },
|
|
259
537
|
duration: { type: "integer" }
|
|
260
538
|
},
|
|
261
539
|
required: ["brief"]
|
|
262
540
|
};
|
|
263
|
-
async function
|
|
264
|
-
const requested = input.model ??
|
|
541
|
+
async function run4(input, ctx) {
|
|
542
|
+
const requested = input.model ?? DEFAULT_MODEL4;
|
|
265
543
|
if (!ALLOWED2.includes(requested)) {
|
|
266
544
|
return {
|
|
267
545
|
kind: "text",
|
|
@@ -290,21 +568,21 @@ async function run3(input, ctx) {
|
|
|
290
568
|
var storyboardSkill = {
|
|
291
569
|
name: "pixeldojo:storyboard",
|
|
292
570
|
description: "Generate a video storyboard preview from a single brief. v1: single-shot preview; multi-shot orchestration coming.",
|
|
293
|
-
inputZod:
|
|
294
|
-
inputJsonSchema:
|
|
571
|
+
inputZod: InputSchema4,
|
|
572
|
+
inputJsonSchema: InputJsonSchema4,
|
|
295
573
|
allowedModels: [...ALLOWED2],
|
|
296
|
-
defaultModel:
|
|
297
|
-
run:
|
|
574
|
+
defaultModel: DEFAULT_MODEL4,
|
|
575
|
+
run: run4
|
|
298
576
|
};
|
|
299
577
|
var ALLOWED3 = ["magnific-upscaler", "creative-upscale", "face-enhance"];
|
|
300
|
-
var
|
|
301
|
-
var
|
|
578
|
+
var DEFAULT_MODEL5 = "magnific-upscaler";
|
|
579
|
+
var InputSchema5 = zod.z.object({
|
|
302
580
|
image_url: zod.z.string().url().describe("URL of the source image to upscale."),
|
|
303
|
-
model: zod.z.string().optional().describe(`One of: ${ALLOWED3.join(", ")}. Defaults to ${
|
|
581
|
+
model: zod.z.string().optional().describe(`One of: ${ALLOWED3.join(", ")}. Defaults to ${DEFAULT_MODEL5}.`),
|
|
304
582
|
scale_factor: zod.z.number().int().min(1).max(4).optional().describe("Magnific only: 1, 2, or 4. Defaults to 2."),
|
|
305
583
|
creativity: zod.z.number().min(0).max(1).optional().describe("Magnific only: 0 (preserve) \u2192 1 (creative). Defaults to 0.")
|
|
306
584
|
});
|
|
307
|
-
var
|
|
585
|
+
var InputJsonSchema5 = {
|
|
308
586
|
type: "object",
|
|
309
587
|
properties: {
|
|
310
588
|
image_url: { type: "string", format: "uri" },
|
|
@@ -314,8 +592,8 @@ var InputJsonSchema4 = {
|
|
|
314
592
|
},
|
|
315
593
|
required: ["image_url"]
|
|
316
594
|
};
|
|
317
|
-
async function
|
|
318
|
-
const requested = input.model ??
|
|
595
|
+
async function run5(input, ctx) {
|
|
596
|
+
const requested = input.model ?? DEFAULT_MODEL5;
|
|
319
597
|
if (!ALLOWED3.includes(requested)) {
|
|
320
598
|
return {
|
|
321
599
|
kind: "text",
|
|
@@ -349,16 +627,16 @@ async function run4(input, ctx) {
|
|
|
349
627
|
var upscaleSkill = {
|
|
350
628
|
name: "pixeldojo:upscale",
|
|
351
629
|
description: "Enhance an image. Magnific (default) supports configurable scale_factor and creativity; face-enhance for portraits.",
|
|
352
|
-
inputZod:
|
|
353
|
-
inputJsonSchema:
|
|
630
|
+
inputZod: InputSchema5,
|
|
631
|
+
inputJsonSchema: InputJsonSchema5,
|
|
354
632
|
allowedModels: [...ALLOWED3],
|
|
355
|
-
defaultModel:
|
|
356
|
-
run:
|
|
633
|
+
defaultModel: DEFAULT_MODEL5,
|
|
634
|
+
run: run5
|
|
357
635
|
};
|
|
358
|
-
var
|
|
636
|
+
var InputSchema6 = zod.z.object({
|
|
359
637
|
jobId: zod.z.string().min(1).max(200).describe("Job id returned from a prior pixeldojo:* skill handoff.")
|
|
360
638
|
});
|
|
361
|
-
var
|
|
639
|
+
var InputJsonSchema6 = {
|
|
362
640
|
type: "object",
|
|
363
641
|
properties: {
|
|
364
642
|
jobId: {
|
|
@@ -370,7 +648,7 @@ var InputJsonSchema5 = {
|
|
|
370
648
|
};
|
|
371
649
|
var STUCK_HINT_MS = 5 * 60 * 1e3;
|
|
372
650
|
var ABANDON_HINT_MS = 12 * 60 * 1e3;
|
|
373
|
-
async function
|
|
651
|
+
async function run6(input, ctx) {
|
|
374
652
|
const job = await ctx.client.getJob(input.jobId);
|
|
375
653
|
if (job.status === "completed") {
|
|
376
654
|
const urls = extractAssetUrls2(job);
|
|
@@ -437,17 +715,17 @@ function extractAssetUrls2(job) {
|
|
|
437
715
|
var statusSkill = {
|
|
438
716
|
name: "pixeldojo:status",
|
|
439
717
|
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.",
|
|
440
|
-
inputZod:
|
|
441
|
-
inputJsonSchema:
|
|
718
|
+
inputZod: InputSchema6,
|
|
719
|
+
inputJsonSchema: InputJsonSchema6,
|
|
442
720
|
// status is special — it doesn't dispatch to a model
|
|
443
721
|
allowedModels: [],
|
|
444
722
|
defaultModel: "",
|
|
445
|
-
run:
|
|
723
|
+
run: run6
|
|
446
724
|
};
|
|
447
|
-
var
|
|
725
|
+
var InputSchema7 = zod.z.object({
|
|
448
726
|
url: zod.z.string().min(1).max(2048).describe("Product page URL. Shopify, BigCommerce, WooCommerce, or any page with JSON-LD / OpenGraph product metadata.")
|
|
449
727
|
});
|
|
450
|
-
var
|
|
728
|
+
var InputJsonSchema7 = {
|
|
451
729
|
type: "object",
|
|
452
730
|
properties: {
|
|
453
731
|
url: {
|
|
@@ -457,7 +735,7 @@ var InputJsonSchema6 = {
|
|
|
457
735
|
},
|
|
458
736
|
required: ["url"]
|
|
459
737
|
};
|
|
460
|
-
async function
|
|
738
|
+
async function run7(input, ctx) {
|
|
461
739
|
const product = await ctx.client.extractProduct(input.url);
|
|
462
740
|
const summary = [
|
|
463
741
|
`Product: ${product.name}`,
|
|
@@ -477,14 +755,14 @@ function truncate(input, max) {
|
|
|
477
755
|
var fromUrlSkill = {
|
|
478
756
|
name: "pixeldojo:from_url",
|
|
479
757
|
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.",
|
|
480
|
-
inputZod:
|
|
481
|
-
inputJsonSchema:
|
|
758
|
+
inputZod: InputSchema7,
|
|
759
|
+
inputJsonSchema: InputJsonSchema7,
|
|
482
760
|
// No model dispatch — this skill calls a server-side scrape, not a generation.
|
|
483
761
|
allowedModels: [],
|
|
484
762
|
defaultModel: "",
|
|
485
|
-
run:
|
|
763
|
+
run: run7
|
|
486
764
|
};
|
|
487
|
-
var
|
|
765
|
+
var InputSchema8 = zod.z.object({
|
|
488
766
|
productUrl: zod.z.string().url().optional().describe("Product page URL. We extract name + description + images automatically."),
|
|
489
767
|
product: zod.z.object({
|
|
490
768
|
name: zod.z.string().min(1).max(200),
|
|
@@ -500,7 +778,7 @@ var InputSchema7 = zod.z.object({
|
|
|
500
778
|
message: "productUrl or product is required",
|
|
501
779
|
path: ["productUrl"]
|
|
502
780
|
});
|
|
503
|
-
var
|
|
781
|
+
var InputJsonSchema8 = {
|
|
504
782
|
type: "object",
|
|
505
783
|
properties: {
|
|
506
784
|
productUrl: {
|
|
@@ -528,7 +806,7 @@ var InputJsonSchema7 = {
|
|
|
528
806
|
},
|
|
529
807
|
oneOf: [{ required: ["productUrl"] }, { required: ["product"] }]
|
|
530
808
|
};
|
|
531
|
-
async function
|
|
809
|
+
async function run8(input, ctx) {
|
|
532
810
|
const client = ctx.client;
|
|
533
811
|
const result = await client.runCampaign(input);
|
|
534
812
|
if (result.kind === "campaign") {
|
|
@@ -558,18 +836,18 @@ async function run7(input, ctx) {
|
|
|
558
836
|
var campaignSkill = {
|
|
559
837
|
name: "pixeldojo:campaign",
|
|
560
838
|
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.",
|
|
561
|
-
inputZod:
|
|
562
|
-
inputJsonSchema:
|
|
839
|
+
inputZod: InputSchema8,
|
|
840
|
+
inputJsonSchema: InputJsonSchema8,
|
|
563
841
|
// Composition skill — internal sub-jobs route to per-tool models, no
|
|
564
842
|
// single allowed-models list applies here.
|
|
565
843
|
allowedModels: [],
|
|
566
844
|
defaultModel: "",
|
|
567
|
-
run:
|
|
845
|
+
run: run8
|
|
568
846
|
};
|
|
569
|
-
var
|
|
847
|
+
var InputSchema9 = zod.z.object({
|
|
570
848
|
campaignId: zod.z.string().min(1).max(200).describe("Campaign id returned from a prior pixeldojo:campaign handoff.")
|
|
571
849
|
});
|
|
572
|
-
var
|
|
850
|
+
var InputJsonSchema9 = {
|
|
573
851
|
type: "object",
|
|
574
852
|
properties: {
|
|
575
853
|
campaignId: {
|
|
@@ -579,7 +857,7 @@ var InputJsonSchema8 = {
|
|
|
579
857
|
},
|
|
580
858
|
required: ["campaignId"]
|
|
581
859
|
};
|
|
582
|
-
async function
|
|
860
|
+
async function run9(input, ctx) {
|
|
583
861
|
const client = ctx.client;
|
|
584
862
|
const result = await client.getCampaign(input.campaignId);
|
|
585
863
|
if (result.kind === "campaign") {
|
|
@@ -612,21 +890,63 @@ async function run8(input, ctx) {
|
|
|
612
890
|
var campaignStatusSkill = {
|
|
613
891
|
name: "pixeldojo:campaign_status",
|
|
614
892
|
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.",
|
|
615
|
-
inputZod:
|
|
616
|
-
inputJsonSchema:
|
|
893
|
+
inputZod: InputSchema9,
|
|
894
|
+
inputJsonSchema: InputJsonSchema9,
|
|
617
895
|
allowedModels: [],
|
|
618
896
|
defaultModel: "",
|
|
619
|
-
run:
|
|
897
|
+
run: run9
|
|
898
|
+
};
|
|
899
|
+
var InputSchema10 = zod.z.object({
|
|
900
|
+
path: zod.z.string().min(1).max(4096).describe(
|
|
901
|
+
"Absolute local file path to upload. JPEG, PNG, WebP, GIF (\u226450MB) or MP4, WebM, MOV (\u2264200MB). Returns a public URL on the temp CDN; storage auto-expires after 24 hours."
|
|
902
|
+
)
|
|
903
|
+
});
|
|
904
|
+
var InputJsonSchema10 = {
|
|
905
|
+
type: "object",
|
|
906
|
+
properties: {
|
|
907
|
+
path: {
|
|
908
|
+
type: "string",
|
|
909
|
+
description: "Absolute local file path. Allowed: JPEG, PNG, WebP, GIF (\u226450MB) or MP4, WebM, MOV (\u2264200MB). Returns a public URL on the temp CDN; auto-expires after 24h."
|
|
910
|
+
}
|
|
911
|
+
},
|
|
912
|
+
required: ["path"]
|
|
913
|
+
};
|
|
914
|
+
async function run10(input, ctx) {
|
|
915
|
+
let url;
|
|
916
|
+
try {
|
|
917
|
+
url = await uploadLocalFile(input.path, ctx.client);
|
|
918
|
+
} catch (err) {
|
|
919
|
+
if (err instanceof SkillUploadError) {
|
|
920
|
+
return { kind: "text", text: `pixeldojo:upload: ${err.userMessage}` };
|
|
921
|
+
}
|
|
922
|
+
throw err;
|
|
923
|
+
}
|
|
924
|
+
return {
|
|
925
|
+
kind: "text",
|
|
926
|
+
text: `Uploaded ${path.basename(input.path)} \u2192 ${url}
|
|
927
|
+
(temp storage; auto-deletes in ~24h \u2014 pass this URL as image_url to :generate or :edit)`
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
var uploadSkill = {
|
|
931
|
+
name: "pixeldojo:upload",
|
|
932
|
+
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.",
|
|
933
|
+
inputZod: InputSchema10,
|
|
934
|
+
inputJsonSchema: InputJsonSchema10,
|
|
935
|
+
allowedModels: [],
|
|
936
|
+
defaultModel: "",
|
|
937
|
+
run: run10
|
|
620
938
|
};
|
|
621
939
|
|
|
622
940
|
// src/skills/index.ts
|
|
623
941
|
var ALL_SKILLS = [
|
|
624
942
|
generateSkill,
|
|
943
|
+
editSkill,
|
|
625
944
|
characterSkill,
|
|
626
945
|
storyboardSkill,
|
|
627
946
|
upscaleSkill,
|
|
628
947
|
statusSkill,
|
|
629
948
|
fromUrlSkill,
|
|
949
|
+
uploadSkill,
|
|
630
950
|
campaignSkill,
|
|
631
951
|
campaignStatusSkill
|
|
632
952
|
];
|