pixelkiln 0.66.0 → 0.67.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
  ]);
@@ -329,6 +330,30 @@ var RevisionSchema = z.object({
329
330
  direction: CharacterDirectionSchema.optional(),
330
331
  /** `animate`/`animate-pixminimax` only: let PixelLab expand the action into a fuller motion description first. */
331
332
  enhancePrompt: z.boolean().optional(),
333
+ /**
334
+ * `animate-skeleton` only: manifest-relative path to a committed
335
+ * `SkeletonSet` JSON file (see `src/skeleton.ts`) — the pose the parent
336
+ * image is already in, plus 3-15 per-frame poses for the motion.
337
+ * Authored via `pixelkiln estimate-skeleton`, then hand-tweaked; never
338
+ * inline in the manifest, the same reasoning `mask`/`lastFrame` are
339
+ * committed files rather than inline pixel data.
340
+ */
341
+ keypointsFile: z.string().min(1).optional(),
342
+ /**
343
+ * `animate-skeleton` only: the body a joint's missing `depth` is taken
344
+ * from. PixelLab's default is `mannequin`; free string like `character`'s
345
+ * own `template` field, validated at the provider layer against the
346
+ * same `mannequin`/`bear`/`cat`/`dog`/`horse`/`lion` vocabulary.
347
+ */
348
+ skeletonTemplate: z.string().min(1).optional(),
349
+ /**
350
+ * `animate-skeleton` only: what the character *looks like*, a noun
351
+ * phrase (colours, clothing, held items) — distinct from the asset's
352
+ * own `prompt`, which this mode sends as the motion's short label
353
+ * (PixelLab's `action`), the same `prompt`-as-motion convention
354
+ * `animate`/`animate-pixminimax` already use.
355
+ */
356
+ description: z.string().min(1).optional(),
332
357
  /**
333
358
  * `image-to-image`/`inpaint` only: which provider engine draws the
334
359
  * edit. `pro-flash` selects PixelLab's Pro Flash edit and inpaint tier
@@ -419,18 +444,41 @@ var RevisionSchema = z.object({
419
444
  path: ["fps"]
420
445
  });
421
446
  }
422
- if (revision.direction !== void 0 && revision.mode !== "animate-pixminimax") {
447
+ if (revision.direction !== void 0 && revision.mode !== "animate-pixminimax" && revision.mode !== "animate-skeleton") {
423
448
  context.addIssue({
424
449
  code: z.ZodIssueCode.custom,
425
- message: "direction applies to animate-pixminimax revisions only",
450
+ message: "direction applies to animate-pixminimax/animate-skeleton revisions only",
451
+ path: ["direction"]
452
+ });
453
+ }
454
+ if (revision.mode === "animate-skeleton" && revision.direction === void 0) {
455
+ context.addIssue({
456
+ code: z.ZodIssueCode.custom,
457
+ message: "animate-skeleton revisions require a direction",
426
458
  path: ["direction"]
427
459
  });
428
460
  }
429
461
  if (revision.frames !== void 0 && revision.frames % 2 !== 0) {
430
462
  context.addIssue({ code: z.ZodIssueCode.custom, message: "frames must be even", path: ["frames"] });
431
463
  }
464
+ if (revision.mode === "animate-skeleton" && !revision.keypointsFile) {
465
+ context.addIssue({
466
+ code: z.ZodIssueCode.custom,
467
+ message: "animate-skeleton revisions require keypointsFile",
468
+ path: ["keypointsFile"]
469
+ });
470
+ }
471
+ for (const field of ["keypointsFile", "skeletonTemplate", "description"]) {
472
+ if (revision[field] !== void 0 && revision.mode !== "animate-skeleton") {
473
+ context.addIssue({
474
+ code: z.ZodIssueCode.custom,
475
+ message: `${field} applies to animate-skeleton revisions only`,
476
+ path: [field]
477
+ });
478
+ }
479
+ }
432
480
  });
433
- var FRAME_SET_REVISION_MODES = ["animate", "animate-pixminimax", "interpolate", "edit-animation"];
481
+ var FRAME_SET_REVISION_MODES = ["animate", "animate-pixminimax", "animate-skeleton", "interpolate", "edit-animation"];
434
482
  var MEMBER_SET_REVISION_MODES = ["reduce-colors", "correct-pixelart", "edit-animation"];
435
483
  var StyleObjectSchema = z.object({
436
484
  /** Generation backend for this style. Omit to inherit the manifest default. */
@@ -1062,11 +1110,16 @@ var LockEntrySchema = z.object({
1062
1110
  paletteImageSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1063
1111
  dithering: RevisionDitheringSchema.optional(),
1064
1112
  ditheringStrength: z.number().min(0).max(10).optional(),
1065
- frames: z.number().int().min(4).max(40).optional(),
1113
+ // 4-40 for animate/animate-pixminimax; animate-skeleton's own 3-15
1114
+ // range extends the floor down to 3, hence min(3) here rather than 4.
1115
+ frames: z.number().int().min(3).max(40).optional(),
1066
1116
  fps: z.number().int().min(1).max(60).optional(),
1067
1117
  lastFrameSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1068
1118
  direction: CharacterDirectionSchema.optional(),
1069
1119
  enhancePrompt: z.boolean().optional(),
1120
+ keypointsSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1121
+ skeletonTemplate: z.string().min(1).optional(),
1122
+ description: z.string().min(1).optional(),
1070
1123
  engine: z.enum(["pro-flash"]).optional()
1071
1124
  }).strict().nullable().default(null),
1072
1125
  /** For a mirror: the asset it flips and a hash over that asset's output hashes when this was made. */
@@ -1253,7 +1306,7 @@ function exitCodeFor(err) {
1253
1306
  }
1254
1307
 
1255
1308
  // src/client.ts
1256
- import { z as z2 } from "zod";
1309
+ import { z as z3 } from "zod";
1257
1310
 
1258
1311
  // src/http.ts
1259
1312
  var MAX_RETRIES = 4;
@@ -1303,262 +1356,289 @@ function fetchWithRetry(request, opts = {}) {
1303
1356
  };
1304
1357
  }
1305
1358
 
1359
+ // src/skeleton.ts
1360
+ import { z as z2 } from "zod";
1361
+ var SkeletonKeypointSchema = z2.object({
1362
+ label: z2.string().min(1),
1363
+ x: z2.number().min(0).max(1),
1364
+ y: z2.number().min(0).max(1),
1365
+ z_index: z2.number(),
1366
+ depth: z2.number().optional()
1367
+ }).strict();
1368
+ var SkeletonFrameSchema = z2.array(SkeletonKeypointSchema).length(18);
1369
+ var SkeletonSetSchema = z2.object({
1370
+ firstFrameKeypoints: SkeletonFrameSchema,
1371
+ frames: z2.array(SkeletonFrameSchema).min(3).max(15)
1372
+ }).strict();
1373
+ function parseSkeletonSet(json, file) {
1374
+ const result = SkeletonSetSchema.safeParse(json);
1375
+ if (!result.success) {
1376
+ const issues = result.error.issues.slice(0, 4).map((issue) => `${issue.path.join(".") || "root"}: ${issue.message}`).join("; ");
1377
+ throw new Error(`revision keypoints file ${file} does not match the expected shape: ${issues}`);
1378
+ }
1379
+ return result.data;
1380
+ }
1381
+
1306
1382
  // src/client.ts
1307
1383
  var BASE = process.env.PIXELLAB_API_BASE ?? "https://api.pixellab.ai/v2";
1308
1384
  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()
1385
+ var BalanceResponseSchema = z3.object({
1386
+ credits: z3.object({ usd: z3.number() }).passthrough(),
1387
+ subscription: z3.object({
1388
+ generations: z3.number(),
1389
+ total: z3.number(),
1390
+ plan: z3.string().nullable().optional()
1315
1391
  }).passthrough()
1316
1392
  }).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)
1393
+ var ObjectSubmitSchema = z3.object({
1394
+ background_job_id: z3.string().min(1),
1395
+ object_id: z3.string().min(1),
1396
+ status: z3.string().default("queued"),
1397
+ n_frames: z3.number().int().min(0)
1322
1398
  }).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")
1399
+ var MapSubmitSchema = z3.object({
1400
+ background_job_id: z3.string().min(1),
1401
+ object_id: z3.string().min(1),
1402
+ status: z3.string().default("processing")
1327
1403
  }).passthrough();
