pixelkiln 0.66.0 → 0.68.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
@@ -11,6 +11,7 @@ var RevisionModeSchema = z.enum([
11
11
  "correct-pixelart",
12
12
  "animate",
13
13
  "animate-pixminimax",
14
+ "animate-skeleton",
14
15
  "interpolate",
15
16
  "edit-animation"
16
17
  ]);
@@ -199,6 +200,16 @@ var CharacterPortraitSchema = z.object({
199
200
  of: z.string().min(1),
200
201
  size: CharacterPortraitSizeSchema.default(64)
201
202
  }).strict();
203
+ var MapScenePlacementSchema = z.union([
204
+ z.object({ oval: z.number().min(0.05).max(0.95) }).strict(),
205
+ z.object({ rectangle: z.number().min(0.05).max(0.95) }).strict(),
206
+ z.object({ mask: z.string().min(1) }).strict()
207
+ ]);
208
+ var MapSceneSchema = z.object({
209
+ /** Manifest-relative PNG of the scene. Any earlier asset's output file works. */
210
+ image: z.string().min(1),
211
+ placement: MapScenePlacementSchema.default({ oval: 0.3 })
212
+ }).strict();
202
213
  var CharacterOutfitSchema = z.object({
203
214
  /** The loop (a character animation asset) to re-clothe, in the same style. */
204
215
  of: z.string().min(1),
@@ -329,6 +340,30 @@ var RevisionSchema = z.object({
329
340
  direction: CharacterDirectionSchema.optional(),
330
341
  /** `animate`/`animate-pixminimax` only: let PixelLab expand the action into a fuller motion description first. */
331
342
  enhancePrompt: z.boolean().optional(),
343
+ /**
344
+ * `animate-skeleton` only: manifest-relative path to a committed
345
+ * `SkeletonSet` JSON file (see `src/skeleton.ts`) — the pose the parent
346
+ * image is already in, plus 3-15 per-frame poses for the motion.
347
+ * Authored via `pixelkiln estimate-skeleton`, then hand-tweaked; never
348
+ * inline in the manifest, the same reasoning `mask`/`lastFrame` are
349
+ * committed files rather than inline pixel data.
350
+ */
351
+ keypointsFile: z.string().min(1).optional(),
352
+ /**
353
+ * `animate-skeleton` only: the body a joint's missing `depth` is taken
354
+ * from. PixelLab's default is `mannequin`; free string like `character`'s
355
+ * own `template` field, validated at the provider layer against the
356
+ * same `mannequin`/`bear`/`cat`/`dog`/`horse`/`lion` vocabulary.
357
+ */
358
+ skeletonTemplate: z.string().min(1).optional(),
359
+ /**
360
+ * `animate-skeleton` only: what the character *looks like*, a noun
361
+ * phrase (colours, clothing, held items) — distinct from the asset's
362
+ * own `prompt`, which this mode sends as the motion's short label
363
+ * (PixelLab's `action`), the same `prompt`-as-motion convention
364
+ * `animate`/`animate-pixminimax` already use.
365
+ */
366
+ description: z.string().min(1).optional(),
332
367
  /**
333
368
  * `image-to-image`/`inpaint` only: which provider engine draws the
334
369
  * edit. `pro-flash` selects PixelLab's Pro Flash edit and inpaint tier
@@ -419,18 +454,41 @@ var RevisionSchema = z.object({
419
454
  path: ["fps"]
420
455
  });
421
456
  }
422
- if (revision.direction !== void 0 && revision.mode !== "animate-pixminimax") {
457
+ if (revision.direction !== void 0 && revision.mode !== "animate-pixminimax" && revision.mode !== "animate-skeleton") {
423
458
  context.addIssue({
424
459
  code: z.ZodIssueCode.custom,
425
- message: "direction applies to animate-pixminimax revisions only",
460
+ message: "direction applies to animate-pixminimax/animate-skeleton revisions only",
461
+ path: ["direction"]
462
+ });
463
+ }
464
+ if (revision.mode === "animate-skeleton" && revision.direction === void 0) {
465
+ context.addIssue({
466
+ code: z.ZodIssueCode.custom,
467
+ message: "animate-skeleton revisions require a direction",
426
468
  path: ["direction"]
427
469
  });
428
470
  }
429
471
  if (revision.frames !== void 0 && revision.frames % 2 !== 0) {
430
472
  context.addIssue({ code: z.ZodIssueCode.custom, message: "frames must be even", path: ["frames"] });
431
473
  }
474
+ if (revision.mode === "animate-skeleton" && !revision.keypointsFile) {
475
+ context.addIssue({
476
+ code: z.ZodIssueCode.custom,
477
+ message: "animate-skeleton revisions require keypointsFile",
478
+ path: ["keypointsFile"]
479
+ });
480
+ }
481
+ for (const field of ["keypointsFile", "skeletonTemplate", "description"]) {
482
+ if (revision[field] !== void 0 && revision.mode !== "animate-skeleton") {
483
+ context.addIssue({
484
+ code: z.ZodIssueCode.custom,
485
+ message: `${field} applies to animate-skeleton revisions only`,
486
+ path: [field]
487
+ });
488
+ }
489
+ }
432
490
  });
433
- var FRAME_SET_REVISION_MODES = ["animate", "animate-pixminimax", "interpolate", "edit-animation"];
491
+ var FRAME_SET_REVISION_MODES = ["animate", "animate-pixminimax", "animate-skeleton", "interpolate", "edit-animation"];
434
492
  var MEMBER_SET_REVISION_MODES = ["reduce-colors", "correct-pixelart", "edit-animation"];
435
493
  var StyleObjectSchema = z.object({
436
494
  /** Generation backend for this style. Omit to inherit the manifest default. */
@@ -846,6 +904,8 @@ var AssetSchema = z.object({
846
904
  portrait: CharacterPortraitSchema.optional(),
847
905
  /** `character` styles: this asset re-clothes an existing loop's frames with a reference outfit. */
848
906
  outfit: CharacterOutfitSchema.optional(),
907
+ /** `map` styles only: draw this object in the style of a scene image. See `MapSceneSchema`. */
908
+ scene: MapSceneSchema.optional(),
849
909
  /** `character` styles, `standard` humanoid bases: this character's proportions, over the style's. */
850
910
  proportions: CharacterProportionsSchema.optional(),
851
911
  /**
@@ -922,7 +982,7 @@ var AssetSchema = z.object({
922
982
  path: ["revision"]
923
983
  });
924
984
  }
925
- const shapes = [asset.revision && "revision", asset.state && "state", asset.animation && "animation", asset.mirror && "mirror", asset.batch && "batch", asset.portrait && "portrait", asset.outfit && "outfit"].filter(Boolean);
985
+ const shapes = [asset.revision && "revision", asset.state && "state", asset.animation && "animation", asset.mirror && "mirror", asset.batch && "batch", asset.portrait && "portrait", asset.outfit && "outfit", asset.scene && "scene"].filter(Boolean);
926
986
  if (shapes.length > 1) {
927
987
  context.addIssue({
928
988
  code: z.ZodIssueCode.custom,
@@ -1062,11 +1122,16 @@ var LockEntrySchema = z.object({
1062
1122
  paletteImageSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1063
1123
  dithering: RevisionDitheringSchema.optional(),
1064
1124
  ditheringStrength: z.number().min(0).max(10).optional(),
1065
- frames: z.number().int().min(4).max(40).optional(),
1125
+ // 4-40 for animate/animate-pixminimax; animate-skeleton's own 3-15
1126
+ // range extends the floor down to 3, hence min(3) here rather than 4.
1127
+ frames: z.number().int().min(3).max(40).optional(),
1066
1128
  fps: z.number().int().min(1).max(60).optional(),
1067
1129
  lastFrameSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1068
1130
  direction: CharacterDirectionSchema.optional(),
1069
1131
  enhancePrompt: z.boolean().optional(),
1132
+ keypointsSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1133
+ skeletonTemplate: z.string().min(1).optional(),
1134
+ description: z.string().min(1).optional(),
1070
1135
  engine: z.enum(["pro-flash"]).optional()
1071
1136
  }).strict().nullable().default(null),
1072
1137
  /** For a mirror: the asset it flips and a hash over that asset's output hashes when this was made. */
@@ -1253,7 +1318,7 @@ function exitCodeFor(err) {
1253
1318
  }
1254
1319
 
1255
1320
  // src/client.ts
1256
- import { z as z2 } from "zod";
1321
+ import { z as z3 } from "zod";
1257
1322
 
1258
1323
  // src/http.ts
1259
1324
  var MAX_RETRIES = 4;
@@ -1303,262 +1368,289 @@ function fetchWithRetry(request, opts = {}) {
1303
1368
  };
1304
1369
  }
1305
1370
 
1371
+ // src/skeleton.ts
1372
+ import { z as z2 } from "zod";
1373
+ var SkeletonKeypointSchema = z2.object({
1374
+ label: z2.string().min(1),
1375
+ x: z2.number().min(0).max(1),
1376
+ y: z2.number().min(0).max(1),
1377
+ z_index: z2.number(),
1378
+ depth: z2.number().optional()
1379
+ }).strict();
1380
+ var SkeletonFrameSchema = z2.array(SkeletonKeypointSchema).length(18);
1381
+ var SkeletonSetSchema = z2.object({
1382
+ firstFrameKeypoints: SkeletonFrameSchema,
1383
+ frames: z2.array(SkeletonFrameSchema).min(3).max(15)
1384
+ }).strict();
1385
+ function parseSkeletonSet(json, file) {
1386
+ const result = SkeletonSetSchema.safeParse(json);
1387
+ if (!result.success) {
1388
+ const issues = result.error.issues.slice(0, 4).map((issue) => `${issue.path.join(".") || "root"}: ${issue.message}`).join("; ");
1389
+ throw new Error(`revision keypoints file ${file} does not match the expected shape: ${issues}`);
1390
+ }
1391
+ return result.data;
1392
+ }
1393
+
1306
1394
  // src/client.ts
1307
1395
  var BASE = process.env.PIXELLAB_API_BASE ?? "https://api.pixellab.ai/v2";
1308
1396
  var MAX_DOWNLOAD_BYTES = 64 * 1024 * 1024;
1309
- var BalanceResponseSchema = z2.object({
1310
- credits: z2.object({ usd: z2.number() }).passthrough(),
1311
- subscription: z2.object({
1312
- generations: z2.number(),
1313
- total: z2.number(),
1314
- plan: z2.string().nullable().optional()
1397
+ var BalanceResponseSchema = z3.object({
1398
+ credits: z3.object({ usd: z3.number() }).passthrough(),
1399
+ subscription: z3.object({
1400
+ generations: z3.number(),
1401
+ total: z3.number(),
1402
+ plan: z3.string().nullable().optional()
1315
1403
  }).passthrough()
1316
1404
  }).passthrough();
1317
- var ObjectSubmitSchema = z2.object({
1318
- background_job_id: z2.string().min(1),
1319
- object_id: z2.string().min(1),
1320
- status: z2.string().default("queued"),
1321
- n_frames: z2.number().int().min(0)
1405
+ var ObjectSubmitSchema = z3.object({
1406
+ background_job_id: z3.string().min(1),
1407
+ object_id: z3.string().min(1),
1408
+ status: z3.string().default("queued"),
1409
+ n_frames: z3.number().int().min(0)
1322
1410
  }).passthrough();
1323
- var MapSubmitSchema = z2.object({
1324
- background_job_id: z2.string().min(1),
1325
- object_id: z2.string().min(1),
1326
- status: z2.string().default("processing")
1411
+ var MapSubmitSchema = z3.object({
1412
+ background_job_id: z3.string().min(1),
1413
+ object_id: z3.string().min(1),
1414
+ status: z3.string().default("processing")
1327
1415
  }).passthrough();
1328
- var UsageSchema = z2.object({
1329
- type: z2.string().optional(),
1330
- usd: z2.number().nullable().optional(),
1331
- generations: z2.number().nullable().optional()
1416
+ var UsageSchema = z3.object({
1417
+ type: z3.string().optional(),
1418
+ usd: z3.number().nullable().optional(),
1419
+ generations: z3.number().nullable().optional()
1332
1420
  }).passthrough().nullable().optional();
1333
- var TilesSubmitSchema = z2.object({
1334
- tile_id: z2.string().min(1),
1335
- background_job_id: z2.string().min(1),
1336
- status: z2.literal("processing").default("processing")
1421
+ var TilesSubmitSchema = z3.object({
1422
+ tile_id: z3.string().min(1),
1423
+ background_job_id: z3.string().min(1),
1424
+ status: z3.literal("processing").default("processing")
1337
1425
  }).passthrough();
1338
- var TilesProSchema = z2.object({
1339
- storage_urls: z2.record(z2.string()),
1340
- kind: z2.string().nullable().default(null),
1341
- tile_rules: z2.record(z2.unknown()).nullable().optional()
1426
+ var TilesProSchema = z3.object({
1427
+ storage_urls: z3.record(z3.string()),
1428
+ kind: z3.string().nullable().default(null),
1429
+ tile_rules: z3.record(z3.unknown()).nullable().optional()
1342
1430
  }).passthrough();
1343
- var TilesetSubmitSchema = z2.object({
1344
- tileset_id: z2.string().min(1),
1345
- background_job_id: z2.string().min(1),
1346
- status: z2.literal("processing").default("processing")
1431
+ var TilesetSubmitSchema = z3.object({
1432
+ tileset_id: z3.string().min(1),
1433
+ background_job_id: z3.string().min(1),
1434
+ status: z3.literal("processing").default("processing")
1347
1435
  }).passthrough();
1348
- var TilesetTileSchema = z2.object({
1349
- id: z2.string().min(1),
1350
- name: z2.string(),
1351
- image: z2.object({ base64: z2.string().min(1), format: z2.string().default("png") }).passthrough(),
1352
- corners: z2.object({ NW: z2.string(), NE: z2.string(), SW: z2.string(), SE: z2.string() }).passthrough(),
1353
- pattern_4x4: z2.object({
1354
- row_0: z2.array(z2.number()),
1355
- row_1: z2.array(z2.number()),
1356
- row_2: z2.array(z2.number()),
1357
- row_3: z2.array(z2.number())
1436
+ var TilesetTileSchema = z3.object({
1437
+ id: z3.string().min(1),
1438
+ name: z3.string(),
1439
+ image: z3.object({ base64: z3.string().min(1), format: z3.string().default("png") }).passthrough(),
1440
+ corners: z3.object({ NW: z3.string(), NE: z3.string(), SW: z3.string(), SE: z3.string() }).passthrough(),
1441
+ pattern_4x4: z3.object({
1442
+ row_0: z3.array(z3.number()),
1443
+ row_1: z3.array(z3.number()),
1444
+ row_2: z3.array(z3.number()),
1445
+ row_3: z3.array(z3.number())
1358
1446
  }).passthrough()
1359
1447
  }).passthrough();
1360
- var TilesetGetSchema = z2.object({
1361
- tileset: z2.object({
1362
- total_tiles: z2.number().int().min(1),
1363
- tile_size: z2.object({ width: z2.number().int().positive(), height: z2.number().int().positive() }).passthrough(),
1364
- terrain_types: z2.array(z2.string()),
1365
- tiles: z2.array(TilesetTileSchema).min(1)
1448
+ var TilesetGetSchema = z3.object({
1449
+ tileset: z3.object({
1450
+ total_tiles: z3.number().int().min(1),
1451
+ tile_size: z3.object({ width: z3.number().int().positive(), height: z3.number().int().positive() }).passthrough(),
1452
+ terrain_types: z3.array(z3.string()),
1453
+ tiles: z3.array(TilesetTileSchema).min(1)
1366
1454
  }).passthrough(),
1367
1455
  usage: UsageSchema
1368
1456
  }).passthrough();
1369
- var IsometricTileSubmitSchema = z2.object({
1370
- tile_id: z2.string().min(1),
1371
- background_job_id: z2.string().min(1),
1372
- status: z2.literal("processing").default("processing")
1457
+ var IsometricTileSubmitSchema = z3.object({
1458
+ tile_id: z3.string().min(1),
1459
+ background_job_id: z3.string().min(1),
1460
+ status: z3.literal("processing").default("processing")
1373
1461
  }).passthrough();
1374
- var IsometricTileGetSchema = z2.object({
1375
- image: z2.object({ base64: z2.string().min(1), format: z2.string().default("png") }).passthrough(),
1462
+ var IsometricTileGetSchema = z3.object({
1463
+ image: z3.object({ base64: z3.string().min(1), format: z3.string().default("png") }).passthrough(),
1376
1464
  usage: UsageSchema
1377
1465
  }).passthrough();
1378
- var UiAssetSubmitSchema = z2.object({
1379
- ui_asset_id: z2.string().min(1),
1380
- background_job_id: z2.string().min(1),
1381
- status: z2.string().default("processing"),
1466
+ var UiAssetSubmitSchema = z3.object({
1467
+ ui_asset_id: z3.string().min(1),
1468
+ background_job_id: z3.string().min(1),
1469
+ status: z3.string().default("processing"),
1382
1470
  usage: UsageSchema
1383
1471
  }).passthrough();
1384
- var UiAssetGetSchema = z2.object({
1385
- id: z2.string().min(1),
1386
- status: z2.string().nullable().default(null),
1387
- image_url: z2.string().nullable().optional(),
1388
- progress_percent: z2.number().nullable().optional(),
1389
- eta_seconds: z2.number().nullable().optional()
1472
+ var UiAssetGetSchema = z3.object({
1473
+ id: z3.string().min(1),
1474
+ status: z3.string().nullable().default(null),
1475
+ image_url: z3.string().nullable().optional(),
1476
+ progress_percent: z3.number().nullable().optional(),
1477
+ eta_seconds: z3.number().nullable().optional()
1390
1478
  }).passthrough();
1391
- var RevisionJobSubmitSchema = z2.object({
1392
- background_job_id: z2.string().min(1),
1393
- status: z2.string().default("processing")
1479
+ var RevisionJobSubmitSchema = z3.object({
1480
+ background_job_id: z3.string().min(1),
1481
+ status: z3.string().default("processing")
1394
1482
  }).passthrough();
1395
- var ObjectAnimationGroupSchema = z2.object({
1396
- animation_group_id: z2.string().min(1),
1397
- display_name: z2.string().nullable().default(null),
1398
- description: z2.string().default(""),
1399
- frame_count: z2.number().int().default(0),
1400
- directions: z2.array(
1401
- z2.object({
1402
- direction: z2.string(),
1403
- storage_urls: z2.record(z2.unknown()).default({}),
1404
- created_at: z2.string()
1483
+ var ObjectAnimationGroupSchema = z3.object({
1484
+ animation_group_id: z3.string().min(1),
1485
+ display_name: z3.string().nullable().default(null),
1486
+ description: z3.string().default(""),
1487
+ frame_count: z3.number().int().default(0),
1488
+ directions: z3.array(
1489
+ z3.object({
1490
+ direction: z3.string(),
1491
+ storage_urls: z3.record(z3.unknown()).default({}),
1492
+ created_at: z3.string()
1405
1493
  }).passthrough()
1406
1494
  ).default([])
1407
1495
  }).passthrough();
1408
- var PixelLabObjectSchema = z2.object({
1409
- id: z2.string().min(1),
1410
- name: z2.string().nullable().default(null),
1411
- state_name: z2.string().nullable().optional(),
1412
- prompt: z2.string().default(""),
1413
- size: z2.object({ width: z2.number().int().positive(), height: z2.number().int().positive() }),
1414
- directions: z2.number().default(0),
1415
- created_at: z2.string(),
1416
- view: z2.string().nullable().default(null),
1417
- preview_url: z2.string().nullable().optional(),
1418
- rotation_urls: z2.record(z2.string().nullable()).nullable().optional(),
1419
- frame_urls: z2.array(z2.string()).nullable().optional(),
1420
- tags: z2.array(z2.string()).default([]),
1421
- status: z2.string().nullable().default(null),
1422
- progress_percent: z2.number().nullable().optional(),
1423
- eta_seconds: z2.number().nullable().optional(),
1424
- group_id: z2.string().nullable().optional(),
1425
- animations: z2.array(ObjectAnimationGroupSchema).default([])
1496
+ var PixelLabObjectSchema = z3.object({
1497
+ id: z3.string().min(1),
1498
+ name: z3.string().nullable().default(null),
1499
+ state_name: z3.string().nullable().optional(),
1500
+ prompt: z3.string().default(""),
1501
+ size: z3.object({ width: z3.number().int().positive(), height: z3.number().int().positive() }),
1502
+ directions: z3.number().default(0),
1503
+ created_at: z3.string(),
1504
+ view: z3.string().nullable().default(null),
1505
+ preview_url: z3.string().nullable().optional(),
1506
+ rotation_urls: z3.record(z3.string().nullable()).nullable().optional(),
1507
+ frame_urls: z3.array(z3.string()).nullable().optional(),
1508
+ tags: z3.array(z3.string()).default([]),
1509
+ status: z3.string().nullable().default(null),
1510
+ progress_percent: z3.number().nullable().optional(),
1511
+ eta_seconds: z3.number().nullable().optional(),
1512
+ group_id: z3.string().nullable().optional(),
1513
+ animations: z3.array(ObjectAnimationGroupSchema).default([])
1426
1514
  }).passthrough();
1427
- var MapObjectSchema = z2.object({
1428
- object_id: z2.string().min(1),
1429
- status: z2.string(),
1430
- description: z2.string().nullable().default(null),
1431
- width: z2.number().nullable().default(null),
1432
- height: z2.number().nullable().default(null),
1433
- download_url: z2.string().nullable().default(null)
1515
+ var MapObjectSchema = z3.object({
1516
+ object_id: z3.string().min(1),
1517
+ status: z3.string(),
1518
+ description: z3.string().nullable().default(null),
1519
+ width: z3.number().nullable().default(null),
1520
+ height: z3.number().nullable().default(null),
1521
+ download_url: z3.string().nullable().default(null)
1434
1522
  }).passthrough();
1435
- var ObjectListSchema = z2.object({ objects: z2.array(PixelLabObjectSchema), total: z2.number().int().min(0) }).passthrough();
1436
- var PixfluxResponseSchema = z2.object({ image: z2.object({ base64: z2.string().min(1) }).passthrough(), usage: z2.unknown().optional() }).passthrough();
1437
- var ReduceColorsResponseSchema = z2.object({
1438
- images: z2.array(z2.object({ base64: z2.string().min(1) }).passthrough()).min(1),
1439
- palette: z2.object({ base64: z2.string().min(1) }).passthrough(),
1440
- n_colors: z2.number().int(),
1441
- usage: z2.unknown().optional()
1523
+ var ObjectListSchema = z3.object({ objects: z3.array(PixelLabObjectSchema), total: z3.number().int().min(0) }).passthrough();
1524
+ var PixfluxResponseSchema = z3.object({ image: z3.object({ base64: z3.string().min(1) }).passthrough(), usage: z3.unknown().optional() }).passthrough();
1525
+ var ReduceColorsResponseSchema = z3.object({
1526
+ images: z3.array(z3.object({ base64: z3.string().min(1) }).passthrough()).min(1),
1527
+ palette: z3.object({ base64: z3.string().min(1) }).passthrough(),
1528
+ n_colors: z3.number().int(),
1529
+ usage: z3.unknown().optional()
1442
1530
  }).passthrough();
1443
- var CorrectPixelartResponseSchema = z2.object({
1444
- images: z2.array(z2.object({ base64: z2.string().min(1) }).passthrough()).min(1),
1445
- usage: z2.unknown().optional()
1531
+ var CorrectPixelartResponseSchema = z3.object({
1532
+ images: z3.array(z3.object({ base64: z3.string().min(1) }).passthrough()).min(1),
1533
+ usage: z3.unknown().optional()
1446
1534
  }).passthrough();
1447
- var ProFlashImageSubmitSchema = z2.object({
1448
- background_job_id: z2.string().min(1),
1449
- image_id: z2.string().min(1),
1450
- source_image_id: z2.string().min(1),
1451
- status: z2.string().default("processing"),
1452
- estimated_generations: z2.number().nullable().optional(),
1453
- usage: z2.unknown().optional()
1535
+ var ProFlashImageSubmitSchema = z3.object({
1536
+ background_job_id: z3.string().min(1),
1537
+ image_id: z3.string().min(1),
1538
+ source_image_id: z3.string().min(1),
1539
+ status: z3.string().default("processing"),
1540
+ estimated_generations: z3.number().nullable().optional(),
1541
+ usage: z3.unknown().optional()
1454
1542
  }).passthrough();
1455
- var UnzoomResponseSchema = z2.object({
1456
- image: z2.object({ base64: z2.string().min(1) }).passthrough(),
1457
- original_size: z2.object({ width: z2.number().int(), height: z2.number().int() }).passthrough(),
1458
- unzoomed_size: z2.object({ width: z2.number().int(), height: z2.number().int() }).passthrough(),
1459
- zoom_factor_detected: z2.number(),
1460
- usage: z2.unknown().optional()
1543
+ var UnzoomResponseSchema = z3.object({
1544
+ image: z3.object({ base64: z3.string().min(1) }).passthrough(),
1545
+ original_size: z3.object({ width: z3.number().int(), height: z3.number().int() }).passthrough(),
1546
+ unzoomed_size: z3.object({ width: z3.number().int(), height: z3.number().int() }).passthrough(),
1547
+ zoom_factor_detected: z3.number(),
1548
+ usage: z3.unknown().optional()
1461
1549
  }).passthrough();
1462
- var FontJobSchema = z2.object({
1463
- job_id: z2.string().min(1),
1464
- status: z2.string(),
1465
- glyph_px: z2.number().int().nullable().optional(),
1466
- download_atlas_url: z2.string().nullable().optional(),
1467
- download_ttf_url: z2.string().nullable().optional(),
1468
- usage: z2.unknown().optional()
1550
+ var FontJobSchema = z3.object({
1551
+ job_id: z3.string().min(1),
1552
+ status: z3.string(),
1553
+ glyph_px: z3.number().int().nullable().optional(),
1554
+ download_atlas_url: z3.string().nullable().optional(),
1555
+ download_ttf_url: z3.string().nullable().optional(),
1556
+ usage: z3.unknown().optional()
1469
1557
  }).passthrough();
1470
- var SelectFramesSchema = z2.object({ created_object_ids: z2.array(z2.string()) }).passthrough();
1471
- var CharacterSubmitSchema = z2.object({
1472
- background_job_id: z2.string().min(1),
1473
- character_id: z2.string().min(1),
1474
- status: z2.string().default("processing"),
1558
+ var SelectFramesSchema = z3.object({ created_object_ids: z3.array(z3.string()) }).passthrough();
1559
+ var EstimateSkeletonResponseSchema = z3.object({
1560
+ keypoints: z3.array(SkeletonKeypointSchema).length(18),
1561
+ usage: z3.unknown().optional()
1562
+ }).passthrough();
1563
+ var CharacterSubmitSchema = z3.object({
1564
+ background_job_id: z3.string().min(1),
1565
+ character_id: z3.string().min(1),
1566
+ status: z3.string().default("processing"),
1475
1567
  usage: UsageSchema
1476
1568
  }).passthrough();
1477
- var AnimateSubmitSchema = z2.object({
1478
- background_job_ids: z2.array(z2.string()),
1479
- directions: z2.array(z2.string()),
1480
- status: z2.string().default("processing"),
1569
+ var AnimateSubmitSchema = z3.object({
1570
+ background_job_ids: z3.array(z3.string()),
1571
+ directions: z3.array(z3.string()),
1572
+ status: z3.string().default("processing"),
1481
1573
  usage: UsageSchema
1482
1574
  }).passthrough();
1483
- var ObjectProSubmitSchema = z2.object({
1484
- object_id: z2.string().min(1),
1485
- background_job_id: z2.string().min(1),
1486
- status: z2.string().default("processing"),
1575
+ var ObjectProSubmitSchema = z3.object({
1576
+ object_id: z3.string().min(1),
1577
+ background_job_id: z3.string().min(1),
1578
+ status: z3.string().default("processing"),
1487
1579
  usage: UsageSchema
1488
1580
  }).passthrough();
1489
- var DirectionSubmissionSchema = z2.object({
1490
- direction: z2.string(),
1491
- status: z2.string(),
1492
- background_job_id: z2.string().nullable().default(null),
1493
- animation_id: z2.string().nullable().default(null)
1581
+ var DirectionSubmissionSchema = z3.object({
1582
+ direction: z3.string(),
1583
+ status: z3.string(),
1584
+ background_job_id: z3.string().nullable().default(null),
1585
+ animation_id: z3.string().nullable().default(null)
1494
1586
  }).passthrough();
1495
- var AnimateObjectSubmitSchema = z2.object({
1496
- animation_group_id: z2.string().min(1),
1497
- object_id: z2.string().min(1),
1498
- mode: z2.string(),
1499
- frame_count: z2.number().int(),
1500
- display_name: z2.string().nullable().default(null),
1501
- description: z2.string().default(""),
1502
- submissions: z2.array(DirectionSubmissionSchema),
1587
+ var AnimateObjectSubmitSchema = z3.object({
1588
+ animation_group_id: z3.string().min(1),
1589
+ object_id: z3.string().min(1),
1590
+ mode: z3.string(),
1591
+ frame_count: z3.number().int(),
1592
+ display_name: z3.string().nullable().default(null),
1593
+ description: z3.string().default(""),
1594
+ submissions: z3.array(DirectionSubmissionSchema),
1503
1595
  usage: UsageSchema
1504
1596
  }).passthrough();
1505
- var PortraitSubmitSchema = z2.object({
1506
- background_job_id: z2.string().min(1),
1507
- status: z2.string().default("processing"),
1597
+ var PortraitSubmitSchema = z3.object({
1598
+ background_job_id: z3.string().min(1),
1599
+ status: z3.string().default("processing"),
1508
1600
  usage: UsageSchema
1509
1601
  }).passthrough();
1510
- var PortraitJobSchema = z2.object({
1511
- status: z2.string(),
1512
- download_url: z2.string().nullable().optional(),
1513
- width: z2.number().int().nullable().optional(),
1514
- height: z2.number().int().nullable().optional()
1602
+ var PortraitJobSchema = z3.object({
1603
+ status: z3.string(),
1604
+ download_url: z3.string().nullable().optional(),
1605
+ width: z3.number().int().nullable().optional(),
1606
+ height: z3.number().int().nullable().optional()
1515
1607
  }).passthrough();
1516
- var SetPortraitSchema = z2.object({
1517
- character_id: z2.string().min(1),
1518
- size: z2.number().int().positive(),
1519
- url: z2.string().min(1),
1608
+ var SetPortraitSchema = z3.object({
1609
+ character_id: z3.string().min(1),
1610
+ size: z3.number().int().positive(),
1611
+ url: z3.string().min(1),
1520
1612
  usage: UsageSchema
1521
1613
  }).passthrough();
1522
- var TransferOutfitSubmitSchema = z2.object({
1523
- background_job_id: z2.string().min(1),
1524
- status: z2.string().default("processing"),
1614
+ var TransferOutfitSubmitSchema = z3.object({
1615
+ background_job_id: z3.string().min(1),
1616
+ status: z3.string().default("processing"),
1525
1617
  usage: UsageSchema
1526
1618
  }).passthrough();
1527
- var CharacterAnimationDirectionSchema = z2.object({
1528
- direction: z2.string(),
1529
- frame_count: z2.number().int().min(0),
1530
- frames: z2.array(z2.string())
1619
+ var CharacterAnimationDirectionSchema = z3.object({
1620
+ direction: z3.string(),
1621
+ frame_count: z3.number().int().min(0),
1622
+ frames: z3.array(z3.string())
1531
1623
  }).passthrough();
1532
- var CharacterAnimationSchema2 = z2.object({
1533
- animation_type: z2.string(),
1534
- display_name: z2.string().nullable().optional(),
1535
- animation_group_id: z2.string().nullable().optional(),
1536
- directions: z2.array(CharacterAnimationDirectionSchema)
1624
+ var CharacterAnimationSchema2 = z3.object({
1625
+ animation_type: z3.string(),
1626
+ display_name: z3.string().nullable().optional(),
1627
+ animation_group_id: z3.string().nullable().optional(),
1628
+ directions: z3.array(CharacterAnimationDirectionSchema)
1537
1629
  }).passthrough();
1538
- var CharacterSchema = z2.object({
1539
- id: z2.string().min(1),
1540
- name: z2.string().default(""),
1541
- state_name: z2.string().nullable().optional(),
1542
- prompt: z2.string().default(""),
1543
- size: z2.object({ width: z2.number().int().positive(), height: z2.number().int().positive() }),
1544
- directions: z2.number().int(),
1545
- created_at: z2.string(),
1546
- updated_at: z2.string().nullable().optional(),
1547
- animation_count: z2.number().int().default(0),
1548
- template_id: z2.string().default("mannequin"),
1549
- view: z2.string().nullable().optional(),
1550
- status: z2.string().default("completed"),
1551
- rotation_urls: z2.record(z2.string().nullable()).nullable().optional(),
1552
- tags: z2.array(z2.string()).default([]),
1553
- group_id: z2.string().nullable().optional(),
1554
- animations: z2.array(CharacterAnimationSchema2).default([]),
1555
- preview_url: z2.string().nullable().optional()
1630
+ var CharacterSchema = z3.object({
1631
+ id: z3.string().min(1),
1632
+ name: z3.string().default(""),
1633
+ state_name: z3.string().nullable().optional(),
1634
+ prompt: z3.string().default(""),
1635
+ size: z3.object({ width: z3.number().int().positive(), height: z3.number().int().positive() }),
1636
+ directions: z3.number().int(),
1637
+ created_at: z3.string(),
1638
+ updated_at: z3.string().nullable().optional(),
1639
+ animation_count: z3.number().int().default(0),
1640
+ template_id: z3.string().default("mannequin"),
1641
+ view: z3.string().nullable().optional(),
1642
+ status: z3.string().default("completed"),
1643
+ rotation_urls: z3.record(z3.string().nullable()).nullable().optional(),
1644
+ tags: z3.array(z3.string()).default([]),
1645
+ group_id: z3.string().nullable().optional(),
1646
+ animations: z3.array(CharacterAnimationSchema2).default([]),
1647
+ preview_url: z3.string().nullable().optional()
1556
1648
  }).passthrough();
1557
- var CharacterListSchema = z2.object({ characters: z2.array(CharacterSchema), total: z2.number().int().min(0) }).passthrough();
1558
- var BackgroundJobSchema = z2.object({
1559
- id: z2.string(),
1560
- status: z2.string(),
1561
- last_response: z2.record(z2.unknown()).nullable().optional(),
1649
+ var CharacterListSchema = z3.object({ characters: z3.array(CharacterSchema), total: z3.number().int().min(0) }).passthrough();
1650
+ var BackgroundJobSchema = z3.object({
1651
+ id: z3.string(),
1652
+ status: z3.string(),
1653
+ last_response: z3.record(z3.unknown()).nullable().optional(),
1562
1654
  /**
1563
1655
  * The billed amount, confirmed live for a `map` object and a standard
1564
1656
  * character base (docs/ENDPOINTS.md, "Limits and billing"); duplicated at
@@ -1566,7 +1658,7 @@ var BackgroundJobSchema = z2.object({
1566
1658
  */
1567
1659
  usage: UsageSchema
1568
1660
  }).passthrough();
1569
- var DeleteAnimationsSchema = z2.object({ success: z2.boolean(), deleted_count: z2.number().int().default(0), error: z2.string().nullable().optional() }).passthrough();
1661
+ var DeleteAnimationsSchema = z3.object({ success: z3.boolean(), deleted_count: z3.number().int().default(0), error: z3.string().nullable().optional() }).passthrough();
1570
1662
  function validateResponse(schema, raw, operation) {
1571
1663
  const parsed = schema.safeParse(raw);
1572
1664
  if (parsed.success) return parsed.data;
@@ -1686,6 +1778,12 @@ var PixelLabClient = class {
1686
1778
  if (args.shading) body.shading = args.shading;
1687
1779
  if (args.detail) body.detail = args.detail;
1688
1780
  if (args.seed != null) body.seed = args.seed;
1781
+ if (args.scene) {
1782
+ const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
1783
+ const placement = args.scene.placement;
1784
+ body.background_image = encode(args.scene.image);
1785
+ body.inpainting = placement.type === "mask" ? { type: "mask", mask_image: encode(placement.mask) } : { type: placement.type, fraction: placement.fraction };
1786
+ }
1689
1787
  return validateResponse(
1690
1788
  MapSubmitSchema,
1691
1789
  await this.request("/map-objects", { method: "POST", body: JSON.stringify(body) }),
@@ -2597,6 +2695,61 @@ var PixelLabClient = class {
2597
2695
  "animate-pixminimax"
2598
2696
  );
2599
2697
  }
2698
+ /**
2699
+ * `/animate-with-skeleton-v3`, beta (tier 1 subscription or higher): poses
2700
+ * one reference image frame-by-frame from a supplied skeleton per frame,
2701
+ * rather than a text motion description. Confirmed against the live
2702
+ * `animate_with_skeleton_v3` MCP tool schema (the same authoritative
2703
+ * source this codebase already treats as ground truth for the
2704
+ * character/object family) — `action`/`description` are both real,
2705
+ * optional wire fields, contrary to an initial guess that this endpoint
2706
+ * took no text input at all; `direction`/`keypoints`/`firstFrameKeypoints`
2707
+ * are required. The example keypoint from that same tool schema,
2708
+ * `{"label": "RIGHT KNEE", "x": 0.52, "y": 0.71, "z_index": 9}`, is what
2709
+ * `SkeletonKeypointSchema` (`src/skeleton.ts`) models. Same
2710
+ * unverified-completed-shape caveat as `animateWithTextV3`/
2711
+ * `animatePixminimax` above — this endpoint has never been called against
2712
+ * a live account by this codebase.
2713
+ */
2714
+ async animateWithSkeletonV3(args) {
2715
+ const body = {
2716
+ first_frame: args.firstFrame,
2717
+ first_frame_keypoints: args.firstFrameKeypoints,
2718
+ keypoints: args.keypoints,
2719
+ direction: args.direction
2720
+ };
2721
+ if (args.templateId) body.template_id = args.templateId;
2722
+ if (args.action) body.action = args.action;
2723
+ if (args.description) body.description = args.description;
2724
+ if (args.seed != null) body.seed = args.seed;
2725
+ if (args.noBackground != null) body.no_background = args.noBackground;
2726
+ return validateResponse(
2727
+ RevisionJobSubmitSchema,
2728
+ await this.request("/animate-with-skeleton-v3", { method: "POST", body: JSON.stringify(body) }),
2729
+ "animate-with-skeleton-v3"
2730
+ );
2731
+ }
2732
+ /**
2733
+ * `/estimate-skeleton`: auto-derives a reference image's own 18-joint
2734
+ * keypoints, directly usable as `animateWithSkeletonV3`'s
2735
+ * `firstFrameKeypoints` (or, run against a candidate pose image, as one
2736
+ * entry of its `keypoints` sequence). Not called anywhere in this
2737
+ * codebase's own submit/poll pipeline — deliberately kept outside it (see
2738
+ * `runEstimateSkeleton` in `src/cli/commands`) so the only PixelLab
2739
+ * endpoint composed into `animate-with-skeleton-v3`'s submission is that
2740
+ * one endpoint itself, not two unverified calls chained together.
2741
+ * Synchronous per this endpoint's own framing; unverified against a live
2742
+ * account (see `EstimateSkeletonResponseSchema`'s own caveat).
2743
+ */
2744
+ async estimateSkeleton(args) {
2745
+ const body = { image: args.image };
2746
+ const res = validateResponse(
2747
+ EstimateSkeletonResponseSchema,
2748
+ await this.request("/estimate-skeleton", { method: "POST", body: JSON.stringify(body) }),
2749
+ "estimate-skeleton"
2750
+ );
2751
+ return { keypoints: res.keypoints, usage: res.usage };
2752
+ }
2600
2753
  async getBackgroundJob(jobId) {
2601
2754
  const raw = await this.request(`/background-jobs/${encodeURIComponent(jobId)}`);
2602
2755
  return validateResponse(BackgroundJobSchema, raw, "background-jobs/{id}");
@@ -3253,6 +3406,11 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
3253
3406
  uiPieces: spec.uiPieces,
3254
3407
  uiElements: spec.uiElements,
3255
3408
  uiColorPalette: spec.uiColorPalette,
3409
+ // Scene and mask enter as their bytes' hashes, never their paths.
3410
+ scene: spec.scene ? {
3411
+ image: spec.scene.sha256,
3412
+ placement: spec.scene.placement.type === "mask" ? { type: "mask", mask: spec.scene.placement.sha256 } : spec.scene.placement
3413
+ } : void 0,
3256
3414
  styleTraits: spec.styleTraits,
3257
3415
  styleImages: styleImageHashes,
3258
3416
  // A character family: the engine and rotations of a base, the parent's
@@ -3331,6 +3489,9 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
3331
3489
  lastFrameSha256: spec.revision.lastFrameSha256,
3332
3490
  direction: spec.revision.direction,
3333
3491
  enhancePrompt: spec.revision.enhancePrompt,
3492
+ keypointsSha256: spec.revision.keypointsSha256,
3493
+ skeletonTemplate: spec.revision.skeletonTemplate,
3494
+ description: spec.revision.description,
3334
3495
  engine: spec.revision.engine
3335
3496
  } : void 0
3336
3497
  })
@@ -3419,6 +3580,18 @@ function objectProCost(spec) {
3419
3580
  const south = object.reference;
3420
3581
  return south ? proFlashObjectCost(south.width, south.height, object.directions, true) : proFlashObjectCost(spec.width, spec.height, object.directions, false);
3421
3582
  }
3583
+ function animateSkeletonCost(frames) {
3584
+ const anchors = [[3, 2], [8, 3], [15, 4]];
3585
+ for (let i = 0; i < anchors.length - 1; i++) {
3586
+ const [lowFrames, lowCost] = anchors[i];
3587
+ const [highFrames, highCost] = anchors[i + 1];
3588
+ if (frames >= lowFrames && frames <= highFrames) {
3589
+ const t = (frames - lowFrames) / (highFrames - lowFrames);
3590
+ return lowCost + t * (highCost - lowCost);
3591
+ }
3592
+ }
3593
+ return frames <= anchors[0][0] ? anchors[0][1] : anchors[anchors.length - 1][1];
3594
+ }
3422
3595
  var CHARACTER_TEMPLATES = ["mannequin", "bear", "cat", "dog", "horse", "lion"];
3423
3596
  var PRO_FLASH_TEMPLATES = [...CHARACTER_TEMPLATES, "custom"];
3424
3597
  function encodeAnimationJob(job) {
@@ -3469,6 +3642,8 @@ var PixelLabProvider = class _PixelLabProvider {
3469
3642
  supports(generator) {
3470
3643
  return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character" || generator === "isometricTile" || generator === "objectPro" || generator === "uiAsset" || generator === "uiElement" || generator === "imageProFlash";
3471
3644
  }
3645
+ /** `/map-objects` style matching: `background_image` plus `inpainting`. */
3646
+ supportsMapScene = true;
3472
3647
  /**
3473
3648
  * `inpaint` (`/inpaint-v3`, a mask) and `image-to-image` (`/edit-images-v2`,
3474
3649
  * no mask) both exist on PixelLab, and so do `reduce-colors`
@@ -3477,7 +3652,9 @@ var PixelLabProvider = class _PixelLabProvider {
3477
3652
  * rather than a described edit — and `animate`/`animate-pixminimax`
3478
3653
  * (`/animate-with-text-v3`, `/animate-pixminimax`), which animate any loose
3479
3654
  * image from a text description with no character/object resource
3480
- * required — and `interpolate` (`/interpolation-v2`, the in-betweens
3655
+ * required — `animate-skeleton` (`/animate-with-skeleton-v3`, posing the
3656
+ * source from a supplied per-frame skeleton instead of a text description)
3657
+ * — and `interpolate` (`/interpolation-v2`, the in-betweens
3481
3658
  * from one keyframe to another) and `edit-animation`
3482
3659
  * (`/edit-animation-v2`, one text edit applied across a whole frame set).
3483
3660
  * `outpaint` does not: there is no canvas-expansion endpoint in the API,
@@ -3485,7 +3662,7 @@ var PixelLabProvider = class _PixelLabProvider {
3485
3662
  * outpaint path yet.
3486
3663
  */
3487
3664
  supportsRevision(mode2) {
3488
- return mode2 === "inpaint" || mode2 === "image-to-image" || mode2 === "reduce-colors" || mode2 === "correct-pixelart" || mode2 === "animate" || mode2 === "animate-pixminimax" || mode2 === "interpolate" || mode2 === "edit-animation";
3665
+ return mode2 === "inpaint" || mode2 === "image-to-image" || mode2 === "reduce-colors" || mode2 === "correct-pixelart" || mode2 === "animate" || mode2 === "animate-pixminimax" || mode2 === "animate-skeleton" || mode2 === "interpolate" || mode2 === "edit-animation";
3489
3666
  }
3490
3667
  /** PixelLab's own constraints: submissions must be >2s apart, and
3491
3668
  * background jobs in flight are capped by subscription tier (Tier 1=8,
@@ -3516,6 +3693,10 @@ var PixelLabProvider = class _PixelLabProvider {
3516
3693
  if (spec.revision?.mode === "reduce-colors" || spec.revision?.mode === "correct-pixelart") {
3517
3694
  return { unit: "generations", amount: 0.1, candidates: 1 };
3518
3695
  }
3696
+ if (spec.revision?.mode === "animate-skeleton") {
3697
+ const frames = spec.revision.frames ?? 8;
3698
+ return { unit: "generations", amount: Math.ceil(animateSkeletonCost(frames)), candidates: 1 };
3699
+ }
3519
3700
  if (spec.revision?.mode === "interpolate" || spec.revision?.mode === "edit-animation") {
3520
3701
  const width = spec.revision.sourceWidth ?? spec.width;
3521
3702
  const height = spec.revision.sourceHeight ?? spec.height;
@@ -3597,6 +3778,18 @@ var PixelLabProvider = class _PixelLabProvider {
3597
3778
  throw new Error(`PixelLab correct-pixelart source is ${width}x${height}; the API takes at most 1024 pixels per side`);
3598
3779
  }
3599
3780
  }
3781
+ if (spec.revision?.mode === "animate-skeleton") {
3782
+ const { sourceWidth: width, sourceHeight: height, frames, skeletonTemplate } = spec.revision;
3783
+ if (width != null && height != null && (width > 256 || height > 256)) {
3784
+ throw new Error(`PixelLab animate-skeleton source is ${width}x${height}; the API takes at most 256 pixels per side`);
3785
+ }
3786
+ if (frames != null && (frames < 3 || frames > 15)) {
3787
+ throw new Error(`PixelLab animate-skeleton takes 3 to 15 frames; the keypoints file has ${frames}`);
3788
+ }
3789
+ if (skeletonTemplate && !CHARACTER_TEMPLATES.includes(skeletonTemplate)) {
3790
+ throw new Error(`PixelLab animate-skeleton template "${skeletonTemplate}" is not one of ${CHARACTER_TEMPLATES.join(", ")}`);
3791
+ }
3792
+ }
3600
3793
  if (spec.revision?.mode === "animate" || spec.revision?.mode === "animate-pixminimax") {
3601
3794
  const { sourceWidth: width, sourceHeight: height, mode: mode2, frames, lastFrameWidth, lastFrameHeight } = spec.revision;
3602
3795
  if (width != null && height != null && (width > 256 || height > 256)) {
@@ -4596,7 +4789,8 @@ var PixelLabProvider = class _PixelLabProvider {
4596
4789
  outline: spec.outline,
4597
4790
  shading: spec.shading,
4598
4791
  detail: spec.detail,
4599
- seed: spec.seed
4792
+ seed: spec.seed,
4793
+ scene: spec.scene ? mapScenePayload(spec) : void 0
4600
4794
  });
4601
4795
  return { jobId: res.object_id, metadata: { backgroundJobId: res.background_job_id } };
4602
4796
  }
@@ -4746,6 +4940,27 @@ var PixelLabProvider = class _PixelLabProvider {
4746
4940
  });
4747
4941
  return { jobId: res2.background_job_id };
4748
4942
  }
4943
+ if (revision.mode === "animate-skeleton") {
4944
+ if (!revision.keypointsFile || !revision.keypointsSha256 || !revision.skeleton) {
4945
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision keypoints are not ready`);
4946
+ }
4947
+ const keypointsBytes = readFileSync(revision.keypointsFile);
4948
+ if (sha256(keypointsBytes) !== revision.keypointsSha256) {
4949
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision keypoints changed after the manifest was resolved`);
4950
+ }
4951
+ const res2 = await this.client.animateWithSkeletonV3({
4952
+ firstFrame: image,
4953
+ firstFrameKeypoints: revision.skeleton.firstFrameKeypoints,
4954
+ keypoints: revision.skeleton.frames,
4955
+ direction: revision.direction,
4956
+ templateId: revision.skeletonTemplate,
4957
+ action: spec.prompt,
4958
+ description: revision.description,
4959
+ seed: spec.seed,
4960
+ noBackground: spec.noBackground
4961
+ });
4962
+ return { jobId: res2.background_job_id };
4963
+ }
4749
4964
  if (revision.strength != null) {
4750
4965
  throw new Error(
4751
4966
  `${spec.styleId}/${spec.assetId}: PixelLab image-to-image revisions take no strength; edit-images-v2 always applies the full instruction`
@@ -4811,7 +5026,7 @@ var PixelLabProvider = class _PixelLabProvider {
4811
5026
  if (revisionMode === "reduce-colors" || revisionMode === "correct-pixelart") {
4812
5027
  return this.pollCachedRevision(jobId, context);
4813
5028
  }
4814
- if (revisionMode === "animate" || revisionMode === "animate-pixminimax" || revisionMode === "interpolate" || revisionMode === "edit-animation") {
5029
+ if (revisionMode === "animate" || revisionMode === "animate-pixminimax" || revisionMode === "animate-skeleton" || revisionMode === "interpolate" || revisionMode === "edit-animation") {
4815
5030
  return this.pollAnimateRevision(jobId, context);
4816
5031
  }
4817
5032
  if (context?.spec?.revision) return this.pollRevision(jobId);
@@ -5452,6 +5667,21 @@ function findObjectAnimation(object, name, direction, animationId) {
5452
5667
  }
5453
5668
  return null;
5454
5669
  }
5670
+ function mapScenePayload(spec) {
5671
+ const scene = spec.scene;
5672
+ const read = (file, hash, label) => {
5673
+ const bytes = readFileSync(file);
5674
+ if (!hash || sha256(bytes) !== hash) {
5675
+ throw new Error(`${spec.styleId}/${spec.assetId}: ${label} changed after the manifest was resolved`);
5676
+ }
5677
+ return { base64: bytes.toString("base64"), format: "png" };
5678
+ };
5679
+ const placement = scene.placement;
5680
+ return {
5681
+ image: read(scene.file, scene.sha256, "scene image"),
5682
+ placement: placement.type === "mask" ? { type: "mask", mask: read(placement.file, placement.sha256, "scene mask") } : placement
5683
+ };
5684
+ }
5455
5685
 
5456
5686
  // src/providers/retrodiffusion.ts
5457
5687
  var DEFAULT_BASE_URL = "https://api.retrodiffusion.ai/v1";
@@ -6546,11 +6776,12 @@ var ComfyUIProvider = class _ComfyUIProvider {
6546
6776
  /**
6547
6777
  * Everything else resolves generically to whatever the user's own
6548
6778
  * workflow does with `bindings.sourceImage`. The frame-set modes
6549
- * (`animate`, `animate-pixminimax`, `interpolate`, `edit-animation`) are
6550
- * the exception: they produce an ordered frame set, and this adapter's
6551
- * revision path always writes a single output image (see `submit`/`fetch`
6552
- * below) — a real structural gap, not a missing binding, so they are
6553
- * rejected here rather than only failing once `validate` runs.
6779
+ * (`animate`, `animate-pixminimax`, `animate-skeleton`, `interpolate`,
6780
+ * `edit-animation`) are the exception: they produce an ordered frame set,
6781
+ * and this adapter's revision path always writes a single output image
6782
+ * (see `submit`/`fetch` below) — a real structural gap, not a missing
6783
+ * binding, so they are rejected here rather than only failing once
6784
+ * `validate` runs.
6554
6785
  */
6555
6786
  supportsRevision(mode2) {
6556
6787
  return !FRAME_SET_REVISION_MODES.includes(mode2);
@@ -7230,6 +7461,7 @@ var FakeProvider = class {
7230
7461
  supports(generator) {
7231
7462
  return generator === "1dir" || generator === "map" || generator === "character";
7232
7463
  }
7464
+ supportsMapScene = true;
7233
7465
  supportsRevision(_mode) {
7234
7466
  return true;
7235
7467
  }
@@ -8453,6 +8685,8 @@ async function resolveSpecs(loaded, filter) {
8453
8685
  const paletteImage = paletteImageFile ? await optionalRevisionImage(paletteImageFile, "revision palette image", "png") : null;
8454
8686
  const lastFrameFile = asset.revision.lastFrame ? path5.resolve(root, asset.revision.lastFrame) : void 0;
8455
8687
  const lastFrame = lastFrameFile ? await optionalRevisionImage(lastFrameFile, "revision last frame") : null;
8688
+ const keypointsFile = asset.revision.keypointsFile ? path5.resolve(root, asset.revision.keypointsFile) : void 0;
8689
+ const keypoints = keypointsFile ? await optionalSkeletonSet(keypointsFile) : null;
8456
8690
  resolved.revision = {
8457
8691
  mode: asset.revision.mode,
8458
8692
  sourceAssetId: asset.revision.from,
@@ -8493,6 +8727,18 @@ async function resolveSpecs(loaded, filter) {
8493
8727
  } : {},
8494
8728
  ...asset.revision.direction ? { direction: asset.revision.direction } : {},
8495
8729
  ...asset.revision.enhancePrompt == null ? {} : { enhancePrompt: asset.revision.enhancePrompt },
8730
+ ...keypointsFile ? {
8731
+ keypointsFile,
8732
+ keypointsSha256: keypoints?.hash ?? null,
8733
+ skeleton: keypoints?.skeleton ?? null,
8734
+ // The manifest's own `frames` field stays rejected for this
8735
+ // mode (superRefine, types.ts) — the real frame count comes
8736
+ // from the keypoints file once it exists, not a manifest
8737
+ // number, so plan can only know it once authored.
8738
+ ...keypoints ? { frames: keypoints.skeleton.frames.length } : {}
8739
+ } : {},
8740
+ ...asset.revision.skeletonTemplate ? { skeletonTemplate: asset.revision.skeletonTemplate } : {},
8741
+ ...asset.revision.description ? { description: asset.revision.description } : {},
8496
8742
  ...asset.revision.engine ? { engine: asset.revision.engine } : {}
8497
8743
  };
8498
8744
  }
@@ -8546,6 +8792,7 @@ async function resolveSpecs(loaded, filter) {
8546
8792
  };
8547
8793
  }
8548
8794
  }
8795
+ if (asset.scene) resolved.scene = await resolveMapScene(root, styleId, assetId, resolved.generator, asset.scene, activeProvider);
8549
8796
  resolved.specHash = specHash(
8550
8797
  resolved,
8551
8798
  styleImageHashes,
@@ -8604,6 +8851,32 @@ function pngPath(file) {
8604
8851
  const extension = path5.extname(file);
8605
8852
  return extension ? `${file.slice(0, -extension.length)}.png` : `${file}.png`;
8606
8853
  }
8854
+ async function resolveMapScene(root, styleId, assetId, generator, scene, provider) {
8855
+ if (generator !== "map") {
8856
+ throw new Error(`assets.${assetId}.scene: only a map object can be drawn into a scene; style "${styleId}" uses "${generator}"`);
8857
+ }
8858
+ if (!provider.supportsMapScene) {
8859
+ throw new Error(`assets.${assetId}.scene: provider "${provider.id}" cannot draw into a scene; PixelLab's /map-objects can`);
8860
+ }
8861
+ const file = path5.resolve(root, scene.image);
8862
+ const image = await optionalRevisionImage(file, "scene image", "png");
8863
+ const base = { file, sha256: image?.hash ?? null, width: image?.width ?? null, height: image?.height ?? null };
8864
+ const placement = scene.placement;
8865
+ if ("mask" in placement) {
8866
+ const maskFile = path5.resolve(root, placement.mask);
8867
+ const mask = await optionalRevisionImage(maskFile, "scene mask", "png");
8868
+ if (image && mask && (image.width !== mask.width || image.height !== mask.height)) {
8869
+ throw new Error(
8870
+ `assets.${assetId}.scene: the mask is ${mask.width}x${mask.height}; the scene ${scene.image} is ${image.width}x${image.height}`
8871
+ );
8872
+ }
8873
+ return {
8874
+ ...base,
8875
+ placement: { type: "mask", file: maskFile, sha256: mask?.hash ?? null, width: mask?.width ?? null, height: mask?.height ?? null }
8876
+ };
8877
+ }
8878
+ return "oval" in placement ? { ...base, placement: { type: "oval", fraction: placement.oval } } : { ...base, placement: { type: "rectangle", fraction: placement.rectangle } };
8879
+ }
8607
8880
  async function optionalRevisionImage(file, label, requiredFormat) {
8608
8881
  if (!existsSync4(file)) return null;
8609
8882
  const bytes = await readFile3(file);
@@ -8614,6 +8887,17 @@ async function optionalRevisionImage(file, label, requiredFormat) {
8614
8887
  }
8615
8888
  return { hash: sha256(bytes), ...metadata };
8616
8889
  }
8890
+ async function optionalSkeletonSet(file) {
8891
+ if (!existsSync4(file)) return null;
8892
+ const bytes = await readFile3(file);
8893
+ let json;
8894
+ try {
8895
+ json = JSON.parse(bytes.toString("utf8"));
8896
+ } catch (error) {
8897
+ throw new Error(`revision keypoints file ${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
8898
+ }
8899
+ return { hash: sha256(bytes), skeleton: parseSkeletonSet(json, file) };
8900
+ }
8617
8901
  async function revisionSourceMembers(stem, label) {
8618
8902
  if (existsSync4(stem)) return null;
8619
8903
  const at = (role) => memberPath(stem, role, 0, 2, MediaType.PNG);
@@ -10263,6 +10547,7 @@ async function inspectRevisionReadiness(spec, lock) {
10263
10547
  if (spec.character?.parentSpec) return inspectCharacterParent(spec, lock, /* @__PURE__ */ new Set());
10264
10548
  if (spec.character?.styleAnchor) return inspectStyleAnchor(spec, lock, /* @__PURE__ */ new Set());
10265
10549
  if (spec.objectPro?.parentSpec) return inspectObjectProParent(spec, lock, /* @__PURE__ */ new Set());
10550
+ if (spec.scene) return inspectScene(spec);
10266
10551
  if (!spec.revision) return null;
10267
10552
  return inspectRevision(spec, lock, /* @__PURE__ */ new Set());
10268
10553
  }
@@ -10280,6 +10565,16 @@ async function inspectStyleAnchor(spec, lock, seen) {
10280
10565
  }
10281
10566
  return { ready: true, reason: `${label} is current` };
10282
10567
  }
10568
+ async function inspectScene(spec) {
10569
+ const scene = spec.scene;
10570
+ const files = [{ label: "scene image", file: scene.file, sha256: scene.sha256 }];
10571
+ if (scene.placement.type === "mask") files.push({ label: "scene mask", file: scene.placement.file, sha256: scene.placement.sha256 });
10572
+ for (const { label, file, sha256: sha2562 } of files) {
10573
+ if (!sha2562 || !existsSync7(file)) return { ready: false, reason: `${label} does not exist yet: ${file}` };
10574
+ if (await sha256File(file) !== sha2562) return { ready: false, reason: `${label} changed after the manifest was resolved` };
10575
+ }
10576
+ return { ready: true, reason: "scene is current" };
10577
+ }
10283
10578
  async function inspectMirror(spec, lock, seen) {
10284
10579
  const source = spec.mirror.sourceSpec;
10285
10580
  const label = `mirror source ${spec.styleId}/${spec.mirror.sourceAssetId}`;
@@ -10736,47 +11031,47 @@ import path14 from "path";
10736
11031
  import { existsSync as existsSync9 } from "fs";
10737
11032
  import { readdir, readFile as readFile7 } from "fs/promises";
10738
11033
  import { fileURLToPath } from "url";
10739
- import { z as z3 } from "zod";
11034
+ import { z as z4 } from "zod";
10740
11035
  var SHA256_RE = /^[0-9a-f]{64}$/;
10741
11036
  var RECIPE_ID_RE = /^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/;
10742
11037
  var VERSION_RE = /^\d+\.\d+\.\d+$/;
10743
11038
  var RECIPE_FILE = "pixelkiln.recipe.json";
10744
11039
  var RECIPE_DIR_TOKEN = "{{recipeDir}}";
10745
- var PortablePathSchema = z3.string().min(1).refine((value) => {
11040
+ var PortablePathSchema = z4.string().min(1).refine((value) => {
10746
11041
  if (value.includes("\\") || path14.posix.isAbsolute(value)) return false;
10747
11042
  const parts = value.split("/");
10748
11043
  return !parts.includes("") && !parts.includes(".") && !parts.includes("..");
10749
11044
  }, "expected a portable relative path without '.', '..', or backslashes");
10750
- var RecipeBindingSchema = z3.object({
10751
- nodeId: z3.string().min(1),
10752
- input: z3.string().min(1)
11045
+ var RecipeBindingSchema = z4.object({
11046
+ nodeId: z4.string().min(1),
11047
+ input: z4.string().min(1)
10753
11048
  }).strict();
10754
- var RecipeSchema = z3.object({
10755
- $schema: z3.string().url().optional(),
10756
- format: z3.literal("pixelkiln-recipe"),
10757
- schemaVersion: z3.literal(1),
10758
- id: z3.string().regex(RECIPE_ID_RE, "expected provider/name"),
10759
- version: z3.string().regex(VERSION_RE, "expected x.y.z"),
10760
- provider: z3.string().regex(/^[a-z0-9][a-z0-9-]*$/),
10761
- summary: z3.string().min(1).max(240),
10762
- files: z3.array(z3.object({
11049
+ var RecipeSchema = z4.object({
11050
+ $schema: z4.string().url().optional(),
11051
+ format: z4.literal("pixelkiln-recipe"),
11052
+ schemaVersion: z4.literal(1),
11053
+ id: z4.string().regex(RECIPE_ID_RE, "expected provider/name"),
11054
+ version: z4.string().regex(VERSION_RE, "expected x.y.z"),
11055
+ provider: z4.string().regex(/^[a-z0-9][a-z0-9-]*$/),
11056
+ summary: z4.string().min(1).max(240),
11057
+ files: z4.array(z4.object({
10763
11058
  path: PortablePathSchema,
10764
- role: z3.enum(["workflow", "reference"]),
10765
- sha256: z3.string().regex(SHA256_RE)
11059
+ role: z4.enum(["workflow", "reference"]),
11060
+ sha256: z4.string().regex(SHA256_RE)
10766
11061
  }).strict()).default([]),
10767
- models: z3.array(z3.object({
11062
+ models: z4.array(z4.object({
10768
11063
  path: PortablePathSchema,
10769
- sha256: z3.string().regex(SHA256_RE),
10770
- source: z3.string().url(),
10771
- license: z3.string().min(1)
11064
+ sha256: z4.string().regex(SHA256_RE),
11065
+ source: z4.string().url(),
11066
+ license: z4.string().min(1)
10772
11067
  }).strict()).default([]),
10773
- styleId: z3.string().regex(/^[a-z0-9][a-z0-9-]*$/),
11068
+ styleId: z4.string().regex(/^[a-z0-9][a-z0-9-]*$/),
10774
11069
  style: StyleSchema,
10775
- workflow: z3.object({
11070
+ workflow: z4.object({
10776
11071
  path: PortablePathSchema,
10777
- outputNodeId: z3.string().min(1),
10778
- numImages: z3.number().int().min(1).max(16),
10779
- bindings: z3.object({
11072
+ outputNodeId: z4.string().min(1),
11073
+ numImages: z4.number().int().min(1).max(16),
11074
+ bindings: z4.object({
10780
11075
  prompt: RecipeBindingSchema,
10781
11076
  width: RecipeBindingSchema.optional(),
10782
11077
  height: RecipeBindingSchema.optional(),
@@ -10787,67 +11082,67 @@ var RecipeSchema = z3.object({
10787
11082
  strength: RecipeBindingSchema.optional()
10788
11083
  }).strict()
10789
11084
  }).strict().optional(),
10790
- quality: z3.object({
10791
- stage: z3.enum(["composition-source", "production-candidate"]),
10792
- workingCanvas: z3.object({
10793
- width: z3.number().int().min(16),
10794
- height: z3.number().int().min(16)
11085
+ quality: z4.object({
11086
+ stage: z4.enum(["composition-source", "production-candidate"]),
11087
+ workingCanvas: z4.object({
11088
+ width: z4.number().int().min(16),
11089
+ height: z4.number().int().min(16)
10795
11090
  }).strict(),
10796
- recommendedNativeSize: z3.object({
10797
- min: z3.number().int().min(1),
10798
- max: z3.number().int().min(1)
11091
+ recommendedNativeSize: z4.object({
11092
+ min: z4.number().int().min(1),
11093
+ max: z4.number().int().min(1)
10799
11094
  }).strict(),
10800
- paletteColors: z3.object({
10801
- min: z3.number().int().min(1),
10802
- max: z3.number().int().min(1)
11095
+ paletteColors: z4.object({
11096
+ min: z4.number().int().min(1),
11097
+ max: z4.number().int().min(1)
10803
11098
  }).strict(),
10804
- background: z3.enum(["opaque", "transparent", "full-bleed"]),
10805
- checks: z3.array(z3.enum([
11099
+ background: z4.enum(["opaque", "transparent", "full-bleed"]),
11100
+ checks: z4.array(z4.enum([
10806
11101
  "prompt-coverage",
10807
11102
  "native-grid",
10808
11103
  "final-palette",
10809
11104
  "alpha-edge",
10810
11105
  "human-1x"
10811
11106
  ])).min(1),
10812
- notes: z3.array(z3.string().min(1)).default([])
11107
+ notes: z4.array(z4.string().min(1)).default([])
10813
11108
  }).strict(),
10814
- integrity: z3.object({
10815
- algorithm: z3.literal("sha256"),
10816
- digest: z3.string().regex(SHA256_RE)
11109
+ integrity: z4.object({
11110
+ algorithm: z4.literal("sha256"),
11111
+ digest: z4.string().regex(SHA256_RE)
10817
11112
  }).strict()
10818
11113
  }).strict().superRefine((recipe, context) => {
10819
11114
  if (recipe.style.provider && recipe.style.provider !== recipe.provider) {
10820
11115
  context.addIssue({
10821
- code: z3.ZodIssueCode.custom,
11116
+ code: z4.ZodIssueCode.custom,
10822
11117
  path: ["style", "provider"],
10823
11118
  message: "must match the recipe provider"
10824
11119
  });
10825
11120
  }
10826
11121
  const filePaths = recipe.files.map((file) => file.path);
10827
11122
  if (new Set(filePaths).size !== filePaths.length) {
10828
- context.addIssue({ code: z3.ZodIssueCode.custom, path: ["files"], message: "duplicate file path" });
11123
+ context.addIssue({ code: z4.ZodIssueCode.custom, path: ["files"], message: "duplicate file path" });
10829
11124
  }
10830
11125
  const modelPaths = recipe.models.map((model) => model.path);
10831
11126
  if (new Set(modelPaths).size !== modelPaths.length) {
10832
- context.addIssue({ code: z3.ZodIssueCode.custom, path: ["models"], message: "duplicate model path" });
11127
+ context.addIssue({ code: z4.ZodIssueCode.custom, path: ["models"], message: "duplicate model path" });
10833
11128
  }
10834
11129
  if (filePaths.includes(RECIPE_FILE)) {
10835
11130
  context.addIssue({
10836
- code: z3.ZodIssueCode.custom,
11131
+ code: z4.ZodIssueCode.custom,
10837
11132
  path: ["files"],
10838
11133
  message: `${RECIPE_FILE} cannot hash itself`
10839
11134
  });
10840
11135
  }
10841
11136
  if (recipe.quality.recommendedNativeSize.min > recipe.quality.recommendedNativeSize.max) {
10842
11137
  context.addIssue({
10843
- code: z3.ZodIssueCode.custom,
11138
+ code: z4.ZodIssueCode.custom,
10844
11139
  path: ["quality", "recommendedNativeSize"],
10845
11140
  message: "min cannot exceed max"
10846
11141
  });
10847
11142
  }
10848
11143
  if (recipe.quality.paletteColors.min > recipe.quality.paletteColors.max) {
10849
11144
  context.addIssue({
10850
- code: z3.ZodIssueCode.custom,
11145
+ code: z4.ZodIssueCode.custom,
10851
11146
  path: ["quality", "paletteColors"],
10852
11147
  message: "min cannot exceed max"
10853
11148
  });
@@ -10856,7 +11151,7 @@ var RecipeSchema = z3.object({
10856
11151
  (file) => file.path === recipe.workflow.path && file.role === "workflow"
10857
11152
  )) {
10858
11153
  context.addIssue({
10859
- code: z3.ZodIssueCode.custom,
11154
+ code: z4.ZodIssueCode.custom,
10860
11155
  path: ["workflow", "path"],
10861
11156
  message: "must name a file whose role is workflow"
10862
11157
  });
@@ -10864,7 +11159,7 @@ var RecipeSchema = z3.object({
10864
11159
  if (recipe.provider === "comfyui") {
10865
11160
  if (!recipe.workflow) {
10866
11161
  context.addIssue({
10867
- code: z3.ZodIssueCode.custom,
11162
+ code: z4.ZodIssueCode.custom,
10868
11163
  path: ["workflow"],
10869
11164
  message: "ComfyUI recipes require workflow metadata"
10870
11165
  });
@@ -10873,7 +11168,7 @@ var RecipeSchema = z3.object({
10873
11168
  const options = recipe.style.providerOptions.comfyui;
10874
11169
  if (!options || typeof options !== "object" || Array.isArray(options)) {
10875
11170
  context.addIssue({
10876
- code: z3.ZodIssueCode.custom,
11171
+ code: z4.ZodIssueCode.custom,
10877
11172
  path: ["style", "providerOptions", "comfyui"],
10878
11173
  message: "ComfyUI recipes require ComfyUI provider options"
10879
11174
  });
@@ -10887,7 +11182,7 @@ var RecipeSchema = z3.object({
10887
11182
  };
10888
11183
  if (JSON.stringify(canonical3(options)) !== JSON.stringify(canonical3(expected))) {
10889
11184
  context.addIssue({
10890
- code: z3.ZodIssueCode.custom,
11185
+ code: z4.ZodIssueCode.custom,
10891
11186
  path: ["style", "providerOptions", "comfyui"],
10892
11187
  message: "must match workflow metadata and use {{recipeDir}} for workflowFile"
10893
11188
  });
@@ -11325,11 +11620,11 @@ async function generateFont(client2, opts) {
11325
11620
  import path17 from "path";
11326
11621
  import { existsSync as existsSync12 } from "fs";
11327
11622
  import { readFile as readFile10 } from "fs/promises";
11328
- import { z as z4 } from "zod";
11623
+ import { z as z5 } from "zod";
11329
11624
  var SHA256_RE2 = /^[0-9a-f]{64}$/;
11330
11625
  var HEX_RE = /^#[0-9a-f]{6}$/;
11331
11626
  var MAX_REDMEAN_DISTANCE = 765;
11332
- var QualityPathSchema = z4.string().min(1).refine(
11627
+ var QualityPathSchema = z5.string().min(1).refine(
11333
11628
  (value) => !path17.posix.isAbsolute(value) && !value.includes("\\"),
11334
11629
  "expected a portable relative path"
11335
11630
  );
@@ -11343,53 +11638,53 @@ var DEFAULT_QUALITY_TOLERANCES = {
11343
11638
  maxEdgeContrastDrop: 0.03,
11344
11639
  maxIsolatedPixelIncrease: 5e-3
11345
11640
  };
11346
- var QualityToleranceSchema = z4.object({
11347
- requireExactHash: z4.boolean(),
11348
- maxColorCountIncrease: z4.number().int().min(0),
11349
- maxNewColors: z4.number().int().min(0),
11350
- maxTransparencyDelta: z4.number().min(0).max(1),
11351
- maxPartialAlphaIncrease: z4.number().min(0).max(1),
11352
- maxEdgeDensityDelta: z4.number().min(0).max(1),
11353
- maxEdgeContrastDrop: z4.number().min(0).max(1),
11354
- maxIsolatedPixelIncrease: z4.number().min(0).max(1)
11641
+ var QualityToleranceSchema = z5.object({
11642
+ requireExactHash: z5.boolean(),
11643
+ maxColorCountIncrease: z5.number().int().min(0),
11644
+ maxNewColors: z5.number().int().min(0),
11645
+ maxTransparencyDelta: z5.number().min(0).max(1),
11646
+ maxPartialAlphaIncrease: z5.number().min(0).max(1),
11647
+ maxEdgeDensityDelta: z5.number().min(0).max(1),
11648
+ maxEdgeContrastDrop: z5.number().min(0).max(1),
11649
+ maxIsolatedPixelIncrease: z5.number().min(0).max(1)
11355
11650
  }).strict();
11356
- var ImageQualityMetricsSchema = z4.object({
11357
- sha256: z4.string().regex(SHA256_RE2),
11358
- width: z4.number().int().min(1),
11359
- height: z4.number().int().min(1),
11360
- colorCount: z4.number().int().min(0),
11361
- colors: z4.array(z4.string().regex(HEX_RE)),
11362
- transparency: z4.number().min(0).max(1),
11363
- partialAlpha: z4.number().min(0).max(1),
11364
- edgeDensity: z4.number().min(0).max(1),
11365
- meanEdgeContrast: z4.number().min(0).max(1),
11366
- isolatedPixelRatio: z4.number().min(0).max(1)
11651
+ var ImageQualityMetricsSchema = z5.object({
11652
+ sha256: z5.string().regex(SHA256_RE2),
11653
+ width: z5.number().int().min(1),
11654
+ height: z5.number().int().min(1),
11655
+ colorCount: z5.number().int().min(0),
11656
+ colors: z5.array(z5.string().regex(HEX_RE)),
11657
+ transparency: z5.number().min(0).max(1),
11658
+ partialAlpha: z5.number().min(0).max(1),
11659
+ edgeDensity: z5.number().min(0).max(1),
11660
+ meanEdgeContrast: z5.number().min(0).max(1),
11661
+ isolatedPixelRatio: z5.number().min(0).max(1)
11367
11662
  }).strict();
11368
- var QualityBaselineCaseSchema = z4.object({
11369
- id: z4.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
11663
+ var QualityBaselineCaseSchema = z5.object({
11664
+ id: z5.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
11370
11665
  file: QualityPathSchema,
11371
- record: z4.object({
11666
+ record: z5.object({
11372
11667
  path: QualityPathSchema,
11373
- sha256: z4.string().regex(SHA256_RE2)
11668
+ sha256: z5.string().regex(SHA256_RE2)
11374
11669
  }).strict().optional(),
11375
11670
  expected: ImageQualityMetricsSchema,
11376
11671
  tolerances: QualityToleranceSchema
11377
11672
  }).strict();
11378
- var QualityBaselineSchema = z4.object({
11379
- $schema: z4.string().url().optional(),
11380
- format: z4.literal("pixelkiln-quality-baseline"),
11381
- schemaVersion: z4.literal(1),
11382
- cases: z4.array(QualityBaselineCaseSchema).min(1)
11673
+ var QualityBaselineSchema = z5.object({
11674
+ $schema: z5.string().url().optional(),
11675
+ format: z5.literal("pixelkiln-quality-baseline"),
11676
+ schemaVersion: z5.literal(1),
11677
+ cases: z5.array(QualityBaselineCaseSchema).min(1)
11383
11678
  }).strict().superRefine((baseline, context) => {
11384
11679
  const ids = baseline.cases.map((entry) => entry.id);
11385
11680
  if (new Set(ids).size !== ids.length) {
11386
- context.addIssue({ code: z4.ZodIssueCode.custom, path: ["cases"], message: "duplicate case id" });
11681
+ context.addIssue({ code: z5.ZodIssueCode.custom, path: ["cases"], message: "duplicate case id" });
11387
11682
  }
11388
11683
  });
11389
- var QualityInputSchema = z4.object({
11390
- id: z4.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
11391
- path: z4.string().min(1),
11392
- record: z4.string().min(1).optional(),
11684
+ var QualityInputSchema = z5.object({
11685
+ id: z5.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
11686
+ path: z5.string().min(1),
11687
+ record: z5.string().min(1).optional(),
11393
11688
  tolerances: QualityToleranceSchema.partial().optional()
11394
11689
  }).strict();
11395
11690
  function message4(error) {
@@ -11491,7 +11786,7 @@ async function measureImageQuality(file) {
11491
11786
  }
11492
11787
  }
11493
11788
  function resolveQualityInputs(raw, inputsFilePath) {
11494
- const parsed = z4.array(QualityInputSchema).min(1).safeParse(raw);
11789
+ const parsed = z5.array(QualityInputSchema).min(1).safeParse(raw);
11495
11790
  if (!parsed.success) {
11496
11791
  throw new Error(
11497
11792
  "--inputs must be a non-empty JSON array of quality cases:\n" + parsed.error.issues.map((issue) => ` ${issue.path.join(".") || "$"}: ${issue.message}`).join("\n")
@@ -11714,11 +12009,11 @@ import path20 from "path";
11714
12009
  import { mkdir as mkdir5, readFile as readFile11, rename as rename4, rm as rm4, writeFile as writeFile5 } from "fs/promises";
11715
12010
  import { existsSync as existsSync13 } from "fs";
11716
12011
  import pathModule from "path";
11717
- import { z as z5 } from "zod";
11718
- var HashCacheSchema = z5.object({
11719
- version: z5.literal(1),
12012
+ import { z as z6 } from "zod";
12013
+ var HashCacheSchema = z6.object({
12014
+ version: z6.literal(1),
11720
12015
  /** objectId → sha256 of that object's image bytes. */
11721
- hashes: z5.record(z5.string())
12016
+ hashes: z6.record(z6.string())
11722
12017
  });
11723
12018
  var SHA256_HEX = /^[a-f0-9]{64}$/;
11724
12019
  function isSha256Hash(value) {
@@ -13266,6 +13561,9 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
13266
13561
  ...spec.revision.lastFrameSha256 ? { lastFrameSha256: spec.revision.lastFrameSha256 } : {},
13267
13562
  ...spec.revision.direction ? { direction: spec.revision.direction } : {},
13268
13563
  ...spec.revision.enhancePrompt == null ? {} : { enhancePrompt: spec.revision.enhancePrompt },
13564
+ ...spec.revision.keypointsSha256 ? { keypointsSha256: spec.revision.keypointsSha256 } : {},
13565
+ ...spec.revision.skeletonTemplate ? { skeletonTemplate: spec.revision.skeletonTemplate } : {},
13566
+ ...spec.revision.description ? { description: spec.revision.description } : {},
13269
13567
  ...spec.revision.engine ? { engine: spec.revision.engine } : {}
13270
13568
  } : null,
13271
13569
  outfit: spec.character?.kind === "outfit" && outfitParentEntry ? {
@@ -14563,20 +14861,20 @@ import path29 from "path";
14563
14861
  import { mkdir as mkdir7, readFile as readFile18, rename as rename6, rm as rm7, writeFile as writeFile9 } from "fs/promises";
14564
14862
  import { existsSync as existsSync21 } from "fs";
14565
14863
  import path25 from "path";
14566
- import { z as z6 } from "zod";
14567
- var WorkspaceProjectSchema = z6.object({
14568
- id: z6.string().min(1),
14864
+ import { z as z7 } from "zod";
14865
+ var WorkspaceProjectSchema = z7.object({
14866
+ id: z7.string().min(1),
14569
14867
  /** Manifest path, relative to the catalog file's own directory. */
14570
- manifest: z6.string().min(1),
14868
+ manifest: z7.string().min(1),
14571
14869
  /** Lockfile path, relative to the catalog file's own directory. */
14572
- lock: z6.string().min(1),
14573
- provider: z6.string().min(1).default("pixellab"),
14870
+ lock: z7.string().min(1),
14871
+ provider: z7.string().min(1).default("pixellab"),
14574
14872
  /** Free-form label for a shared account, e.g. distinguishing sandboxes. */
14575
- account: z6.string().optional()
14873
+ account: z7.string().optional()
14576
14874
  }).strict();
14577
- var WorkspaceSchema = z6.object({
14578
- version: z6.literal(1),
14579
- projects: z6.array(WorkspaceProjectSchema).default([])
14875
+ var WorkspaceSchema = z7.object({
14876
+ version: z7.literal(1),
14877
+ projects: z7.array(WorkspaceProjectSchema).default([])
14580
14878
  }).strict();
14581
14879
  function parseWorkspace(raw) {
14582
14880
  const parsed = WorkspaceSchema.safeParse(raw);
@@ -14694,62 +14992,62 @@ function validateWorkspace(ws, dir) {
14694
14992
 
14695
14993
  // src/gallery/edit.ts
14696
14994
  import path28 from "path";
14697
- import { z as z9 } from "zod";
14995
+ import { z as z10 } from "zod";
14698
14996
 
14699
14997
  // src/manifest-edit.ts
14700
14998
  import { readFile as readFile19, rename as rename7, rm as rm8, writeFile as writeFile10 } from "fs/promises";
14701
14999
  import path26 from "path";
14702
- import { z as z7 } from "zod";
14703
- var HexSha = z7.string().regex(/^[0-9a-f]{64}$/);
14704
- var AssetPatchSchema = z7.object({
14705
- prompt: z7.string().optional(),
15000
+ import { z as z8 } from "zod";
15001
+ var HexSha = z8.string().regex(/^[0-9a-f]{64}$/);
15002
+ var AssetPatchSchema = z8.object({
15003
+ prompt: z8.string().optional(),
14706
15004
  /** Per-style override for the style being viewed; null removes it. */
14707
- promptForStyle: z7.object({ styleId: z7.string().min(1), prompt: z7.string().nullable() }).optional(),
14708
- category: z7.string().nullable().optional(),
14709
- width: z7.number().int().nullable().optional(),
14710
- height: z7.number().int().nullable().optional(),
14711
- size: z7.number().int().nullable().optional(),
14712
- tags: z7.array(z7.string()).optional()
15005
+ promptForStyle: z8.object({ styleId: z8.string().min(1), prompt: z8.string().nullable() }).optional(),
15006
+ category: z8.string().nullable().optional(),
15007
+ width: z8.number().int().nullable().optional(),
15008
+ height: z8.number().int().nullable().optional(),
15009
+ size: z8.number().int().nullable().optional(),
15010
+ tags: z8.array(z8.string()).optional()
14713
15011
  }).strict();
14714
- var NewStateSchema = z7.object({
15012
+ var NewStateSchema = z8.object({
14715
15013
  /** Asset id of the parent this states; must already exist. */
14716
- of: z7.string().min(1),
14717
- paletteFromReference: z7.boolean().optional(),
14718
- canvas: z7.object({ width: z7.number().int().min(16).max(256), height: z7.number().int().min(16).max(256) }).strict().optional()
15014
+ of: z8.string().min(1),
15015
+ paletteFromReference: z8.boolean().optional(),
15016
+ canvas: z8.object({ width: z8.number().int().min(16).max(256), height: z8.number().int().min(16).max(256) }).strict().optional()
14719
15017
  }).strict();
14720
- var NewAnimationSchema = z7.object({
15018
+ var NewAnimationSchema = z8.object({
14721
15019
  /** Asset id of the parent this animates; must already exist. */
14722
- of: z7.string().min(1),
15020
+ of: z8.string().min(1),
14723
15021
  /** A PixelLab template id (`character` only; `objectPro` has no template concept). */
14724
- template: z7.string().min(1).optional(),
15022
+ template: z8.string().min(1).optional(),
14725
15023
  direction: CharacterDirectionSchema.optional(),
14726
- frames: z7.number().int().min(4).max(16).optional(),
14727
- fps: z7.number().int().min(1).max(60).optional(),
15024
+ frames: z8.number().int().min(4).max(16).optional(),
15025
+ fps: z8.number().int().min(1).max(60).optional(),
14728
15026
  mode: CharacterAnimationModeSchema.optional(),
14729
15027
  /** What is being animated, when the parent's own description would mislead the model (`character` only). */
14730
- subject: z7.string().min(1).optional(),
14731
- enhancePrompt: z7.boolean().optional()
15028
+ subject: z8.string().min(1).optional(),
15029
+ enhancePrompt: z8.boolean().optional()
14732
15030
  }).strict();
14733
- var NewAssetSchema = z7.object({
14734
- prompt: z7.string(),
14735
- category: z7.string().optional(),
14736
- width: z7.number().int().optional(),
14737
- height: z7.number().int().optional(),
14738
- size: z7.number().int().optional(),
14739
- tags: z7.array(z7.string()).optional(),
15031
+ var NewAssetSchema = z8.object({
15032
+ prompt: z8.string(),
15033
+ category: z8.string().optional(),
15034
+ width: z8.number().int().optional(),
15035
+ height: z8.number().int().optional(),
15036
+ size: z8.number().int().optional(),
15037
+ tags: z8.array(z8.string()).optional(),
14740
15038
  /** Restrict to these styles; empty or omitted means every style. */
14741
- styles: z7.array(z7.string().min(1)).optional(),
15039
+ styles: z8.array(z8.string().min(1)).optional(),
14742
15040
  /**
14743
15041
  * A controlled regeneration of another asset in the same manifest,
14744
15042
  * instead of a fresh generation. `inpaint` needs a mask upload the
14745
15043
  * gallery does not offer yet, so only `image-to-image` can be created
14746
15044
  * here; hand-edit the manifest for the other modes.
14747
15045
  */
14748
- revision: z7.object({
14749
- mode: z7.literal("image-to-image"),
15046
+ revision: z8.object({
15047
+ mode: z8.literal("image-to-image"),
14750
15048
  /** Asset id of the parent this revises; must already exist. */
14751
- from: z7.string().min(1),
14752
- strength: z7.number().min(0).max(1).optional()
15049
+ from: z8.string().min(1),
15050
+ strength: z8.number().min(0).max(1).optional()
14753
15051
  }).strict().optional(),
14754
15052
  /** A pose or outfit of an existing `character`/`objectPro` base or state. */
14755
15053
  state: NewStateSchema.optional(),
@@ -14765,58 +15063,58 @@ var CANDIDATE_OPTION = {
14765
15063
  comfyui: "numImages",
14766
15064
  scenario: "numOutputs"
14767
15065
  };
14768
- var ManifestEditSchema = z7.discriminatedUnion("action", [
14769
- z7.object({
14770
- action: z7.literal("patch-asset"),
15066
+ var ManifestEditSchema = z8.discriminatedUnion("action", [
15067
+ z8.object({
15068
+ action: z8.literal("patch-asset"),
14771
15069
  /** Workspace project id; omitted for a single-project gallery. */
14772
- project: z7.string().min(1).optional(),
14773
- assetId: z7.string().min(1),
15070
+ project: z8.string().min(1).optional(),
15071
+ assetId: z8.string().min(1),
14774
15072
  /** Manifest bytes the page last saw; a mismatch refuses the write. */
14775
15073
  expectedSha256: HexSha,
14776
15074
  patch: AssetPatchSchema
14777
15075
  }).strict(),
14778
- z7.object({
14779
- action: z7.literal("add-asset"),
14780
- project: z7.string().min(1).optional(),
14781
- assetId: z7.string().min(1).regex(/^[^/\\]+$/, "asset ids cannot contain slashes"),
15076
+ z8.object({
15077
+ action: z8.literal("add-asset"),
15078
+ project: z8.string().min(1).optional(),
15079
+ assetId: z8.string().min(1).regex(/^[^/\\]+$/, "asset ids cannot contain slashes"),
14782
15080
  expectedSha256: HexSha,
14783
15081
  asset: NewAssetSchema
14784
15082
  }).strict(),
14785
- z7.object({
14786
- action: z7.literal("set-source"),
14787
- project: z7.string().min(1).optional(),
14788
- assetId: z7.string().min(1),
14789
- styleId: z7.string().min(1),
15083
+ z8.object({
15084
+ action: z8.literal("set-source"),
15085
+ project: z8.string().min(1).optional(),
15086
+ assetId: z8.string().min(1),
15087
+ styleId: z8.string().min(1),
14790
15088
  /** Manifest-relative path of the art that stands in for this asset in this style. */
14791
- source: z7.string().min(1),
15089
+ source: z8.string().min(1),
14792
15090
  expectedSha256: HexSha
14793
15091
  }).strict(),
14794
- z7.object({
14795
- action: z7.literal("clear-source"),
14796
- project: z7.string().min(1).optional(),
14797
- assetId: z7.string().min(1),
14798
- styleId: z7.string().min(1),
15092
+ z8.object({
15093
+ action: z8.literal("clear-source"),
15094
+ project: z8.string().min(1).optional(),
15095
+ assetId: z8.string().min(1),
15096
+ styleId: z8.string().min(1),
14799
15097
  expectedSha256: HexSha
14800
15098
  }).strict(),
14801
- z7.object({
14802
- action: z7.literal("patch-style"),
14803
- project: z7.string().min(1).optional(),
14804
- styleId: z7.string().min(1),
15099
+ z8.object({
15100
+ action: z8.literal("patch-style"),
15101
+ project: z8.string().min(1).optional(),
15102
+ styleId: z8.string().min(1),
14805
15103
  expectedSha256: HexSha,
14806
- patch: z7.object({
15104
+ patch: z8.object({
14807
15105
  /** Candidates per generation, written to the provider's own option. */
14808
- candidates: z7.number().int().min(1).max(64).optional(),
15106
+ candidates: z8.number().int().min(1).max(64).optional(),
14809
15107
  /** Empty clears the style's own value (inherit, or the default). */
14810
- promptPrefix: z7.string().optional(),
14811
- promptSuffix: z7.string().optional(),
15108
+ promptPrefix: z8.string().optional(),
15109
+ promptSuffix: z8.string().optional(),
14812
15110
  /** `#rrggbb` values; null or empty clears the style's own palette. */
14813
- palette: z7.array(z7.string().regex(/^#?[0-9a-f]{6}$/i, "expected a six-digit hex colour")).max(256).nullable().optional(),
15111
+ palette: z8.array(z8.string().regex(/^#?[0-9a-f]{6}$/i, "expected a six-digit hex colour")).max(256).nullable().optional(),
14814
15112
  /** Snap downloaded art to the palette; false clears the style's own value. */
14815
- enforcePalette: z7.boolean().optional(),
15113
+ enforcePalette: z8.boolean().optional(),
14816
15114
  /** Provider view name (PixelLab: low top-down, high top-down, side); empty clears. */
14817
- view: z7.string().max(64).nullable().optional(),
15115
+ view: z8.string().max(64).nullable().optional(),
14818
15116
  /** pixflux only; null clears the style's own value. */
14819
- noBackground: z7.boolean().nullable().optional()
15117
+ noBackground: z8.boolean().nullable().optional()
14820
15118
  }).strict().refine((patch) => Object.keys(patch).length > 0, { message: "nothing to change" })
14821
15119
  }).strict()
14822
15120
  ]);
@@ -15010,7 +15308,7 @@ async function applyManifestEdit(manifestPath, edit) {
15010
15308
  import { existsSync as existsSync22 } from "fs";
15011
15309
  import { copyFile, mkdir as mkdir8, readFile as readFile20, rename as rename8, rm as rm9, writeFile as writeFile11 } from "fs/promises";
15012
15310
  import path27 from "path";
15013
- import { z as z8 } from "zod";
15311
+ import { z as z9 } from "zod";
15014
15312
  var HAND_EDIT_DIR = "edits";
15015
15313
  function handEditPath(loaded, spec) {
15016
15314
  const style = loaded.manifest.styles[spec.styleId];
@@ -15107,35 +15405,35 @@ async function detachHandEdit(loaded, spec, opts = {}) {
15107
15405
  });
15108
15406
  return { manifestSha256: result.sha256, changed: result.changed };
15109
15407
  }
15110
- var CompanionOutputSchema = z8.object({
15408
+ var CompanionOutputSchema = z9.object({
15111
15409
  /** Output role for a set member; null for a single image. */
15112
- role: z8.string().nullable(),
15410
+ role: z9.string().nullable(),
15113
15411
  /** sha256 of the generated file this member started from; null when it started from untracked art. */
15114
- basedOn: z8.string().regex(/^[0-9a-f]{64}$/).nullable(),
15412
+ basedOn: z9.string().regex(/^[0-9a-f]{64}$/).nullable(),
15115
15413
  /** sha256 of the member as saved, so a later change by another tool is visible. */
15116
- sha256: z8.string().regex(/^[0-9a-f]{64}$/)
15414
+ sha256: z9.string().regex(/^[0-9a-f]{64}$/)
15117
15415
  }).strict();
15118
- var CompanionV2Schema = z8.object({
15119
- version: z8.literal(2),
15416
+ var CompanionV2Schema = z9.object({
15417
+ version: z9.literal(2),
15120
15418
  /** Editor identity the bridge reported, e.g. `pixelorama@v1.2.2-stable`. */
15121
- editor: z8.string().min(1),
15122
- protocol: z8.number().int().positive(),
15123
- savedAt: z8.string().datetime(),
15419
+ editor: z9.string().min(1),
15420
+ protocol: z9.number().int().positive(),
15421
+ savedAt: z9.string().datetime(),
15124
15422
  /** Basename of the layered project file beside the edit, when one was kept. */
15125
- project: z8.string().min(1).nullable(),
15423
+ project: z9.string().min(1).nullable(),
15126
15424
  /** One entry per file, in output order. */
15127
- outputs: z8.array(CompanionOutputSchema).min(1)
15425
+ outputs: z9.array(CompanionOutputSchema).min(1)
15128
15426
  }).strict();
15129
- var CompanionV1Schema = z8.object({
15130
- version: z8.literal(1),
15131
- basedOn: z8.string().regex(/^[0-9a-f]{64}$/).nullable(),
15132
- sha256: z8.string().regex(/^[0-9a-f]{64}$/),
15133
- editor: z8.string().min(1),
15134
- protocol: z8.number().int().positive(),
15135
- savedAt: z8.string().datetime(),
15136
- project: z8.string().min(1).nullable()
15427
+ var CompanionV1Schema = z9.object({
15428
+ version: z9.literal(1),
15429
+ basedOn: z9.string().regex(/^[0-9a-f]{64}$/).nullable(),
15430
+ sha256: z9.string().regex(/^[0-9a-f]{64}$/),
15431
+ editor: z9.string().min(1),
15432
+ protocol: z9.number().int().positive(),
15433
+ savedAt: z9.string().datetime(),
15434
+ project: z9.string().min(1).nullable()
15137
15435
  }).strict();
15138
- var HandEditCompanionSchema = z8.union([CompanionV2Schema, CompanionV1Schema]).transform(
15436
+ var HandEditCompanionSchema = z9.union([CompanionV2Schema, CompanionV1Schema]).transform(
15139
15437
  (value) => value.version === 2 ? value : {
15140
15438
  version: 2,
15141
15439
  editor: value.editor,
@@ -15255,40 +15553,40 @@ function openInEditor(file, editor = process.env.PIXELKILN_EDITOR) {
15255
15553
  }
15256
15554
 
15257
15555
  // src/gallery/edit.ts
15258
- var Base64 = z9.string().regex(/^[A-Za-z0-9+/]*={0,2}$/).max(Math.ceil(MAX_HAND_EDIT_BYTES / 3) * 4);
15259
- var HandEditRequestSchema = z9.discriminatedUnion("action", [
15260
- z9.object({
15261
- action: z9.literal("start-edit"),
15262
- project: z9.string().min(1).optional(),
15263
- assetId: z9.string().min(1),
15264
- styleId: z9.string().min(1),
15265
- expectedSha256: z9.string().regex(/^[0-9a-f]{64}$/),
15556
+ var Base64 = z10.string().regex(/^[A-Za-z0-9+/]*={0,2}$/).max(Math.ceil(MAX_HAND_EDIT_BYTES / 3) * 4);
15557
+ var HandEditRequestSchema = z10.discriminatedUnion("action", [
15558
+ z10.object({
15559
+ action: z10.literal("start-edit"),
15560
+ project: z10.string().min(1).optional(),
15561
+ assetId: z10.string().min(1),
15562
+ styleId: z10.string().min(1),
15563
+ expectedSha256: z10.string().regex(/^[0-9a-f]{64}$/),
15266
15564
  /** Also launch the author's editor on the file. */
15267
- open: z9.boolean().optional()
15565
+ open: z10.boolean().optional()
15268
15566
  }).strict(),
15269
- z9.object({
15270
- action: z9.literal("detach-edit"),
15271
- project: z9.string().min(1).optional(),
15272
- assetId: z9.string().min(1),
15273
- styleId: z9.string().min(1),
15274
- expectedSha256: z9.string().regex(/^[0-9a-f]{64}$/)
15567
+ z10.object({
15568
+ action: z10.literal("detach-edit"),
15569
+ project: z10.string().min(1).optional(),
15570
+ assetId: z10.string().min(1),
15571
+ styleId: z10.string().min(1),
15572
+ expectedSha256: z10.string().regex(/^[0-9a-f]{64}$/)
15275
15573
  }).strict(),
15276
15574
  /** From the in-browser editor: the page holds the bytes, the server validates and writes. */
15277
- z9.object({
15278
- action: z9.literal("save-edit"),
15279
- project: z9.string().min(1).optional(),
15280
- assetId: z9.string().min(1),
15281
- styleId: z9.string().min(1),
15282
- expectedSha256: z9.string().regex(/^[0-9a-f]{64}$/),
15575
+ z10.object({
15576
+ action: z10.literal("save-edit"),
15577
+ project: z10.string().min(1).optional(),
15578
+ assetId: z10.string().min(1),
15579
+ styleId: z10.string().min(1),
15580
+ expectedSha256: z10.string().regex(/^[0-9a-f]{64}$/),
15283
15581
  /** The flattened image; for a frame set, `frames` instead. */
15284
15582
  png: Base64.min(1).optional(),
15285
15583
  /** Every frame of a set, tagged with the role it was opened under (null for one added in the editor). */
15286
- frames: z9.array(z9.object({ role: z9.string().min(1).nullable(), png: Base64.min(1) }).strict()).min(1).max(256).optional(),
15584
+ frames: z10.array(z10.object({ role: z10.string().min(1).nullable(), png: Base64.min(1) }).strict()).min(1).max(256).optional(),
15287
15585
  /** The editor's layered project file, kept beside the edit. */
15288
15586
  pxo: Base64.optional(),
15289
15587
  /** Editor identity from the bridge's ready message. */
15290
- editor: z9.string().min(1).max(200),
15291
- protocol: z9.number().int().positive()
15588
+ editor: z10.string().min(1).max(200),
15589
+ protocol: z10.number().int().positive()
15292
15590
  }).strict()
15293
15591
  ]);
15294
15592
  function createGalleryEditHandler(opts) {
@@ -16622,27 +16920,27 @@ async function serveGallery(opts) {
16622
16920
 
16623
16921
  // src/gallery/generate.ts
16624
16922
  import { randomBytes as randomBytes2 } from "crypto";
16625
- import { z as z10 } from "zod";
16626
- var GenerateRequestSchema = z10.object({
16627
- project: z10.string().min(1).optional(),
16628
- keys: z10.array(z10.string().min(1)).min(1).max(500),
16923
+ import { z as z11 } from "zod";
16924
+ var GenerateRequestSchema = z11.object({
16925
+ project: z11.string().min(1).optional(),
16926
+ keys: z11.array(z11.string().min(1)).min(1).max(500),
16629
16927
  /**
16630
16928
  * Regenerate work that is up to date, as `gen --force` does, and let the
16631
16929
  * fetch replace a locally modified or untracked file. With `restore`, put
16632
16930
  * the recorded bytes back over a modified file.
16633
16931
  */
16634
- force: z10.boolean().optional(),
16932
+ force: z11.boolean().optional(),
16635
16933
  /** Advance in-flight, review, or selected work without submitting anything. */
16636
- resume: z10.boolean().optional(),
16934
+ resume: z11.boolean().optional(),
16637
16935
  /** Re-download downloaded outputs and replace files whose object changed upstream. */
16638
- refresh: z10.boolean().optional(),
16936
+ refresh: z11.boolean().optional(),
16639
16937
  /** Put recorded outputs back on disk from the cache or the provider, without generating. */
16640
- restore: z10.boolean().optional(),
16938
+ restore: z11.boolean().optional(),
16641
16939
  /**
16642
16940
  * Bring a replaced generation back: 1-based position in the record's
16643
16941
  * history (newest first) or an output hash prefix. One key at a time.
16644
16942
  */
16645
- revert: z10.string().min(1).max(64).optional()
16943
+ revert: z11.string().min(1).max(64).optional()
16646
16944
  }).strict().refine((request) => [request.resume, request.refresh, request.restore, request.revert !== void 0].filter(Boolean).length <= 1, {
16647
16945
  message: "resume, refresh, restore, and revert are exclusive"
16648
16946
  }).refine((request) => request.revert === void 0 || request.keys.length === 1, { message: "revert takes one key" });
@@ -17645,6 +17943,8 @@ export {
17645
17943
  ManifestEditSchema,
17646
17944
  ManifestInputSchema,
17647
17945
  ManifestSchema,
17946
+ MapScenePlacementSchema,
17947
+ MapSceneSchema,
17648
17948
  MediaType,
17649
17949
  OverwriteRefusedError,
17650
17950
  PIXEL_ART_FIXER_REVISION,