@pixeldojo/mcp 0.10.0 → 0.12.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/index.js CHANGED
@@ -348,7 +348,7 @@ var init_schema_snap = __esm({
348
348
  ];
349
349
  FALLBACK_KEYS = {
350
350
  image: REFERENCE_FIELD_PRIORITY,
351
- video: ["reference_videos"],
351
+ video: ["reference_videos", "video_url", "video"],
352
352
  audio: ["reference_audios", "audio_url"]
353
353
  };
354
354
  REF_KEYS = [
@@ -1072,7 +1072,8 @@ var init_storyboard = __esm({
1072
1072
  }
1073
1073
  });
1074
1074
  async function run5(input, ctx) {
1075
- const requested = input.model ?? DEFAULT_MODEL5;
1075
+ const clipUrl = input.video_url ?? (input.image_url && isVideoUrl(input.image_url) ? input.image_url : void 0);
1076
+ const requested = input.model ?? (clipUrl ? VIDEO_MODEL : DEFAULT_MODEL5);
1076
1077
  if (!ALLOWED3.includes(requested)) {
1077
1078
  return {
1078
1079
  kind: "text",
@@ -1080,11 +1081,33 @@ async function run5(input, ctx) {
1080
1081
  };
1081
1082
  }
1082
1083
  const apiId = requested;
1084
+ if (apiId === VIDEO_MODEL) {
1085
+ if (!clipUrl) {
1086
+ return {
1087
+ kind: "text",
1088
+ text: 'pixeldojo_upscale with model="video-upscaler" requires video_url (the source clip).'
1089
+ };
1090
+ }
1091
+ if (input.video_duration === void 0) {
1092
+ return {
1093
+ kind: "text",
1094
+ text: 'pixeldojo_upscale with model="video-upscaler" requires video_duration (source clip length in seconds \u2014 it sets the credit cost, 1 credit/second).'
1095
+ };
1096
+ }
1097
+ } else if (!input.image_url || input.image_url === clipUrl) {
1098
+ return {
1099
+ kind: "text",
1100
+ text: input.image_url ? `${apiId} upscales still images, but the source is a video clip. Use model="video-upscaler" with video_url and video_duration (seconds).` : `pixeldojo_upscale with model="${apiId}" requires image_url (the source image).`
1101
+ };
1102
+ }
1083
1103
  const body = {
1084
1104
  prompt: "",
1085
- image: input.image_url
1105
+ ...apiId === VIDEO_MODEL ? { video: clipUrl } : { image: input.image_url }
1086
1106
  };
1087
- if (apiId === "magnific-upscaler") {
1107
+ if (apiId === VIDEO_MODEL) {
1108
+ body.video_duration = input.video_duration;
1109
+ if (input.target_resolution) body.target_resolution = input.target_resolution;
1110
+ } else if (apiId === "magnific-upscaler") {
1088
1111
  const allowed = [2, 4, 8, 16];
1089
1112
  const requestedScale = input.scale_factor ?? 2;
1090
1113
  const nearest = allowed.reduce(
@@ -1112,34 +1135,49 @@ async function run5(input, ctx) {
1112
1135
  (urls) => urls.length === 0 ? `Job ${submitted.jobId} completed but returned no assets.` : `Upscaled. Asset: ${urls[0]}`
1113
1136
  );
1114
1137
  }
1115
- var ALLOWED3, DEFAULT_MODEL5, InputSchema5, InputJsonSchema5, upscaleSkill;
1138
+ var ALLOWED3, DEFAULT_MODEL5, VIDEO_MODEL, VIDEO_RESOLUTIONS, InputSchema5, InputJsonSchema5, upscaleSkill;
1116
1139
  var init_upscale = __esm({
1117
1140
  "src/skills/upscale.ts"() {
1118
1141
  init_poll();
1119
1142
  init_types();
1120
- ALLOWED3 = ["magnific-upscaler", "creative-upscale", "face-enhance"];
1143
+ init_schema_snap();
1144
+ ALLOWED3 = ["magnific-upscaler", "creative-upscale", "face-enhance", "video-upscaler"];
1121
1145
  DEFAULT_MODEL5 = "magnific-upscaler";
1146
+ VIDEO_MODEL = "video-upscaler";
1147
+ VIDEO_RESOLUTIONS = ["720p", "1080p", "2k", "4k"];
1122
1148
  InputSchema5 = z.object({
1123
- image_url: z.string().url().describe("URL of the source image to upscale."),
1124
- model: z.string().optional().describe(`One of: ${ALLOWED3.join(", ")}. Defaults to ${DEFAULT_MODEL5}.`),
1149
+ image_url: z.string().url().optional().describe("URL of the source image to upscale."),
1150
+ video_url: z.string().url().optional().describe("URL of a source video clip \u2014 routes to video-upscaler. MP4 only, max 500 MB."),
1151
+ model: z.string().optional().describe(`One of: ${ALLOWED3.join(", ")}. Defaults to ${DEFAULT_MODEL5} (a clip defaults to ${VIDEO_MODEL}).`),
1125
1152
  scale_factor: z.number().min(1).max(16).optional().describe(
1126
- "Upscale multiplier. Magnific snaps to the nearest of 2/4/8/16 (default 2); creative-upscale accepts 1-10 (default 2); face-enhance accepts 1-8 (default 4)."
1153
+ "Image models only. Magnific snaps to the nearest of 2/4/8/16 (default 2); creative-upscale accepts 1-10 (default 2); face-enhance accepts 1-8 (default 4)."
1127
1154
  ),
1128
- creativity: z.number().min(0).max(1).optional().describe("Magnific only: 0 (preserve) \u2192 1 (creative). Defaults to 0.")
1155
+ creativity: z.number().min(0).max(1).optional().describe("Magnific only: 0 (preserve) \u2192 1 (creative). Defaults to 0."),
1156
+ video_duration: z.number().positive().optional().describe("video-upscaler only, REQUIRED there: source clip length in seconds (its credit meter)."),
1157
+ target_resolution: z.enum(VIDEO_RESOLUTIONS).optional().describe("video-upscaler only: target output resolution. Defaults to 1080p.")
1129
1158
  });
1130
1159
  InputJsonSchema5 = {
1131
1160
  type: "object",
1132
1161
  properties: {
1133
1162
  image_url: { type: "string", format: "uri" },
1163
+ video_url: {
1164
+ type: "string",
1165
+ format: "uri",
1166
+ description: "Source video clip \u2014 routes to video-upscaler."
1167
+ },
1134
1168
  model: { type: "string", enum: ALLOWED3 },
1135
1169
  scale_factor: { type: "number", minimum: 1, maximum: 16 },
1136
- creativity: { type: "number", minimum: 0, maximum: 1 }
1137
- },
1138
- required: ["image_url"]
1170
+ creativity: { type: "number", minimum: 0, maximum: 1 },
1171
+ video_duration: {
1172
+ type: "number",
1173
+ description: "video-upscaler only, required there: source clip length in seconds."
1174
+ },
1175
+ target_resolution: { type: "string", enum: VIDEO_RESOLUTIONS }
1176
+ }
1139
1177
  };
1140
1178
  upscaleSkill = {
1141
1179
  name: "pixeldojo_upscale",
1142
- description: "Enhance an image. Magnific (default) supports configurable scale_factor and creativity; face-enhance for portraits.",
1180
+ description: "Enhance an image or video. Magnific (default) supports configurable scale_factor and creativity; face-enhance for portraits; video-upscaler (FlashVSR, up to 4K) for clips \u2014 pass video_url plus video_duration in seconds.",
1143
1181
  inputZod: InputSchema5,
1144
1182
  inputJsonSchema: InputJsonSchema5,
1145
1183
  allowedModels: [...ALLOWED3],
@@ -1287,6 +1325,9 @@ var init_from_url = __esm({
1287
1325
  };
1288
1326
  }
1289
1327
  });
1328
+ function needsSave(url) {
1329
+ return Boolean(url && url.startsWith("https://temp.pixeldojo.ai/"));
1330
+ }
1290
1331
  async function filmApi(ctx, method, path, body) {
1291
1332
  const res = await fetch(`${ctx.baseUrl}${path}`, {
1292
1333
  method,
@@ -1383,7 +1424,7 @@ function buildScorePlanBody(film) {
1383
1424
  }
1384
1425
  function buildFlatMusicBody(film, all) {
1385
1426
  const runtimeSec = all.reduce((n, sh) => n + (sh.durationSec || 0), 0);
1386
- const ms = Math.min(6e5, Math.max(5e3, Math.ceil(runtimeSec * 1e3 / 5e3) * 5e3));
1427
+ const ms = Math.min(3e5, Math.max(3e4, Math.ceil(runtimeSec * 1e3 / 5e3) * 5e3));
1387
1428
  return {
1388
1429
  prompt: `${LOOK_MUSIC[film.look] ?? LOOK_MUSIC["live-action"]}. Instrumental only, no vocals. One consistent theme, loopable as a background bed under dialogue.`,
1389
1430
  music_length_ms: ms,
@@ -1413,24 +1454,8 @@ It is saved in the Library folder "Film: ${film.title}".`
1413
1454
  await filmApi(ctx, "POST", `/api/films/${filmId}/shots/${sh.id}/generate`, { kind: "frame" });
1414
1455
  }
1415
1456
  const framesDone = all.every((sh) => ["frame-ready", "clip-generating", "clip-ready"].includes(sh.status));
1416
- if (framesDone && all.some((sh) => sh.status === "frame-ready" && !sh.approved)) {
1417
- await filmApi(ctx, "PATCH", `/api/films/${filmId}`, {
1418
- scenes: film.scenes.map((s) => ({
1419
- id: s.id,
1420
- title: s.title,
1421
- location: s.location,
1422
- shots: s.shots.map((sh) => ({
1423
- id: sh.id,
1424
- prompt: sh.prompt,
1425
- characterIds: sh.characterIds ?? [],
1426
- elementIds: sh.elementIds ?? [],
1427
- engine: sh.engine,
1428
- cameraPreset: sh.cameraPreset,
1429
- approved: true,
1430
- durationSec: sh.durationSec
1431
- }))
1432
- }))
1433
- });
1457
+ if (framesDone && all.some((sh) => sh.status === "frame-ready" && needsSave(sh.frameUrl))) {
1458
+ await filmApi(ctx, "POST", `/api/films/${filmId}/save`, { kind: "frame" });
1434
1459
  }
1435
1460
  const inFlight = count(film, (sh) => sh.status === "clip-generating");
1436
1461
  const clipQueue = all.filter((sh) => (sh.status === "frame-ready" || sh.status === "failed" && Boolean(sh.frameUrl)) && framesDone);
@@ -1439,6 +1464,9 @@ It is saved in the Library folder "Film: ${film.title}".`
1439
1464
  }
1440
1465
  const clipsDone = all.length > 0 && all.every((sh) => sh.status === "clip-ready");
1441
1466
  if (clipsDone) {
1467
+ if (all.some((sh) => needsSave(sh.videoUrl))) {
1468
+ await filmApi(ctx, "POST", `/api/films/${filmId}/save`, { kind: "clip" });
1469
+ }
1442
1470
  const hasMusic = film.audioStems.some((st) => st.kind === "music");
1443
1471
  if (soundtrack !== "none" && !hasMusic) {
1444
1472
  if (musicJobId) {
@@ -2013,7 +2041,7 @@ var init_library = __esm({
2013
2041
  };
2014
2042
  librarySkill = {
2015
2043
  name: "pixeldojo_library",
2016
- description: "Search your media library: saved My Media items (permanent) and recent API/MCP generations (expire after 24h), newest first. Returns asset URLs to reuse as reference inputs. Keep a result forever with pixeldojo_save.",
2044
+ description: "Search your media library: saved My Media items (permanent) and recent API/MCP generations (deleted 1 hour after they finish), newest first. Returns asset URLs to reuse as reference inputs.",
2017
2045
  inputZod: InputSchema14,
2018
2046
  inputJsonSchema: InputJsonSchema14,
2019
2047
  allowedModels: [],
@@ -2022,52 +2050,6 @@ var init_library = __esm({
2022
2050
  };
2023
2051
  }
2024
2052
  });
2025
- async function run15(input, ctx) {
2026
- const res = await ctx.client.saveMedia({
2027
- url: input.url,
2028
- prompt: input.prompt,
2029
- tool_name: input.tool_name,
2030
- modality: input.modality,
2031
- job_id: input.job_id
2032
- });
2033
- return {
2034
- kind: "text",
2035
- text: `Saved to My Media (permanent): ${res.url}
2036
- It will appear at https://pixeldojo.ai/my-images and in pixeldojo_library searches.`
2037
- };
2038
- }
2039
- var InputSchema15, InputJsonSchema15, saveSkill;
2040
- var init_save = __esm({
2041
- "src/skills/save.ts"() {
2042
- InputSchema15 = z.object({
2043
- url: z.string().url().describe("The asset URL to save (image or video)."),
2044
- prompt: z.string().optional().describe("The prompt that produced it \u2014 makes it findable in the library later."),
2045
- tool_name: z.string().optional().describe("Tool/model slug that produced it."),
2046
- modality: z.enum(["image", "video", "audio"]).optional(),
2047
- job_id: z.string().optional().describe("The jobId that produced this asset, when known.")
2048
- });
2049
- InputJsonSchema15 = {
2050
- type: "object",
2051
- properties: {
2052
- url: { type: "string", format: "uri" },
2053
- prompt: { type: "string" },
2054
- tool_name: { type: "string" },
2055
- modality: { type: "string", enum: ["image", "video", "audio"] },
2056
- job_id: { type: "string" }
2057
- },
2058
- required: ["url"]
2059
- };
2060
- saveSkill = {
2061
- name: "pixeldojo_save",
2062
- description: "Save a generated image or video into the user's My Media library permanently. Generation outputs otherwise expire after 24 hours \u2014 call this when the user wants to keep a result.",
2063
- inputZod: InputSchema15,
2064
- inputJsonSchema: InputJsonSchema15,
2065
- allowedModels: [],
2066
- defaultModel: "",
2067
- run: run15
2068
- };
2069
- }
2070
- });
2071
2053
  function substitutePlaceholders(args, inputs, prevUrl) {
2072
2054
  const missing = [];
2073
2055
  const fill = (value) => {
@@ -2289,7 +2271,6 @@ __export(skills_exports, {
2289
2271
  librarySkill: () => librarySkill,
2290
2272
  listWorkflowsSkill: () => listWorkflowsSkill,
2291
2273
  runWorkflowSkill: () => runWorkflowSkill,
2292
- saveSkill: () => saveSkill,
2293
2274
  saveWorkflowSkill: () => saveWorkflowSkill,
2294
2275
  statusSkill: () => statusSkill,
2295
2276
  storyboardSkill: () => storyboardSkill,
@@ -2317,7 +2298,6 @@ var init_skills = __esm({
2317
2298
  init_upload();
2318
2299
  init_audio();
2319
2300
  init_library();
2320
- init_save();
2321
2301
  init_workflows();
2322
2302
  ALL_SKILLS = [
2323
2303
  generateSkill,
@@ -2327,7 +2307,6 @@ var init_skills = __esm({
2327
2307
  upscaleSkill,
2328
2308
  audioSkill,
2329
2309
  librarySkill,
2330
- saveSkill,
2331
2310
  saveWorkflowSkill,
2332
2311
  runWorkflowSkill,
2333
2312
  listWorkflowsSkill,
@@ -2392,7 +2371,7 @@ function makeRedactedLogger(target = console) {
2392
2371
  }
2393
2372
 
2394
2373
  // src/server.ts
2395
- var PACKAGE_VERSION = "0.10.0" ;
2374
+ var PACKAGE_VERSION = "0.12.0" ;
2396
2375
  function buildServer(options) {
2397
2376
  if (!options.apiKey) {
2398
2377
  throw new Error(