1328
- var UsageSchema = z2.object({
1329
- type: z2.string().optional(),
1330
- usd: z2.number().nullable().optional(),
1331
- generations: z2.number().nullable().optional()
1404
+ var UsageSchema = z3.object({
1405
+ type: z3.string().optional(),
1406
+ usd: z3.number().nullable().optional(),
1407
+ generations: z3.number().nullable().optional()
1332
1408
  }).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")
1409
+ var TilesSubmitSchema = z3.object({
1410
+ tile_id: z3.string().min(1),
1411
+ background_job_id: z3.string().min(1),
1412
+ status: z3.literal("processing").default("processing")
1337
1413
  }).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()
1414
+ var TilesProSchema = z3.object({
1415
+ storage_urls: z3.record(z3.string()),
1416
+ kind: z3.string().nullable().default(null),
1417
+ tile_rules: z3.record(z3.unknown()).nullable().optional()
1342
1418
  }).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")
1419
+ var TilesetSubmitSchema = z3.object({
1420
+ tileset_id: z3.string().min(1),
1421
+ background_job_id: z3.string().min(1),
1422
+ status: z3.literal("processing").default("processing")
1347
1423
  }).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())
1424
+ var TilesetTileSchema = z3.object({
1425
+ id: z3.string().min(1),
1426
+ name: z3.string(),
1427
+ image: z3.object({ base64: z3.string().min(1), format: z3.string().default("png") }).passthrough(),
1428
+ corners: z3.object({ NW: z3.string(), NE: z3.string(), SW: z3.string(), SE: z3.string() }).passthrough(),
1429
+ pattern_4x4: z3.object({
1430
+ row_0: z3.array(z3.number()),
1431
+ row_1: z3.array(z3.number()),
1432
+ row_2: z3.array(z3.number()),
1433
+ row_3: z3.array(z3.number())
1358
1434
  }).passthrough()
1359
1435
  }).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)
1436
+ var TilesetGetSchema = z3.object({
1437
+ tileset: z3.object({
1438
+ total_tiles: z3.number().int().min(1),
1439
+ tile_size: z3.object({ width: z3.number().int().positive(), height: z3.number().int().positive() }).passthrough(),
1440
+ terrain_types: z3.array(z3.string()),
1441
+ tiles: z3.array(TilesetTileSchema).min(1)
1366
1442
  }).passthrough(),
1367
1443
  usage: UsageSchema
1368
1444
  }).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")
1445
+ var IsometricTileSubmitSchema = z3.object({
1446
+ tile_id: z3.string().min(1),
1447
+ background_job_id: z3.string().min(1),
1448
+ status: z3.literal("processing").default("processing")
1373
1449
  }).passthrough();
1374
- var IsometricTileGetSchema = z2.object({
1375
- image: z2.object({ base64: z2.string().min(1), format: z2.string().default("png") }).passthrough(),
1450
+ var IsometricTileGetSchema = z3.object({
1451
+ image: z3.object({ base64: z3.string().min(1), format: z3.string().default("png") }).passthrough(),
1376
1452
  usage: UsageSchema
1377
1453
  }).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"),
1454
+ var UiAssetSubmitSchema = z3.object({
1455
+ ui_asset_id: z3.string().min(1),
1456
+ background_job_id: z3.string().min(1),
1457
+ status: z3.string().default("processing"),
1382
1458
  usage: UsageSchema
1383
1459
  }).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()
1460
+ var UiAssetGetSchema = z3.object({
1461
+ id: z3.string().min(1),
1462
+ status: z3.string().nullable().default(null),
1463
+ image_url: z3.string().nullable().optional(),
1464
+ progress_percent: z3.number().nullable().optional(),
1465
+ eta_seconds: z3.number().nullable().optional()
1390
1466
  }).passthrough();
1391
- var RevisionJobSubmitSchema = z2.object({
1392
- background_job_id: z2.string().min(1),
1393
- status: z2.string().default("processing")
1467
+ var RevisionJobSubmitSchema = z3.object({
1468
+ background_job_id: z3.string().min(1),
1469
+ status: z3.string().default("processing")
1394
1470
  }).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()
1471
+ var ObjectAnimationGroupSchema = z3.object({
1472
+ animation_group_id: z3.string().min(1),
1473
+ display_name: z3.string().nullable().default(null),
1474
+ description: z3.string().default(""),
1475
+ frame_count: z3.number().int().default(0),
1476
+ directions: z3.array(
1477
+ z3.object({
1478
+ direction: z3.string(),
1479
+ storage_urls: z3.record(z3.unknown()).default({}),
1480
+ created_at: z3.string()
1405
1481
  }).passthrough()
1406
1482
  ).default([])
1407
1483
  }).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([])
1484
+ var PixelLabObjectSchema = z3.object({
1485
+ id: z3.string().min(1),
1486
+ name: z3.string().nullable().default(null),
1487
+ state_name: z3.string().nullable().optional(),
1488
+ prompt: z3.string().default(""),
1489
+ size: z3.object({ width: z3.number().int().positive(), height: z3.number().int().positive() }),
1490
+ directions: z3.number().default(0),
1491
+ created_at: z3.string(),
1492
+ view: z3.string().nullable().default(null),
1493
+ preview_url: z3.string().nullable().optional(),
1494
+ rotation_urls: z3.record(z3.string().nullable()).nullable().optional(),
1495
+ frame_urls: z3.array(z3.string()).nullable().optional(),
1496
+ tags: z3.array(z3.string()).default([]),
1497
+ status: z3.string().nullable().default(null),
1498
+ progress_percent: z3.number().nullable().optional(),
1499
+ eta_seconds: z3.number().nullable().optional(),
1500
+ group_id: z3.string().nullable().optional(),
1501
+ animations: z3.array(ObjectAnimationGroupSchema).default([])
1502
+ }).passthrough();
1503
+ var MapObjectSchema = z3.object({
1504
+ object_id: z3.string().min(1),
1505
+ status: z3.string(),
1506
+ description: z3.string().nullable().default(null),
1507
+ width: z3.number().nullable().default(null),
1508
+ height: z3.number().nullable().default(null),
1509
+ download_url: z3.string().nullable().default(null)
1426
1510
  }).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)
1511
+ var ObjectListSchema = z3.object({ objects: z3.array(PixelLabObjectSchema), total: z3.number().int().min(0) }).passthrough();
1512
+ var PixfluxResponseSchema = z3.object({ image: z3.object({ base64: z3.string().min(1) }).passthrough(), usage: z3.unknown().optional() }).passthrough();
1513
+ var ReduceColorsResponseSchema = z3.object({
1514
+ images: z3.array(z3.object({ base64: z3.string().min(1) }).passthrough()).min(1),
1515
+ palette: z3.object({ base64: z3.string().min(1) }).passthrough(),
1516
+ n_colors: z3.number().int(),
1517
+ usage: z3.unknown().optional()
1434
1518
  }).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()
1519
+ var CorrectPixelartResponseSchema = z3.object({
1520
+ images: z3.array(z3.object({ base64: z3.string().min(1) }).passthrough()).min(1),
1521
+ usage: z3.unknown().optional()
1442
1522
  }).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()
1523
+ var ProFlashImageSubmitSchema = z3.object({
1524
+ background_job_id: z3.string().min(1),
1525
+ image_id: z3.string().min(1),
1526
+ source_image_id: z3.string().min(1),
1527
+ status: z3.string().default("processing"),
1528
+ estimated_generations: z3.number().nullable().optional(),
1529
+ usage: z3.unknown().optional()
1446
1530
  }).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()
1531
+ var UnzoomResponseSchema = z3.object({
1532
+ image: z3.object({ base64: z3.string().min(1) }).passthrough(),
1533
+ original_size: z3.object({ width: z3.number().int(), height: z3.number().int() }).passthrough(),
1534
+ unzoomed_size: z3.object({ width: z3.number().int(), height: z3.number().int() }).passthrough(),
1535
+ zoom_factor_detected: z3.number(),
1536
+ usage: z3.unknown().optional()
1454
1537
  }).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()
1538
+ var FontJobSchema = z3.object({
1539
+ job_id: z3.string().min(1),
1540
+ status: z3.string(),
1541
+ glyph_px: z3.number().int().nullable().optional(),
1542
+ download_atlas_url: z3.string().nullable().optional(),
1543
+ download_ttf_url: z3.string().nullable().optional(),
1544
+ usage: z3.unknown().optional()
1461
1545
  }).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()
1546
+ var SelectFramesSchema = z3.object({ created_object_ids: z3.array(z3.string()) }).passthrough();
1547
+ var EstimateSkeletonResponseSchema = z3.object({
1548
+ keypoints: z3.array(SkeletonKeypointSchema).length(18),
1549
+ usage: z3.unknown().optional()
1469
1550
  }).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"),
1551
+ var CharacterSubmitSchema = z3.object({
1552
+ background_job_id: z3.string().min(1),
1553
+ character_id: z3.string().min(1),
1554
+ status: z3.string().default("processing"),
1475
1555
  usage: UsageSchema
1476
1556
  }).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"),
1557
+ var AnimateSubmitSchema = z3.object({
1558
+ background_job_ids: z3.array(z3.string()),
1559
+ directions: z3.array(z3.string()),
1560
+ status: z3.string().default("processing"),
1481
1561
  usage: UsageSchema
1482
1562
  }).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"),
1563
+ var ObjectProSubmitSchema = z3.object({
1564
+ object_id: z3.string().min(1),
1565
+ background_job_id: z3.string().min(1),
1566
+ status: z3.string().default("processing"),
1487
1567
  usage: UsageSchema
1488
1568
  }).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)
1569
+ var DirectionSubmissionSchema = z3.object({
1570
+ direction: z3.string(),
1571
+ status: z3.string(),
1572
+ background_job_id: z3.string().nullable().default(null),
1573
+ animation_id: z3.string().nullable().default(null)
1494
1574
  }).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),
1575
+ var AnimateObjectSubmitSchema = z3.object({
1576
+ animation_group_id: z3.string().min(1),
1577
+ object_id: z3.string().min(1),
1578
+ mode: z3.string(),
1579
+ frame_count: z3.number().int(),
1580
+ display_name: z3.string().nullable().default(null),
1581
+ description: z3.string().default(""),
1582
+ submissions: z3.array(DirectionSubmissionSchema),
1503
1583
  usage: UsageSchema
1504
1584
  }).passthrough();
1505
- var PortraitSubmitSchema = z2.object({
1506
- background_job_id: z2.string().min(1),
1507
- status: z2.string().default("processing"),
1585
+ var PortraitSubmitSchema = z3.object({
1586
+ background_job_id: z3.string().min(1),
1587
+ status: z3.string().default("processing"),
1508
1588
  usage: UsageSchema
1509
1589
  }).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()
1590
+ var PortraitJobSchema = z3.object({
1591
+ status: z3.string(),
1592
+ download_url: z3.string().nullable().optional(),
1593
+ width: z3.number().int().nullable().optional(),
1594
+ height: z3.number().int().nullable().optional()
1515
1595
  }).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),
1596
+ var SetPortraitSchema = z3.object({
1597
+ character_id: z3.string().min(1),
1598
+ size: z3.number().int().positive(),
1599
+ url: z3.string().min(1),
1520
1600
  usage: UsageSchema
1521
1601
  }).passthrough();
1522
- var TransferOutfitSubmitSchema = z2.object({
1523
- background_job_id: z2.string().min(1),
1524
- status: z2.string().default("processing"),
1602
+ var TransferOutfitSubmitSchema = z3.object({
1603
+ background_job_id: z3.string().min(1),
1604
+ status: z3.string().default("processing"),
1525
1605
  usage: UsageSchema
1526
1606
  }).passthrough();
1527
- var CharacterAnimationDirectionSchema = z2.object({
1528
- direction: z2.string(),
1529
- frame_count: z2.number().int().min(0),
1530
- frames: z2.array(z2.string())
1607
+ var CharacterAnimationDirectionSchema = z3.object({
1608
+ direction: z3.string(),
1609
+ frame_count: z3.number().int().min(0),
1610
+ frames: z3.array(z3.string())
1531
1611
  }).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)
1612
+ var CharacterAnimationSchema2 = z3.object({
1613
+ animation_type: z3.string(),
1614
+ display_name: z3.string().nullable().optional(),
1615
+ animation_group_id: z3.string().nullable().optional(),
1616
+ directions: z3.array(CharacterAnimationDirectionSchema)
1537
1617
  }).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()
1618
+ var CharacterSchema = z3.object({
1619
+ id: z3.string().min(1),
1620
+ name: z3.string().default(""),
1621
+ state_name: z3.string().nullable().optional(),
1622
+ prompt: z3.string().default(""),
1623
+ size: z3.object({ width: z3.number().int().positive(), height: z3.number().int().positive() }),
1624
+ directions: z3.number().int(),
1625
+ created_at: z3.string(),
1626
+ updated_at: z3.string().nullable().optional(),
1627
+ animation_count: z3.number().int().default(0),
1628
+ template_id: z3.string().default("mannequin"),
1629
+ view: z3.string().nullable().optional(),
1630
+ status: z3.string().default("completed"),
1631
+ rotation_urls: z3.record(z3.string().nullable()).nullable().optional(),
1632
+ tags: z3.array(z3.string()).default([]),
1633
+ group_id: z3.string().nullable().optional(),
1634
+ animations: z3.array(CharacterAnimationSchema2).default([]),
1635
+ preview_url: z3.string().nullable().optional()
1556
1636
  }).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(),
1637
+ var CharacterListSchema = z3.object({ characters: z3.array(CharacterSchema), total: z3.number().int().min(0) }).passthrough();
1638
+ var BackgroundJobSchema = z3.object({
1639
+ id: z3.string(),
1640
+ status: z3.string(),
1641
+ last_response: z3.record(z3.unknown()).nullable().optional(),
1562
1642
  /**
1563
1643
  * The billed amount, confirmed live for a `map` object and a standard
1564
1644
  * character base (docs/ENDPOINTS.md, "Limits and billing"); duplicated at
@@ -1566,7 +1646,7 @@ var BackgroundJobSchema = z2.object({
1566
1646
  */
1567
1647
  usage: UsageSchema
1568
1648
  }).passthrough();
1569
- var DeleteAnimationsSchema = z2.object({ success: z2.boolean(), deleted_count: z2.number().int().default(0), error: z2.string().nullable().optional() }).passthrough();
1649
+ var DeleteAnimationsSchema = z3.object({ success: z3.boolean(), deleted_count: z3.number().int().default(0), error: z3.string().nullable().optional() }).passthrough();
1570
1650
  function validateResponse(schema, raw, operation) {
1571
1651
  const parsed = schema.safeParse(raw);
1572
1652
  if (parsed.success) return parsed.data;
@@ -2597,6 +2677,61 @@ var PixelLabClient = class {
2597
2677
  "animate-pixminimax"
2598
2678
  );
2599
2679
  }
2680
+ /**
2681
+ * `/animate-with-skeleton-v3`, beta (tier 1 subscription or higher): poses
2682
+ * one reference image frame-by-frame from a supplied skeleton per frame,
2683
+ * rather than a text motion description. Confirmed against the live
2684
+ * `animate_with_skeleton_v3` MCP tool schema (the same authoritative
2685
+ * source this codebase already treats as ground truth for the
2686
+ * character/object family) — `action`/`description` are both real,
2687
+ * optional wire fields, contrary to an initial guess that this endpoint
2688
+ * took no text input at all; `direction`/`keypoints`/`firstFrameKeypoints`
2689
+ * are required. The example keypoint from that same tool schema,
2690
+ * `{"label": "RIGHT KNEE", "x": 0.52, "y": 0.71, "z_index": 9}`, is what
2691
+ * `SkeletonKeypointSchema` (`src/skeleton.ts`) models. Same
2692
+ * unverified-completed-shape caveat as `animateWithTextV3`/
2693
+ * `animatePixminimax` above — this endpoint has never been called against
2694
+ * a live account by this codebase.
2695
+ */
2696
+ async animateWithSkeletonV3(args) {
2697
+ const body = {
2698
+ first_frame: args.firstFrame,
2699
+ first_frame_keypoints: args.firstFrameKeypoints,
2700
+ keypoints: args.keypoints,
2701
+ direction: args.direction
2702
+ };
2703
+ if (args.templateId) body.template_id = args.templateId;
2704
+ if (args.action) body.action = args.action;
2705
+ if (args.description) body.description = args.description;
2706
+ if (args.seed != null) body.seed = args.seed;
2707
+ if (args.noBackground != null) body.no_background = args.noBackground;
2708
+ return validateResponse(
2709
+ RevisionJobSubmitSchema,
2710
+ await this.request("/animate-with-skeleton-v3", { method: "POST", body: JSON.stringify(body) }),
2711
+ "animate-with-skeleton-v3"
2712
+ );
2713
+ }
2714
+ /**
2715
+ * `/estimate-skeleton`: auto-derives a reference image's own 18-joint
2716
+ * keypoints, directly usable as `animateWithSkeletonV3`'s
2717
+ * `firstFrameKeypoints` (or, run against a candidate pose image, as one
2718
+ * entry of its `keypoints` sequence). Not called anywhere in this
2719
+ * codebase's own submit/poll pipeline — deliberately kept outside it (see
2720
+ * `runEstimateSkeleton` in `src/cli/commands`) so the only PixelLab
2721
+ * endpoint composed into `animate-with-skeleton-v3`'s submission is that
2722
+ * one endpoint itself, not two unverified calls chained together.
2723
+ * Synchronous per this endpoint's own framing; unverified against a live
2724
+ * account (see `EstimateSkeletonResponseSchema`'s own caveat).
2725
+ */
2726
+ async estimateSkeleton(args) {
2727
+ const body = { image: args.image };
2728
+ const res = validateResponse(
2729
+ EstimateSkeletonResponseSchema,
2730
+ await this.request("/estimate-skeleton", { method: "POST", body: JSON.stringify(body) }),
2731
+ "estimate-skeleton"
2732
+ );
2733
+ return { keypoints: res.keypoints, usage: res.usage };
2734
+ }
2600
2735
  async getBackgroundJob(jobId) {
2601
2736
  const raw = await this.request(`/background-jobs/${encodeURIComponent(jobId)}`);
2602
2737
  return validateResponse(BackgroundJobSchema, raw, "background-jobs/{id}");
@@ -3331,6 +3466,9 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
3331
3466
  lastFrameSha256: spec.revision.lastFrameSha256,
3332
3467
  direction: spec.revision.direction,
3333
3468
  enhancePrompt: spec.revision.enhancePrompt,
3469
+ keypointsSha256: spec.revision.keypointsSha256,
3470
+ skeletonTemplate: spec.revision.skeletonTemplate,
3471
+ description: spec.revision.description,
3334
3472
  engine: spec.revision.engine
3335
3473
  } : void 0
3336
3474
  })
@@ -3419,6 +3557,18 @@ function objectProCost(spec) {
3419
3557
  const south = object.reference;
3420
3558
  return south ? proFlashObjectCost(south.width, south.height, object.directions, true) : proFlashObjectCost(spec.width, spec.height, object.directions, false);
3421
3559
  }
3560
+ function animateSkeletonCost(frames) {
3561
+ const anchors = [[3, 2], [8, 3], [15, 4]];
3562
+ for (let i = 0; i < anchors.length - 1; i++) {
3563
+ const [lowFrames, lowCost] = anchors[i];
3564
+ const [highFrames, highCost] = anchors[i + 1];
3565
+ if (frames >= lowFrames && frames <= highFrames) {
3566
+ const t = (frames - lowFrames) / (highFrames - lowFrames);
3567
+ return lowCost + t * (highCost - lowCost);
3568
+ }
3569
+ }
3570
+ return frames <= anchors[0][0] ? anchors[0][1] : anchors[anchors.length - 1][1];
3571
+ }
3422
3572
  var CHARACTER_TEMPLATES = ["mannequin", "bear", "cat", "dog", "horse", "lion"];
3423
3573
  var PRO_FLASH_TEMPLATES = [...CHARACTER_TEMPLATES, "custom"];
3424
3574
  function encodeAnimationJob(job) {
@@ -3477,7 +3627,9 @@ var PixelLabProvider = class _PixelLabProvider {
3477
3627
  * rather than a described edit — and `animate`/`animate-pixminimax`
3478
3628
  * (`/animate-with-text-v3`, `/animate-pixminimax`), which animate any loose
3479
3629
  * image from a text description with no character/object resource
3480
- * required — and `interpolate` (`/interpolation-v2`, the in-betweens
3630
+ * required — `animate-skeleton` (`/animate-with-skeleton-v3`, posing the
3631
+ * source from a supplied per-frame skeleton instead of a text description)
3632
+ * — and `interpolate` (`/interpolation-v2`, the in-betweens
3481
3633
  * from one keyframe to another) and `edit-animation`
3482
3634
  * (`/edit-animation-v2`, one text edit applied across a whole frame set).
3483
3635
  * `outpaint` does not: there is no canvas-expansion endpoint in the API,
@@ -3485,7 +3637,7 @@ var PixelLabProvider = class _PixelLabProvider {
3485
3637
  * outpaint path yet.
3486
3638
  */
3487
3639
  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";
3640
+ 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
3641
  }
3490
3642
  /** PixelLab's own constraints: submissions must be >2s apart, and
3491
3643
  * background jobs in flight are capped by subscription tier (Tier 1=8,
@@ -3516,6 +3668,10 @@ var PixelLabProvider = class _PixelLabProvider {
3516
3668
  if (spec.revision?.mode === "reduce-colors" || spec.revision?.mode === "correct-pixelart") {
3517
3669
  return { unit: "generations", amount: 0.1, candidates: 1 };
3518
3670
  }
3671
+ if (spec.revision?.mode === "animate-skeleton") {
3672
+ const frames = spec.revision.frames ?? 8;
3673
+ return { unit: "generations", amount: Math.ceil(animateSkeletonCost(frames)), candidates: 1 };
3674
+ }
3519
3675
  if (spec.revision?.mode === "interpolate" || spec.revision?.mode === "edit-animation") {
3520
3676
  const width = spec.revision.sourceWidth ?? spec.width;
3521
3677
  const height = spec.revision.sourceHeight ?? spec.height;
@@ -3597,6 +3753,18 @@ var PixelLabProvider = class _PixelLabProvider {
3597
3753
  throw new Error(`PixelLab correct-pixelart source is ${width}x${height}; the API takes at most 1024 pixels per side`);
3598
3754
  }
3599
3755
  }
3756
+ if (spec.revision?.mode === "animate-skeleton") {
3757
+ const { sourceWidth: width, sourceHeight: height, frames, skeletonTemplate } = spec.revision;
3758
+ if (width != null && height != null && (width > 256 || height > 256)) {
3759
+ throw new Error(`PixelLab animate-skeleton source is ${width}x${height}; the API takes at most 256 pixels per side`);
3760
+ }
3761
+ if (frames != null && (frames < 3 || frames > 15)) {
3762
+ throw new Error(`PixelLab animate-skeleton takes 3 to 15 frames; the keypoints file has ${frames}`);
3763
+ }
3764
+ if (skeletonTemplate && !CHARACTER_TEMPLATES.includes(skeletonTemplate)) {
3765
+ throw new Error(`PixelLab animate-skeleton template "${skeletonTemplate}" is not one of ${CHARACTER_TEMPLATES.join(", ")}`);
3766
+ }
3767
+ }
3600
3768
  if (spec.revision?.mode === "animate" || spec.revision?.mode === "animate-pixminimax") {
3601
3769
  const { sourceWidth: width, sourceHeight: height, mode: mode2, frames, lastFrameWidth, lastFrameHeight } = spec.revision;
3602
3770
  if (width != null && height != null && (width > 256 || height > 256)) {
@@ -4746,6 +4914,27 @@ var PixelLabProvider = class _PixelLabProvider {
4746
4914
  });
4747
4915
  return { jobId: res2.background_job_id };
4748
4916
  }
4917
+ if (revision.mode === "animate-skeleton") {
4918
+ if (!revision.keypointsFile || !revision.keypointsSha256 || !revision.skeleton) {
4919
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision keypoints are not ready`);
4920
+ }
4921
+ const keypointsBytes = readFileSync(revision.keypointsFile);
4922
+ if (sha256(keypointsBytes) !== revision.keypointsSha256) {
4923
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision keypoints changed after the manifest was resolved`);
4924
+ }
4925
+ const res2 = await this.client.animateWithSkeletonV3({
4926
+ firstFrame: image,
4927
+ firstFrameKeypoints: revision.skeleton.firstFrameKeypoints,
4928
+ keypoints: revision.skeleton.frames,
4929
+ direction: revision.direction,
4930
+ templateId: revision.skeletonTemplate,
4931
+ action: spec.prompt,
4932
+ description: revision.description,
4933
+ seed: spec.seed,
4934
+ noBackground: spec.noBackground
4935
+ });
4936
+ return { jobId: res2.background_job_id };
4937
+ }
4749
4938
  if (revision.strength != null) {
4750
4939
  throw new Error(
4751
4940
  `${spec.styleId}/${spec.assetId}: PixelLab image-to-image revisions take no strength; edit-images-v2 always applies the full instruction`
@@ -4811,7 +5000,7 @@ var PixelLabProvider = class _PixelLabProvider {
4811
5000
  if (revisionMode === "reduce-colors" || revisionMode === "correct-pixelart") {
4812
5001
  return this.pollCachedRevision(jobId, context);
4813
5002
  }
4814
- if (revisionMode === "animate" || revisionMode === "animate-pixminimax" || revisionMode === "interpolate" || revisionMode === "edit-animation") {
5003
+ if (revisionMode === "animate" || revisionMode === "animate-pixminimax" || revisionMode === "animate-skeleton" || revisionMode === "interpolate" || revisionMode === "edit-animation") {
4815
5004
  return this.pollAnimateRevision(jobId, context);
4816
5005
  }
4817
5006
  if (context?.spec?.revision) return this.pollRevision(jobId);
@@ -6546,11 +6735,12 @@ var ComfyUIProvider = class _ComfyUIProvider {
6546
6735
  /**
6547
6736
  * Everything else resolves generically to whatever the user's own
6548
6737
  * 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.
6738
+ * (`animate`, `animate-pixminimax`, `animate-skeleton`, `interpolate`,
6739
+ * `edit-animation`) are the exception: they produce an ordered frame set,
6740
+ * and this adapter's revision path always writes a single output image
6741
+ * (see `submit`/`fetch` below) — a real structural gap, not a missing
6742
+ * binding, so they are rejected here rather than only failing once
6743
+ * `validate` runs.
6554
6744
  */
6555
6745
  supportsRevision(mode2) {
6556
6746
  return !FRAME_SET_REVISION_MODES.includes(mode2);
@@ -8453,6 +8643,8 @@ async function resolveSpecs(loaded, filter) {
8453
8643
  const paletteImage = paletteImageFile ? await optionalRevisionImage(paletteImageFile, "revision palette image", "png") : null;
8454
8644
  const lastFrameFile = asset.revision.lastFrame ? path5.resolve(root, asset.revision.lastFrame) : void 0;
8455
8645
  const lastFrame = lastFrameFile ? await optionalRevisionImage(lastFrameFile, "revision last frame") : null;
8646
+ const keypointsFile = asset.revision.keypointsFile ? path5.resolve(root, asset.revision.keypointsFile) : void 0;
8647
+ const keypoints = keypointsFile ? await optionalSkeletonSet(keypointsFile) : null;
8456
8648
  resolved.revision = {
8457
8649
  mode: asset.revision.mode,
8458
8650
  sourceAssetId: asset.revision.from,
@@ -8493,6 +8685,18 @@ async function resolveSpecs(loaded, filter) {
8493
8685
  } : {},
8494
8686
  ...asset.revision.direction ? { direction: asset.revision.direction } : {},
8495
8687
  ...asset.revision.enhancePrompt == null ? {} : { enhancePrompt: asset.revision.enhancePrompt },
8688
+ ...keypointsFile ? {
8689
+ keypointsFile,
8690
+ keypointsSha256: keypoints?.hash ?? null,
8691
+ skeleton: keypoints?.skeleton ?? null,
8692
+ // The manifest's own `frames` field stays rejected for this
8693
+ // mode (superRefine, types.ts) — the real frame count comes
8694
+ // from the keypoints file once it exists, not a manifest
8695
+ // number, so plan can only know it once authored.
8696
+ ...keypoints ? { frames: keypoints.skeleton.frames.length } : {}
8697
+ } : {},
8698
+ ...asset.revision.skeletonTemplate ? { skeletonTemplate: asset.revision.skeletonTemplate } : {},
8699
+ ...asset.revision.description ? { description: asset.revision.description } : {},
8496
8700
  ...asset.revision.engine ? { engine: asset.revision.engine } : {}
8497
8701
  };
8498
8702
  }
@@ -8614,6 +8818,17 @@ async function optionalRevisionImage(file, label, requiredFormat) {
8614
8818
  }
8615
8819
  return { hash: sha256(bytes), ...metadata };
8616
8820
  }
8821
+ async function optionalSkeletonSet(file) {
8822
+ if (!existsSync4(file)) return null;
8823
+ const bytes = await readFile3(file);
8824
+ let json;
8825
+ try {
8826
+ json = JSON.parse(bytes.toString("utf8"));
8827
+ } catch (error) {
8828
+ throw new Error(`revision keypoints file ${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
8829
+ }
8830
+ return { hash: sha256(bytes), skeleton: parseSkeletonSet(json, file) };
8831
+ }
8617
8832
  async function revisionSourceMembers(stem, label) {
8618
8833
  if (existsSync4(stem)) return null;
8619
8834
  const at = (role) => memberPath(stem, role, 0, 2, MediaType.PNG);
@@ -10736,47 +10951,47 @@ import path14 from "path";
10736
10951
  import { existsSync as existsSync9 } from "fs";
10737
10952
  import { readdir, readFile as readFile7 } from "fs/promises";
10738
10953
  import { fileURLToPath } from "url";
10739
- import { z as z3 } from "zod";
10954
+ import { z as z4 } from "zod";
10740
10955
  var SHA256_RE = /^[0-9a-f]{64}$/;
10741
10956
  var RECIPE_ID_RE = /^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/;
10742
10957
  var VERSION_RE = /^\d+\.\d+\.\d+$/;
10743
10958
  var RECIPE_FILE = "pixelkiln.recipe.json";
10744
10959
  var RECIPE_DIR_TOKEN = "{{recipeDir}}";
10745
- var PortablePathSchema = z3.string().min(1).refine((value) => {
10960
+ var PortablePathSchema = z4.string().min(1).refine((value) => {
10746
10961
  if (value.includes("\\") || path14.posix.isAbsolute(value)) return false;
10747
10962
  const parts = value.split("/");
10748
10963
  return !parts.includes("") && !parts.includes(".") && !parts.includes("..");
10749
10964
  }, "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)
10965
+ var RecipeBindingSchema = z4.object({
10966
+ nodeId: z4.string().min(1),
10967
+ input: z4.string().min(1)
10753
10968
  }).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({
10969
+ var RecipeSchema = z4.object({
10970
+ $schema: z4.string().url().optional(),
10971
+ format: z4.literal("pixelkiln-recipe"),
10972
+ schemaVersion: z4.literal(1),
10973
+ id: z4.string().regex(RECIPE_ID_RE, "expected provider/name"),
10974
+ version: z4.string().regex(VERSION_RE, "expected x.y.z"),
10975
+ provider: z4.string().regex(/^[a-z0-9][a-z0-9-]*$/),
10976
+ summary: z4.string().min(1).max(240),
10977
+ files: z4.array(z4.object({
10763
10978
  path: PortablePathSchema,
10764
- role: z3.enum(["workflow", "reference"]),
10765
- sha256: z3.string().regex(SHA256_RE)
10979
+ role: z4.enum(["workflow", "reference"]),
10980
+ sha256: z4.string().regex(SHA256_RE)
10766
10981
  }).strict()).default([]),
10767
- models: z3.array(z3.object({
10982
+ models: z4.array(z4.object({
10768
10983
  path: PortablePathSchema,
10769
- sha256: z3.string().regex(SHA256_RE),
10770
- source: z3.string().url(),
10771
- license: z3.string().min(1)
10984
+ sha256: z4.string().regex(SHA256_RE),
10985
+ source: z4.string().url(),
10986
+ license: z4.string().min(1)
10772
10987
  }).strict()).default([]),
10773
- styleId: z3.string().regex(/^[a-z0-9][a-z0-9-]*$/),
10988
+ styleId: z4.string().regex(/^[a-z0-9][a-z0-9-]*$/),
10774
10989
  style: StyleSchema,
10775
- workflow: z3.object({
10990
+ workflow: z4.object({
10776
10991
  path: PortablePathSchema,
10777
- outputNodeId: z3.string().min(1),
10778
- numImages: z3.number().int().min(1).max(16),
10779
- bindings: z3.object({
10992
+ outputNodeId: z4.string().min(1),
10993
+ numImages: z4.number().int().min(1).max(16),
10994
+ bindings: z4.object({
10780
10995
  prompt: RecipeBindingSchema,
10781
10996
  width: RecipeBindingSchema.optional(),
10782
10997
  height: RecipeBindingSchema.optional(),
@@ -10787,67 +11002,67 @@ var RecipeSchema = z3.object({
10787
11002
  strength: RecipeBindingSchema.optional()
10788
11003
  }).strict()
10789
11004
  }).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)
11005
+ quality: z4.object({
11006
+ stage: z4.enum(["composition-source", "production-candidate"]),
11007
+ workingCanvas: z4.object({
11008
+ width: z4.number().int().min(16),
11009
+ height: z4.number().int().min(16)
10795
11010
  }).strict(),
10796
- recommendedNativeSize: z3.object({
10797
- min: z3.number().int().min(1),
10798
- max: z3.number().int().min(1)
11011
+ recommendedNativeSize: z4.object({
11012
+ min: z4.number().int().min(1),
11013
+ max: z4.number().int().min(1)
10799
11014
  }).strict(),
10800
- paletteColors: z3.object({
10801
- min: z3.number().int().min(1),
10802
- max: z3.number().int().min(1)
11015
+ paletteColors: z4.object({
11016
+ min: z4.number().int().min(1),
11017
+ max: z4.number().int().min(1)
10803
11018
  }).strict(),
10804
- background: z3.enum(["opaque", "transparent", "full-bleed"]),
10805
- checks: z3.array(z3.enum([
11019
+ background: z4.enum(["opaque", "transparent", "full-bleed"]),
11020
+ checks: z4.array(z4.enum([
10806
11021
  "prompt-coverage",
10807
11022
  "native-grid",
10808
11023
  "final-palette",
10809
11024
  "alpha-edge",
10810
11025
  "human-1x"
10811
11026
  ])).min(1),
10812
- notes: z3.array(z3.string().min(1)).default([])
11027
+ notes: z4.array(z4.string().min(1)).default([])
10813
11028
  }).strict(),
10814
- integrity: z3.object({
10815
- algorithm: z3.literal("sha256"),
10816
- digest: z3.string().regex(SHA256_RE)
11029
+ integrity: z4.object({
11030
+ algorithm: z4.literal("sha256"),
11031
+ digest: z4.string().regex(SHA256_RE)
10817
11032
  }).strict()
10818
11033
  }).strict().superRefine((recipe, context) => {
10819
11034
  if (recipe.style.provider && recipe.style.provider !== recipe.provider) {
10820
11035
  context.addIssue({
10821
- code: z3.ZodIssueCode.custom,
11036
+ code: z4.ZodIssueCode.custom,
10822
11037
  path: ["style", "provider"],
10823
11038
  message: "must match the recipe provider"
10824
11039
  });
10825
11040
  }
10826
11041
  const filePaths = recipe.files.map((file) => file.path);
10827
11042
  if (new Set(filePaths).size !== filePaths.length) {
10828
- context.addIssue({ code: z3.ZodIssueCode.custom, path: ["files"], message: "duplicate file path" });
11043
+ context.addIssue({ code: z4.ZodIssueCode.custom, path: ["files"], message: "duplicate file path" });
10829
11044
  }
10830
11045
  const modelPaths = recipe.models.map((model) => model.path);
10831
11046
  if (new Set(modelPaths).size !== modelPaths.length) {
10832
- context.addIssue({ code: z3.ZodIssueCode.custom, path: ["models"], message: "duplicate model path" });
11047
+ context.addIssue({ code: z4.ZodIssueCode.custom, path: ["models"], message: "duplicate model path" });
10833
11048
  }
10834
11049
  if (filePaths.includes(RECIPE_FILE)) {
10835
11050
  context.addIssue({
10836
- code: z3.ZodIssueCode.custom,
11051
+ code: z4.ZodIssueCode.custom,
10837
11052
  path: ["files"],
10838
11053
  message: `${RECIPE_FILE} cannot hash itself`
10839
11054
  });
10840
11055
  }
10841
11056
  if (recipe.quality.recommendedNativeSize.min > recipe.quality.recommendedNativeSize.max) {
10842
11057
  context.addIssue({
10843
- code: z3.ZodIssueCode.custom,
11058
+ code: z4.ZodIssueCode.custom,
10844
11059
  path: ["quality", "recommendedNativeSize"],
10845
11060
  message: "min cannot exceed max"
10846
11061
  });
10847
11062
  }
10848
11063
  if (recipe.quality.paletteColors.min > recipe.quality.paletteColors.max) {
10849
11064
  context.addIssue({
10850
- code: z3.ZodIssueCode.custom,
11065
+ code: z4.ZodIssueCode.custom,
10851
11066
  path: ["quality", "paletteColors"],
10852
11067
  message: "min cannot exceed max"
10853
11068
  });
@@ -10856,7 +11071,7 @@ var RecipeSchema = z3.object({
10856
11071
  (file) => file.path === recipe.workflow.path && file.role === "workflow"
10857
11072
  )) {
10858
11073
  context.addIssue({
10859
- code: z3.ZodIssueCode.custom,
11074
+ code: z4.ZodIssueCode.custom,
10860
11075
  path: ["workflow", "path"],
10861
11076
  message: "must name a file whose role is workflow"
10862
11077
  });
@@ -10864,7 +11079,7 @@ var RecipeSchema = z3.object({
10864
11079
  if (recipe.provider === "comfyui") {
10865
11080
  if (!recipe.workflow) {
10866
11081
  context.addIssue({
10867
- code: z3.ZodIssueCode.custom,
11082
+ code: z4.ZodIssueCode.custom,
10868
11083
  path: ["workflow"],
10869
11084
  message: "ComfyUI recipes require workflow metadata"
10870
11085
  });
@@ -10873,7 +11088,7 @@ var RecipeSchema = z3.object({
10873
11088
  const options = recipe.style.providerOptions.comfyui;
10874
11089
  if (!options || typeof options !== "object" || Array.isArray(options)) {
10875
11090
  context.addIssue({
10876
- code: z3.ZodIssueCode.custom,
11091
+ code: z4.ZodIssueCode.custom,
10877
11092
  path: ["style", "providerOptions", "comfyui"],
10878
11093
  message: "ComfyUI recipes require ComfyUI provider options"
10879
11094
  });
@@ -10887,7 +11102,7 @@ var RecipeSchema = z3.object({
10887
11102
  };
10888
11103
  if (JSON.stringify(canonical3(options)) !== JSON.stringify(canonical3(expected))) {
10889
11104
  context.addIssue({
10890
- code: z3.ZodIssueCode.custom,
11105
+ code: z4.ZodIssueCode.custom,
10891
11106
  path: ["style", "providerOptions", "comfyui"],
10892
11107
  message: "must match workflow metadata and use {{recipeDir}} for workflowFile"
10893
11108
  });
@@ -11325,11 +11540,11 @@ async function generateFont(client2, opts) {
11325
11540
  import path17 from "path";
11326
11541
  import { existsSync as existsSync12 } from "fs";
11327
11542
  import { readFile as readFile10 } from "fs/promises";
11328
- import { z as z4 } from "zod";
11543
+ import { z as z5 } from "zod";
11329
11544
  var SHA256_RE2 = /^[0-9a-f]{64}$/;
11330
11545
  var HEX_RE = /^#[0-9a-f]{6}$/;
11331
11546
  var MAX_REDMEAN_DISTANCE = 765;
11332
- var QualityPathSchema = z4.string().min(1).refine(
11547
+ var QualityPathSchema = z5.string().min(1).refine(
11333
11548
  (value) => !path17.posix.isAbsolute(value) && !value.includes("\\"),
11334
11549
  "expected a portable relative path"
11335
11550
  );
@@ -11343,53 +11558,53 @@ var DEFAULT_QUALITY_TOLERANCES = {
11343
11558
  maxEdgeContrastDrop: 0.03,
11344
11559
  maxIsolatedPixelIncrease: 5e-3
11345
11560
  };
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)
11561
+ var QualityToleranceSchema = z5.object({
11562
+ requireExactHash: z5.boolean(),
11563
+ maxColorCountIncrease: z5.number().int().min(0),
11564
+ maxNewColors: z5.number().int().min(0),
11565
+ maxTransparencyDelta: z5.number().min(0).max(1),
11566
+ maxPartialAlphaIncrease: z5.number().min(0).max(1),
11567
+ maxEdgeDensityDelta: z5.number().min(0).max(1),
11568
+ maxEdgeContrastDrop: z5.number().min(0).max(1),
11569
+ maxIsolatedPixelIncrease: z5.number().min(0).max(1)
11355
11570
  }).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)
11571
+ var ImageQualityMetricsSchema = z5.object({
11572
+ sha256: z5.string().regex(SHA256_RE2),
11573
+ width: z5.number().int().min(1),
11574
+ height: z5.number().int().min(1),
11575
+ colorCount: z5.number().int().min(0),
11576
+ colors: z5.array(z5.string().regex(HEX_RE)),
11577
+ transparency: z5.number().min(0).max(1),
11578
+ partialAlpha: z5.number().min(0).max(1),
11579
+ edgeDensity: z5.number().min(0).max(1),
11580
+ meanEdgeContrast: z5.number().min(0).max(1),
11581
+ isolatedPixelRatio: z5.number().min(0).max(1)
11367
11582
  }).strict();
11368
- var QualityBaselineCaseSchema = z4.object({
11369
- id: z4.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
11583
+ var QualityBaselineCaseSchema = z5.object({
11584
+ id: z5.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
11370
11585
  file: QualityPathSchema,
11371
- record: z4.object({
11586
+ record: z5.object({
11372
11587
  path: QualityPathSchema,
11373
- sha256: z4.string().regex(SHA256_RE2)
11588
+ sha256: z5.string().regex(SHA256_RE2)
11374
11589
  }).strict().optional(),
11375
11590
  expected: ImageQualityMetricsSchema,
11376
11591
  tolerances: QualityToleranceSchema
11377
11592
  }).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)
11593
+ var QualityBaselineSchema = z5.object({
11594
+ $schema: z5.string().url().optional(),
11595
+ format: z5.literal("pixelkiln-quality-baseline"),
11596
+ schemaVersion: z5.literal(1),
11597
+ cases: z5.array(QualityBaselineCaseSchema).min(1)
11383
11598
  }).strict().superRefine((baseline, context) => {
11384
11599
  const ids = baseline.cases.map((entry) => entry.id);
11385
11600
  if (new Set(ids).size !== ids.length) {
11386
- context.addIssue({ code: z4.ZodIssueCode.custom, path: ["cases"], message: "duplicate case id" });
11601
+ context.addIssue({ code: z5.ZodIssueCode.custom, path: ["cases"], message: "duplicate case id" });
11387
11602
  }
11388
11603
  });
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(),
11604
+ var QualityInputSchema = z5.object({
11605
+ id: z5.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
11606
+ path: z5.string().min(1),
11607
+ record: z5.string().min(1).optional(),
11393
11608
  tolerances: QualityToleranceSchema.partial().optional()
11394
11609
  }).strict();
11395
11610
  function message4(error) {
@@ -11491,7 +11706,7 @@ async function measureImageQuality(file) {
11491
11706
  }
11492
11707
  }
11493
11708
  function resolveQualityInputs(raw, inputsFilePath) {
11494
- const parsed = z4.array(QualityInputSchema).min(1).safeParse(raw);
11709
+ const parsed = z5.array(QualityInputSchema).min(1).safeParse(raw);
11495
11710
  if (!parsed.success) {
11496
11711
  throw new Error(
11497
11712
  "--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 +11929,11 @@ import path20 from "path";
11714
11929
  import { mkdir as mkdir5, readFile as readFile11, rename as rename4, rm as rm4, writeFile as writeFile5 } from "fs/promises";
11715
11930
  import { existsSync as existsSync13 } from "fs";
11716
11931
  import pathModule from "path";
11717
- import { z as z5 } from "zod";
11718
- var HashCacheSchema = z5.object({
11719
- version: z5.literal(1),
11932
+ import { z as z6 } from "zod";
11933
+ var HashCacheSchema = z6.object({
11934
+ version: z6.literal(1),
11720
11935
  /** objectId → sha256 of that object's image bytes. */
11721
- hashes: z5.record(z5.string())
11936
+ hashes: z6.record(z6.string())
11722
11937
  });
11723
11938
  var SHA256_HEX = /^[a-f0-9]{64}$/;
11724
11939
  function isSha256Hash(value) {
@@ -13266,6 +13481,9 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
13266
13481
  ...spec.revision.lastFrameSha256 ? { lastFrameSha256: spec.revision.lastFrameSha256 } : {},
13267
13482
  ...spec.revision.direction ? { direction: spec.revision.direction } : {},
13268
13483
  ...spec.revision.enhancePrompt == null ? {} : { enhancePrompt: spec.revision.enhancePrompt },
13484
+ ...spec.revision.keypointsSha256 ? { keypointsSha256: spec.revision.keypointsSha256 } : {},
13485
+ ...spec.revision.skeletonTemplate ? { skeletonTemplate: spec.revision.skeletonTemplate } : {},
13486
+ ...spec.revision.description ? { description: spec.revision.description } : {},
13269
13487
  ...spec.revision.engine ? { engine: spec.revision.engine } : {}
13270
13488
  } : null,
13271
13489
  outfit: spec.character?.kind === "outfit" && outfitParentEntry ? {
@@ -14563,20 +14781,20 @@ import path29 from "path";
14563
14781
  import { mkdir as mkdir7, readFile as readFile18, rename as rename6, rm as rm7, writeFile as writeFile9 } from "fs/promises";
14564
14782
  import { existsSync as existsSync21 } from "fs";
14565
14783
  import path25 from "path";
14566
- import { z as z6 } from "zod";
14567
- var WorkspaceProjectSchema = z6.object({
14568
- id: z6.string().min(1),
14784
+ import { z as z7 } from "zod";
14785
+ var WorkspaceProjectSchema = z7.object({
14786
+ id: z7.string().min(1),
14569
14787
  /** Manifest path, relative to the catalog file's own directory. */
14570
- manifest: z6.string().min(1),
14788
+ manifest: z7.string().min(1),
14571
14789
  /** Lockfile path, relative to the catalog file's own directory. */
14572
- lock: z6.string().min(1),
14573
- provider: z6.string().min(1).default("pixellab"),
14790
+ lock: z7.string().min(1),
14791
+ provider: z7.string().min(1).default("pixellab"),
14574
14792
  /** Free-form label for a shared account, e.g. distinguishing sandboxes. */
14575
- account: z6.string().optional()
14793
+ account: z7.string().optional()
14576
14794
  }).strict();
14577
- var WorkspaceSchema = z6.object({
14578
- version: z6.literal(1),
14579
- projects: z6.array(WorkspaceProjectSchema).default([])
14795
+ var WorkspaceSchema = z7.object({
14796
+ version: z7.literal(1),
14797
+ projects: z7.array(WorkspaceProjectSchema).default([])
14580
14798
  }).strict();
14581
14799
  function parseWorkspace(raw) {
14582
14800
  const parsed = WorkspaceSchema.safeParse(raw);
@@ -14694,62 +14912,62 @@ function validateWorkspace(ws, dir) {
14694
14912
 
14695
14913
  // src/gallery/edit.ts
14696
14914
  import path28 from "path";
14697
- import { z as z9 } from "zod";
14915
+ import { z as z10 } from "zod";
14698
14916
 
14699
14917
  // src/manifest-edit.ts
14700
14918
  import { readFile as readFile19, rename as rename7, rm as rm8, writeFile as writeFile10 } from "fs/promises";
14701
14919
  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(),
14920
+ import { z as z8 } from "zod";
14921
+ var HexSha = z8.string().regex(/^[0-9a-f]{64}$/);
14922
+ var AssetPatchSchema = z8.object({
14923
+ prompt: z8.string().optional(),
14706
14924
  /** 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()
14925
+ promptForStyle: z8.object({ styleId: z8.string().min(1), prompt: z8.string().nullable() }).optional(),
14926
+ category: z8.string().nullable().optional(),
14927
+ width: z8.number().int().nullable().optional(),
14928
+ height: z8.number().int().nullable().optional(),
14929
+ size: z8.number().int().nullable().optional(),
14930
+ tags: z8.array(z8.string()).optional()
14713
14931
  }).strict();
14714
- var NewStateSchema = z7.object({
14932
+ var NewStateSchema = z8.object({
14715
14933
  /** 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()
14934
+ of: z8.string().min(1),
14935
+ paletteFromReference: z8.boolean().optional(),
14936
+ canvas: z8.object({ width: z8.number().int().min(16).max(256), height: z8.number().int().min(16).max(256) }).strict().optional()
14719
14937
  }).strict();
14720
- var NewAnimationSchema = z7.object({
14938
+ var NewAnimationSchema = z8.object({
14721
14939
  /** Asset id of the parent this animates; must already exist. */
14722
- of: z7.string().min(1),
14940
+ of: z8.string().min(1),
14723
14941
  /** A PixelLab template id (`character` only; `objectPro` has no template concept). */
14724
- template: z7.string().min(1).optional(),
14942
+ template: z8.string().min(1).optional(),
14725
14943
  direction: CharacterDirectionSchema.optional(),
14726
- frames: z7.number().int().min(4).max(16).optional(),
14727
- fps: z7.number().int().min(1).max(60).optional(),
14944
+ frames: z8.number().int().min(4).max(16).optional(),
14945
+ fps: z8.number().int().min(1).max(60).optional(),
14728
14946
  mode: CharacterAnimationModeSchema.optional(),
14729
14947
  /** 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()
14948
+ subject: z8.string().min(1).optional(),
14949
+ enhancePrompt: z8.boolean().optional()
14732
14950
  }).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(),
14951
+ var NewAssetSchema = z8.object({
14952
+ prompt: z8.string(),
14953
+ category: z8.string().optional(),
14954
+ width: z8.number().int().optional(),
14955
+ height: z8.number().int().optional(),
14956
+ size: z8.number().int().optional(),
14957
+ tags: z8.array(z8.string()).optional(),
14740
14958
  /** Restrict to these styles; empty or omitted means every style. */
14741
- styles: z7.array(z7.string().min(1)).optional(),
14959
+ styles: z8.array(z8.string().min(1)).optional(),
14742
14960
  /**
14743
14961
  * A controlled regeneration of another asset in the same manifest,
14744
14962
  * instead of a fresh generation. `inpaint` needs a mask upload the
14745
14963
  * gallery does not offer yet, so only `image-to-image` can be created
14746
14964
  * here; hand-edit the manifest for the other modes.
14747
14965
  */
14748
- revision: z7.object({
14749
- mode: z7.literal("image-to-image"),
14966
+ revision: z8.object({
14967
+ mode: z8.literal("image-to-image"),
14750
14968
  /** 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()
14969
+ from: z8.string().min(1),
14970
+ strength: z8.number().min(0).max(1).optional()
14753
14971
  }).strict().optional(),
14754
14972
  /** A pose or outfit of an existing `character`/`objectPro` base or state. */
14755
14973
  state: NewStateSchema.optional(),
@@ -14765,58 +14983,58 @@ var CANDIDATE_OPTION = {
14765
14983
  comfyui: "numImages",
14766
14984
  scenario: "numOutputs"
14767
14985
  };
14768
- var ManifestEditSchema = z7.discriminatedUnion("action", [
14769
- z7.object({
14770
- action: z7.literal("patch-asset"),
14986
+ var ManifestEditSchema = z8.discriminatedUnion("action", [
14987
+ z8.object({
14988
+ action: z8.literal("patch-asset"),
14771
14989
  /** Workspace project id; omitted for a single-project gallery. */
14772
- project: z7.string().min(1).optional(),
14773
- assetId: z7.string().min(1),
14990
+ project: z8.string().min(1).optional(),
14991
+ assetId: z8.string().min(1),
14774
14992
  /** Manifest bytes the page last saw; a mismatch refuses the write. */
14775
14993
  expectedSha256: HexSha,
14776
14994
  patch: AssetPatchSchema
14777
14995
  }).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"),
14996
+ z8.object({
14997
+ action: z8.literal("add-asset"),
14998
+ project: z8.string().min(1).optional(),
14999
+ assetId: z8.string().min(1).regex(/^[^/\\]+$/, "asset ids cannot contain slashes"),
14782
15000
  expectedSha256: HexSha,
14783
15001
  asset: NewAssetSchema
14784
15002
  }).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),
15003
+ z8.object({
15004
+ action: z8.literal("set-source"),
15005
+ project: z8.string().min(1).optional(),
15006
+ assetId: z8.string().min(1),
15007
+ styleId: z8.string().min(1),
14790
15008
  /** Manifest-relative path of the art that stands in for this asset in this style. */
14791
- source: z7.string().min(1),
15009
+ source: z8.string().min(1),
14792
15010
  expectedSha256: HexSha
14793
15011
  }).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),
15012
+ z8.object({
15013
+ action: z8.literal("clear-source"),
15014
+ project: z8.string().min(1).optional(),
15015
+ assetId: z8.string().min(1),
15016
+ styleId: z8.string().min(1),
14799
15017
  expectedSha256: HexSha
14800
15018
  }).strict(),
14801
- z7.object({
14802
- action: z7.literal("patch-style"),
14803
- project: z7.string().min(1).optional(),
14804
- styleId: z7.string().min(1),
15019
+ z8.object({
15020
+ action: z8.literal("patch-style"),
15021
+ project: z8.string().min(1).optional(),
15022
+ styleId: z8.string().min(1),
14805
15023
  expectedSha256: HexSha,
14806
- patch: z7.object({
15024
+ patch: z8.object({
14807
15025
  /** Candidates per generation, written to the provider's own option. */
14808
- candidates: z7.number().int().min(1).max(64).optional(),
15026
+ candidates: z8.number().int().min(1).max(64).optional(),
14809
15027
  /** Empty clears the style's own value (inherit, or the default). */
14810
- promptPrefix: z7.string().optional(),
14811
- promptSuffix: z7.string().optional(),
15028
+ promptPrefix: z8.string().optional(),
15029
+ promptSuffix: z8.string().optional(),
14812
15030
  /** `#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(),
15031
+ palette: z8.array(z8.string().regex(/^#?[0-9a-f]{6}$/i, "expected a six-digit hex colour")).max(256).nullable().optional(),
14814
15032
  /** Snap downloaded art to the palette; false clears the style's own value. */
14815
- enforcePalette: z7.boolean().optional(),
15033
+ enforcePalette: z8.boolean().optional(),
14816
15034
  /** Provider view name (PixelLab: low top-down, high top-down, side); empty clears. */
14817
- view: z7.string().max(64).nullable().optional(),
15035
+ view: z8.string().max(64).nullable().optional(),
14818
15036
  /** pixflux only; null clears the style's own value. */
14819
- noBackground: z7.boolean().nullable().optional()
15037
+ noBackground: z8.boolean().nullable().optional()
14820
15038
  }).strict().refine((patch) => Object.keys(patch).length > 0, { message: "nothing to change" })
14821
15039
  }).strict()
14822
15040
  ]);
@@ -15010,7 +15228,7 @@ async function applyManifestEdit(manifestPath, edit) {
15010
15228
  import { existsSync as existsSync22 } from "fs";
15011
15229
  import { copyFile, mkdir as mkdir8, readFile as readFile20, rename as rename8, rm as rm9, writeFile as writeFile11 } from "fs/promises";
15012
15230
  import path27 from "path";
15013
- import { z as z8 } from "zod";
15231
+ import { z as z9 } from "zod";
15014
15232
  var HAND_EDIT_DIR = "edits";
15015
15233
  function handEditPath(loaded, spec) {
15016
15234
  const style = loaded.manifest.styles[spec.styleId];
@@ -15107,35 +15325,35 @@ async function detachHandEdit(loaded, spec, opts = {}) {
15107
15325
  });
15108
15326
  return { manifestSha256: result.sha256, changed: result.changed };
15109
15327
  }
15110
- var CompanionOutputSchema = z8.object({
15328
+ var CompanionOutputSchema = z9.object({
15111
15329
  /** Output role for a set member; null for a single image. */
15112
- role: z8.string().nullable(),
15330
+ role: z9.string().nullable(),
15113
15331
  /** 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(),
15332
+ basedOn: z9.string().regex(/^[0-9a-f]{64}$/).nullable(),
15115
15333
  /** sha256 of the member as saved, so a later change by another tool is visible. */
15116
- sha256: z8.string().regex(/^[0-9a-f]{64}$/)
15334
+ sha256: z9.string().regex(/^[0-9a-f]{64}$/)
15117
15335
  }).strict();
15118
- var CompanionV2Schema = z8.object({
15119
- version: z8.literal(2),
15336
+ var CompanionV2Schema = z9.object({
15337
+ version: z9.literal(2),
15120
15338
  /** 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(),
15339
+ editor: z9.string().min(1),
15340
+ protocol: z9.number().int().positive(),
15341
+ savedAt: z9.string().datetime(),
15124
15342
  /** Basename of the layered project file beside the edit, when one was kept. */
15125
- project: z8.string().min(1).nullable(),
15343
+ project: z9.string().min(1).nullable(),
15126
15344
  /** One entry per file, in output order. */
15127
- outputs: z8.array(CompanionOutputSchema).min(1)
15345
+ outputs: z9.array(CompanionOutputSchema).min(1)
15128
15346
  }).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()
15347
+ var CompanionV1Schema = z9.object({
15348
+ version: z9.literal(1),
15349
+ basedOn: z9.string().regex(/^[0-9a-f]{64}$/).nullable(),
15350
+ sha256: z9.string().regex(/^[0-9a-f]{64}$/),
15351
+ editor: z9.string().min(1),
15352
+ protocol: z9.number().int().positive(),
15353
+ savedAt: z9.string().datetime(),
15354
+ project: z9.string().min(1).nullable()
15137
15355
  }).strict();
15138
- var HandEditCompanionSchema = z8.union([CompanionV2Schema, CompanionV1Schema]).transform(
15356
+ var HandEditCompanionSchema = z9.union([CompanionV2Schema, CompanionV1Schema]).transform(
15139
15357
  (value) => value.version === 2 ? value : {
15140
15358
  version: 2,
15141
15359
  editor: value.editor,
@@ -15255,40 +15473,40 @@ function openInEditor(file, editor = process.env.PIXELKILN_EDITOR) {
15255
15473
  }
15256
15474
 
15257
15475
  // 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}$/),
15476
+ var Base64 = z10.string().regex(/^[A-Za-z0-9+/]*={0,2}$/).max(Math.ceil(MAX_HAND_EDIT_BYTES / 3) * 4);
15477
+ var HandEditRequestSchema = z10.discriminatedUnion("action", [
15478
+ z10.object({
15479
+ action: z10.literal("start-edit"),
15480
+ project: z10.string().min(1).optional(),
15481
+ assetId: z10.string().min(1),
15482
+ styleId: z10.string().min(1),
15483
+ expectedSha256: z10.string().regex(/^[0-9a-f]{64}$/),
15266
15484
  /** Also launch the author's editor on the file. */
15267
- open: z9.boolean().optional()
15485
+ open: z10.boolean().optional()
15268
15486
  }).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}$/)
15487
+ z10.object({
15488
+ action: z10.literal("detach-edit"),
15489
+ project: z10.string().min(1).optional(),
15490
+ assetId: z10.string().min(1),
15491
+ styleId: z10.string().min(1),
15492
+ expectedSha256: z10.string().regex(/^[0-9a-f]{64}$/)
15275
15493
  }).strict(),
15276
15494
  /** 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}$/),
15495
+ z10.object({
15496
+ action: z10.literal("save-edit"),
15497
+ project: z10.string().min(1).optional(),
15498
+ assetId: z10.string().min(1),
15499
+ styleId: z10.string().min(1),
15500
+ expectedSha256: z10.string().regex(/^[0-9a-f]{64}$/),
15283
15501
  /** The flattened image; for a frame set, `frames` instead. */
15284
15502
  png: Base64.min(1).optional(),
15285
15503
  /** 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(),
15504
+ frames: z10.array(z10.object({ role: z10.string().min(1).nullable(), png: Base64.min(1) }).strict()).min(1).max(256).optional(),
15287
15505
  /** The editor's layered project file, kept beside the edit. */
15288
15506
  pxo: Base64.optional(),
15289
15507
  /** Editor identity from the bridge's ready message. */
15290
- editor: z9.string().min(1).max(200),
15291
- protocol: z9.number().int().positive()
15508
+ editor: z10.string().min(1).max(200),
15509
+ protocol: z10.number().int().positive()
15292
15510
  }).strict()
15293
15511
  ]);
15294
15512
  function createGalleryEditHandler(opts) {
@@ -16622,27 +16840,27 @@ async function serveGallery(opts) {
16622
16840
 
16623
16841
  // src/gallery/generate.ts
16624
16842
  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),
16843
+ import { z as z11 } from "zod";
16844
+ var GenerateRequestSchema = z11.object({
16845
+ project: z11.string().min(1).optional(),
16846
+ keys: z11.array(z11.string().min(1)).min(1).max(500),
16629
16847
  /**
16630
16848
  * Regenerate work that is up to date, as `gen --force` does, and let the
16631
16849
  * fetch replace a locally modified or untracked file. With `restore`, put
16632
16850
  * the recorded bytes back over a modified file.
16633
16851
  */
16634
- force: z10.boolean().optional(),
16852
+ force: z11.boolean().optional(),
16635
16853
  /** Advance in-flight, review, or selected work without submitting anything. */
16636
- resume: z10.boolean().optional(),
16854
+ resume: z11.boolean().optional(),
16637
16855
  /** Re-download downloaded outputs and replace files whose object changed upstream. */
16638
- refresh: z10.boolean().optional(),
16856
+ refresh: z11.boolean().optional(),
16639
16857
  /** Put recorded outputs back on disk from the cache or the provider, without generating. */
16640
- restore: z10.boolean().optional(),
16858
+ restore: z11.boolean().optional(),
16641
16859
  /**
16642
16860
  * Bring a replaced generation back: 1-based position in the record's
16643
16861
  * history (newest first) or an output hash prefix. One key at a time.
16644
16862
  */
16645
- revert: z10.string().min(1).max(64).optional()
16863
+ revert: z11.string().min(1).max(64).optional()
16646
16864
  }).strict().refine((request) => [request.resume, request.refresh, request.restore, request.revert !== void 0].filter(Boolean).length <= 1, {
16647
16865
  message: "resume, refresh, restore, and revert are exclusive"
16648
16866
  }).refine((request) => request.revert === void 0 || request.keys.length === 1, { message: "revert takes one key" });