pixelkiln 0.65.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
@@ -1,7 +1,7 @@
1
1
  // src/types.ts
2
2
  import { z } from "zod";
3
3
  var MediaTypeSchema = z.enum(["image/png", "image/gif"]);
4
- var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile", "objectPro", "uiAsset", "uiElement"]);
4
+ var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile", "objectPro", "uiAsset", "uiElement", "imageProFlash"]);
5
5
  var GridConfidenceSchema = z.enum(["low", "medium", "high"]);
6
6
  var RevisionModeSchema = z.enum([
7
7
  "image-to-image",
@@ -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
  ]);
@@ -328,8 +329,46 @@ var RevisionSchema = z.object({
328
329
  /** `animate-pixminimax` only: facing direction, used only alongside `enhancePrompt` to hold the sprite's facing. */
329
330
  direction: CharacterDirectionSchema.optional(),
330
331
  /** `animate`/`animate-pixminimax` only: let PixelLab expand the action into a fuller motion description first. */
331
- enhancePrompt: z.boolean().optional()
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(),
357
+ /**
358
+ * `image-to-image`/`inpaint` only: which provider engine draws the
359
+ * edit. `pro-flash` selects PixelLab's Pro Flash edit and inpaint tier
360
+ * (`/edit-image-pro-flash`, `/inpaint-image-pro-flash`) instead of the
361
+ * default Pro endpoints; omit it for the default.
362
+ */
363
+ engine: z.enum(["pro-flash"]).optional()
332
364
  }).strict().superRefine((revision, context) => {
365
+ if (revision.engine !== void 0 && revision.mode !== "image-to-image" && revision.mode !== "inpaint") {
366
+ context.addIssue({
367
+ code: z.ZodIssueCode.custom,
368
+ message: "engine applies to image-to-image and inpaint revisions only",
369
+ path: ["engine"]
370
+ });
371
+ }
333
372
  if (revision.mode === "inpaint" && !revision.mask) {
334
373
  context.addIssue({
335
374
  code: z.ZodIssueCode.custom,
@@ -405,18 +444,41 @@ var RevisionSchema = z.object({
405
444
  path: ["fps"]
406
445
  });
407
446
  }
408
- if (revision.direction !== void 0 && revision.mode !== "animate-pixminimax") {
447
+ if (revision.direction !== void 0 && revision.mode !== "animate-pixminimax" && revision.mode !== "animate-skeleton") {
409
448
  context.addIssue({
410
449
  code: z.ZodIssueCode.custom,
411
- 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",
412
458
  path: ["direction"]
413
459
  });
414
460
  }
415
461
  if (revision.frames !== void 0 && revision.frames % 2 !== 0) {
416
462
  context.addIssue({ code: z.ZodIssueCode.custom, message: "frames must be even", path: ["frames"] });
417
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
+ }
418
480
  });
419
- 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"];
420
482
  var MEMBER_SET_REVISION_MODES = ["reduce-colors", "correct-pixelart", "edit-animation"];
421
483
  var StyleObjectSchema = z.object({
422
484
  /** Generation backend for this style. Omit to inherit the manifest default. */
@@ -618,8 +680,8 @@ var StyleObjectSchema = z.object({
618
680
  /** `character` only. Body template: `mannequin` (default), a quadruped (`bear`, `cat`, `dog`, `horse`, `lion`), or `custom` for `pro-flash`. */
619
681
  template: z.string().min(1).optional(),
620
682
  /**
621
- * `character` only, `pro-flash` bases with a style image: which traits
622
- * the style image lends. Each defaults to true.
683
+ * `character` `pro-flash` bases and `imageProFlash` styles, with a
684
+ * style image: which traits the style image lends. Each defaults to true.
623
685
  */
624
686
  styleTraits: z.object({
625
687
  palette: z.boolean().optional(),
@@ -1048,11 +1110,17 @@ var LockEntrySchema = z.object({
1048
1110
  paletteImageSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1049
1111
  dithering: RevisionDitheringSchema.optional(),
1050
1112
  ditheringStrength: z.number().min(0).max(10).optional(),
1051
- 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(),
1052
1116
  fps: z.number().int().min(1).max(60).optional(),
1053
1117
  lastFrameSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1054
1118
  direction: CharacterDirectionSchema.optional(),
1055
- enhancePrompt: z.boolean().optional()
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(),
1123
+ engine: z.enum(["pro-flash"]).optional()
1056
1124
  }).strict().nullable().default(null),
1057
1125
  /** For a mirror: the asset it flips and a hash over that asset's output hashes when this was made. */
1058
1126
  mirror: z.object({
@@ -1238,7 +1306,7 @@ function exitCodeFor(err) {
1238
1306
  }
1239
1307
 
1240
1308
  // src/client.ts
1241
- import { z as z2 } from "zod";
1309
+ import { z as z3 } from "zod";
1242
1310
 
1243
1311
  // src/http.ts
1244
1312
  var MAX_RETRIES = 4;
@@ -1288,254 +1356,289 @@ function fetchWithRetry(request, opts = {}) {
1288
1356
  };
1289
1357
  }
1290
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
+
1291
1382
  // src/client.ts
1292
1383
  var BASE = process.env.PIXELLAB_API_BASE ?? "https://api.pixellab.ai/v2";
1293
1384
  var MAX_DOWNLOAD_BYTES = 64 * 1024 * 1024;
1294
- var BalanceResponseSchema = z2.object({
1295
- credits: z2.object({ usd: z2.number() }).passthrough(),
1296
- subscription: z2.object({
1297
- generations: z2.number(),
1298
- total: z2.number(),
1299
- 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()
1300
1391
  }).passthrough()
1301
1392
  }).passthrough();
1302
- var ObjectSubmitSchema = z2.object({
1303
- background_job_id: z2.string().min(1),
1304
- object_id: z2.string().min(1),
1305
- status: z2.string().default("queued"),
1306
- 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)
1307
1398
  }).passthrough();
1308
- var MapSubmitSchema = z2.object({
1309
- background_job_id: z2.string().min(1),
1310
- object_id: z2.string().min(1),
1311
- 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")
1312
1403
  }).passthrough();
1313
- var UsageSchema = z2.object({
1314
- type: z2.string().optional(),
1315
- usd: z2.number().nullable().optional(),
1316
- 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()
1317
1408
  }).passthrough().nullable().optional();
1318
- var TilesSubmitSchema = z2.object({
1319
- tile_id: z2.string().min(1),
1320
- background_job_id: z2.string().min(1),
1321
- 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")
1322
1413
  }).passthrough();
1323
- var TilesProSchema = z2.object({
1324
- storage_urls: z2.record(z2.string()),
1325
- kind: z2.string().nullable().default(null),
1326
- 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()
1327
1418
  }).passthrough();
1328
- var TilesetSubmitSchema = z2.object({
1329
- tileset_id: z2.string().min(1),
1330
- background_job_id: z2.string().min(1),
1331
- 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")
1332
1423
  }).passthrough();
1333
- var TilesetTileSchema = z2.object({
1334
- id: z2.string().min(1),
1335
- name: z2.string(),
1336
- image: z2.object({ base64: z2.string().min(1), format: z2.string().default("png") }).passthrough(),
1337
- corners: z2.object({ NW: z2.string(), NE: z2.string(), SW: z2.string(), SE: z2.string() }).passthrough(),
1338
- pattern_4x4: z2.object({
1339
- row_0: z2.array(z2.number()),
1340
- row_1: z2.array(z2.number()),
1341
- row_2: z2.array(z2.number()),
1342
- 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())
1343
1434
  }).passthrough()
1344
1435
  }).passthrough();
1345
- var TilesetGetSchema = z2.object({
1346
- tileset: z2.object({
1347
- total_tiles: z2.number().int().min(1),
1348
- tile_size: z2.object({ width: z2.number().int().positive(), height: z2.number().int().positive() }).passthrough(),
1349
- terrain_types: z2.array(z2.string()),
1350
- 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)
1351
1442
  }).passthrough(),
1352
1443
  usage: UsageSchema
1353
1444
  }).passthrough();
1354
- var IsometricTileSubmitSchema = z2.object({
1355
- tile_id: z2.string().min(1),
1356
- background_job_id: z2.string().min(1),
1357
- 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")
1358
1449
  }).passthrough();
1359
- var IsometricTileGetSchema = z2.object({
1360
- 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(),
1361
1452
  usage: UsageSchema
1362
1453
  }).passthrough();
1363
- var UiAssetSubmitSchema = z2.object({
1364
- ui_asset_id: z2.string().min(1),
1365
- background_job_id: z2.string().min(1),
1366
- 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"),
1367
1458
  usage: UsageSchema
1368
1459
  }).passthrough();
1369
- var UiAssetGetSchema = z2.object({
1370
- id: z2.string().min(1),
1371
- status: z2.string().nullable().default(null),
1372
- image_url: z2.string().nullable().optional(),
1373
- progress_percent: z2.number().nullable().optional(),
1374
- 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()
1375
1466
  }).passthrough();
1376
- var RevisionJobSubmitSchema = z2.object({
1377
- background_job_id: z2.string().min(1),
1378
- status: z2.string().default("processing")
1467
+ var RevisionJobSubmitSchema = z3.object({
1468
+ background_job_id: z3.string().min(1),
1469
+ status: z3.string().default("processing")
1379
1470
  }).passthrough();
1380
- var ObjectAnimationGroupSchema = z2.object({
1381
- animation_group_id: z2.string().min(1),
1382
- display_name: z2.string().nullable().default(null),
1383
- description: z2.string().default(""),
1384
- frame_count: z2.number().int().default(0),
1385
- directions: z2.array(
1386
- z2.object({
1387
- direction: z2.string(),
1388
- storage_urls: z2.record(z2.unknown()).default({}),
1389
- 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()
1390
1481
  }).passthrough()
1391
1482
  ).default([])
1392
1483
  }).passthrough();
1393
- var PixelLabObjectSchema = z2.object({
1394
- id: z2.string().min(1),
1395
- name: z2.string().nullable().default(null),
1396
- state_name: z2.string().nullable().optional(),
1397
- prompt: z2.string().default(""),
1398
- size: z2.object({ width: z2.number().int().positive(), height: z2.number().int().positive() }),
1399
- directions: z2.number().default(0),
1400
- created_at: z2.string(),
1401
- view: z2.string().nullable().default(null),
1402
- preview_url: z2.string().nullable().optional(),
1403
- rotation_urls: z2.record(z2.string().nullable()).nullable().optional(),
1404
- frame_urls: z2.array(z2.string()).nullable().optional(),
1405
- tags: z2.array(z2.string()).default([]),
1406
- status: z2.string().nullable().default(null),
1407
- progress_percent: z2.number().nullable().optional(),
1408
- eta_seconds: z2.number().nullable().optional(),
1409
- group_id: z2.string().nullable().optional(),
1410
- 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)
1510
+ }).passthrough();
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()
1411
1518
  }).passthrough();
1412
- var MapObjectSchema = z2.object({
1413
- object_id: z2.string().min(1),
1414
- status: z2.string(),
1415
- description: z2.string().nullable().default(null),
1416
- width: z2.number().nullable().default(null),
1417
- height: z2.number().nullable().default(null),
1418
- download_url: z2.string().nullable().default(null)
1519
+ var CorrectPixelartResponseSchema = z3.object({
1520
+ images: z3.array(z3.object({ base64: z3.string().min(1) }).passthrough()).min(1),
1521
+ usage: z3.unknown().optional()
1419
1522
  }).passthrough();
1420
- var ObjectListSchema = z2.object({ objects: z2.array(PixelLabObjectSchema), total: z2.number().int().min(0) }).passthrough();
1421
- var PixfluxResponseSchema = z2.object({ image: z2.object({ base64: z2.string().min(1) }).passthrough(), usage: z2.unknown().optional() }).passthrough();
1422
- var ReduceColorsResponseSchema = z2.object({
1423
- images: z2.array(z2.object({ base64: z2.string().min(1) }).passthrough()).min(1),
1424
- palette: z2.object({ base64: z2.string().min(1) }).passthrough(),
1425
- n_colors: z2.number().int(),
1426
- 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()
1427
1530
  }).passthrough();
1428
- var CorrectPixelartResponseSchema = z2.object({
1429
- images: z2.array(z2.object({ base64: z2.string().min(1) }).passthrough()).min(1),
1430
- 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()
1431
1537
  }).passthrough();
1432
- var UnzoomResponseSchema = z2.object({
1433
- image: z2.object({ base64: z2.string().min(1) }).passthrough(),
1434
- original_size: z2.object({ width: z2.number().int(), height: z2.number().int() }).passthrough(),
1435
- unzoomed_size: z2.object({ width: z2.number().int(), height: z2.number().int() }).passthrough(),
1436
- zoom_factor_detected: z2.number(),
1437
- 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()
1438
1545
  }).passthrough();
1439
- var FontJobSchema = z2.object({
1440
- job_id: z2.string().min(1),
1441
- status: z2.string(),
1442
- glyph_px: z2.number().int().nullable().optional(),
1443
- download_atlas_url: z2.string().nullable().optional(),
1444
- download_ttf_url: z2.string().nullable().optional(),
1445
- 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()
1446
1550
  }).passthrough();
1447
- var SelectFramesSchema = z2.object({ created_object_ids: z2.array(z2.string()) }).passthrough();
1448
- var CharacterSubmitSchema = z2.object({
1449
- background_job_id: z2.string().min(1),
1450
- character_id: z2.string().min(1),
1451
- 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"),
1452
1555
  usage: UsageSchema
1453
1556
  }).passthrough();
1454
- var AnimateSubmitSchema = z2.object({
1455
- background_job_ids: z2.array(z2.string()),
1456
- directions: z2.array(z2.string()),
1457
- 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"),
1458
1561
  usage: UsageSchema
1459
1562
  }).passthrough();
1460
- var ObjectProSubmitSchema = z2.object({
1461
- object_id: z2.string().min(1),
1462
- background_job_id: z2.string().min(1),
1463
- 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"),
1464
1567
  usage: UsageSchema
1465
1568
  }).passthrough();
1466
- var DirectionSubmissionSchema = z2.object({
1467
- direction: z2.string(),
1468
- status: z2.string(),
1469
- background_job_id: z2.string().nullable().default(null),
1470
- 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)
1471
1574
  }).passthrough();
1472
- var AnimateObjectSubmitSchema = z2.object({
1473
- animation_group_id: z2.string().min(1),
1474
- object_id: z2.string().min(1),
1475
- mode: z2.string(),
1476
- frame_count: z2.number().int(),
1477
- display_name: z2.string().nullable().default(null),
1478
- description: z2.string().default(""),
1479
- 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),
1480
1583
  usage: UsageSchema
1481
1584
  }).passthrough();
1482
- var PortraitSubmitSchema = z2.object({
1483
- background_job_id: z2.string().min(1),
1484
- status: z2.string().default("processing"),
1585
+ var PortraitSubmitSchema = z3.object({
1586
+ background_job_id: z3.string().min(1),
1587
+ status: z3.string().default("processing"),
1485
1588
  usage: UsageSchema
1486
1589
  }).passthrough();
1487
- var PortraitJobSchema = z2.object({
1488
- status: z2.string(),
1489
- download_url: z2.string().nullable().optional(),
1490
- width: z2.number().int().nullable().optional(),
1491
- 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()
1492
1595
  }).passthrough();
1493
- var SetPortraitSchema = z2.object({
1494
- character_id: z2.string().min(1),
1495
- size: z2.number().int().positive(),
1496
- 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),
1497
1600
  usage: UsageSchema
1498
1601
  }).passthrough();
1499
- var TransferOutfitSubmitSchema = z2.object({
1500
- background_job_id: z2.string().min(1),
1501
- status: z2.string().default("processing"),
1602
+ var TransferOutfitSubmitSchema = z3.object({
1603
+ background_job_id: z3.string().min(1),
1604
+ status: z3.string().default("processing"),
1502
1605
  usage: UsageSchema
1503
1606
  }).passthrough();
1504
- var CharacterAnimationDirectionSchema = z2.object({
1505
- direction: z2.string(),
1506
- frame_count: z2.number().int().min(0),
1507
- 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())
1508
1611
  }).passthrough();
1509
- var CharacterAnimationSchema2 = z2.object({
1510
- animation_type: z2.string(),
1511
- display_name: z2.string().nullable().optional(),
1512
- animation_group_id: z2.string().nullable().optional(),
1513
- 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)
1514
1617
  }).passthrough();
1515
- var CharacterSchema = z2.object({
1516
- id: z2.string().min(1),
1517
- name: z2.string().default(""),
1518
- state_name: z2.string().nullable().optional(),
1519
- prompt: z2.string().default(""),
1520
- size: z2.object({ width: z2.number().int().positive(), height: z2.number().int().positive() }),
1521
- directions: z2.number().int(),
1522
- created_at: z2.string(),
1523
- updated_at: z2.string().nullable().optional(),
1524
- animation_count: z2.number().int().default(0),
1525
- template_id: z2.string().default("mannequin"),
1526
- view: z2.string().nullable().optional(),
1527
- status: z2.string().default("completed"),
1528
- rotation_urls: z2.record(z2.string().nullable()).nullable().optional(),
1529
- tags: z2.array(z2.string()).default([]),
1530
- group_id: z2.string().nullable().optional(),
1531
- animations: z2.array(CharacterAnimationSchema2).default([]),
1532
- 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()
1533
1636
  }).passthrough();
1534
- var CharacterListSchema = z2.object({ characters: z2.array(CharacterSchema), total: z2.number().int().min(0) }).passthrough();
1535
- var BackgroundJobSchema = z2.object({
1536
- id: z2.string(),
1537
- status: z2.string(),
1538
- 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(),
1539
1642
  /**
1540
1643
  * The billed amount, confirmed live for a `map` object and a standard
1541
1644
  * character base (docs/ENDPOINTS.md, "Limits and billing"); duplicated at
@@ -1543,7 +1646,7 @@ var BackgroundJobSchema = z2.object({
1543
1646
  */
1544
1647
  usage: UsageSchema
1545
1648
  }).passthrough();
1546
- 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();
1547
1650
  function validateResponse(schema, raw, operation) {
1548
1651
  const parsed = schema.safeParse(raw);
1549
1652
  if (parsed.success) return parsed.data;
@@ -1869,7 +1972,7 @@ var PixelLabClient = class {
1869
1972
  const body = {
1870
1973
  description: args.description,
1871
1974
  n_directions: args.directions,
1872
- ...args.reference ? { first_frame: encode(args.reference) } : { image_size: { width: args.size, height: args.size } },
1975
+ ...args.sourceImageId ? { source_image_id: args.sourceImageId } : args.reference ? { first_frame: encode(args.reference) } : { image_size: { width: args.size, height: args.size } },
1873
1976
  ...args.view ? { view: args.view } : {},
1874
1977
  ...args.seed != null ? { seed: args.seed } : {},
1875
1978
  ...args.styleReference && args.styleReferenceSize ? {
@@ -2016,8 +2119,9 @@ var PixelLabClient = class {
2016
2119
  description: args.description,
2017
2120
  template_id: args.template,
2018
2121
  n_directions: 8,
2019
- // The author's sprite sets the canvas; a size only applies to text creation.
2020
- ...south ? { first_frame: encode(south) } : { image_size: imageSize },
2122
+ // The author's sprite sets the canvas; a size only applies to text
2123
+ // creation. An owned copy of the same sprite is referenced by id.
2124
+ ...args.sourceImageId ? { source_image_id: args.sourceImageId } : south ? { first_frame: encode(south) } : { image_size: imageSize },
2021
2125
  ...args.view ? { view: args.view } : {},
2022
2126
  ...args.seed != null ? { seed: args.seed } : {},
2023
2127
  ...args.styleReference && args.styleReferenceSize ? {
@@ -2289,6 +2393,66 @@ var PixelLabClient = class {
2289
2393
  "generate-image-v2"
2290
2394
  );
2291
2395
  }
2396
+ /**
2397
+ * `/create-image-pro-flash`: one native pixel-art image from a
2398
+ * description, optionally styled after a style image with per-trait
2399
+ * toggles, on the same Pro Flash model `character`/`objectPro` use for
2400
+ * their `pro-flash` engine. Sizes run 16 to 256 per side in multiples of 4.
2401
+ * A plain background job; `pollRevision`'s generic reader takes the
2402
+ * finished image. The response's `source_image_id` is what a Pro Flash
2403
+ * character or object can reuse instead of an upload.
2404
+ */
2405
+ async createImageProFlash(args) {
2406
+ const body = {
2407
+ description: args.description,
2408
+ image_size: { width: args.width, height: args.height }
2409
+ };
2410
+ if (args.noBackground != null) body.no_background = args.noBackground;
2411
+ if (args.seed != null) body.seed = args.seed;
2412
+ if (args.styleImage) {
2413
+ body.style_image = { image: args.styleImage.image, size: { width: args.styleImage.width, height: args.styleImage.height } };
2414
+ const traits = args.styleTraits;
2415
+ if (traits) {
2416
+ body.style_options = {
2417
+ ...traits.palette !== void 0 ? { color_palette: traits.palette } : {},
2418
+ ...traits.outline !== void 0 ? { outline: traits.outline } : {},
2419
+ ...traits.detail !== void 0 ? { detail: traits.detail } : {},
2420
+ ...traits.shading !== void 0 ? { shading: traits.shading } : {}
2421
+ };
2422
+ }
2423
+ }
2424
+ return validateResponse(
2425
+ ProFlashImageSubmitSchema,
2426
+ await this.request("/create-image-pro-flash", { method: "POST", body: JSON.stringify(body) }),
2427
+ "create-image-pro-flash"
2428
+ );
2429
+ }
2430
+ /** `/edit-image-pro-flash`, text method: edit one image at its own size. The canvas never grows or resizes. */
2431
+ async editImageProFlash(args) {
2432
+ const body = { image: args.image, method: "text", description: args.description };
2433
+ if (args.noBackground != null) body.no_background = args.noBackground;
2434
+ if (args.seed != null) body.seed = args.seed;
2435
+ return validateResponse(
2436
+ ProFlashImageSubmitSchema,
2437
+ await this.request("/edit-image-pro-flash", { method: "POST", body: JSON.stringify(body) }),
2438
+ "edit-image-pro-flash"
2439
+ );
2440
+ }
2441
+ /**
2442
+ * `/inpaint-image-pro-flash`: replace only the white pixels of a same-size
2443
+ * black/white mask, keeping everything else exactly. `Modify current
2444
+ * layer` (the endpoint's default) returns the full composite.
2445
+ */
2446
+ async inpaintImageProFlash(args) {
2447
+ const body = { image: args.image, mask_image: args.maskImage, description: args.description };
2448
+ if (args.noBackground != null) body.no_background = args.noBackground;
2449
+ if (args.seed != null) body.seed = args.seed;
2450
+ return validateResponse(
2451
+ ProFlashImageSubmitSchema,
2452
+ await this.request("/inpaint-image-pro-flash", { method: "POST", body: JSON.stringify(body) }),
2453
+ "inpaint-image-pro-flash"
2454
+ );
2455
+ }
2292
2456
  /**
2293
2457
  * `/generate-ui-v2` ("Generate UI (Pro)"): one UI element (a button, a
2294
2458
  * health bar, an inventory slot, a dialogue box) from a description,
@@ -2513,6 +2677,61 @@ var PixelLabClient = class {
2513
2677
  "animate-pixminimax"
2514
2678
  );
2515
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
+ }
2516
2735
  async getBackgroundJob(jobId) {
2517
2736
  const raw = await this.request(`/background-jobs/${encodeURIComponent(jobId)}`);
2518
2737
  return validateResponse(BackgroundJobSchema, raw, "background-jobs/{id}");
@@ -3169,6 +3388,7 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
3169
3388
  uiPieces: spec.uiPieces,
3170
3389
  uiElements: spec.uiElements,
3171
3390
  uiColorPalette: spec.uiColorPalette,
3391
+ styleTraits: spec.styleTraits,
3172
3392
  styleImages: styleImageHashes,
3173
3393
  // A character family: the engine and rotations of a base, the parent's
3174
3394
  // generated bytes for a state or animation (so a regenerated parent
@@ -3245,7 +3465,11 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
3245
3465
  fps: spec.revision.fps,
3246
3466
  lastFrameSha256: spec.revision.lastFrameSha256,
3247
3467
  direction: spec.revision.direction,
3248
- enhancePrompt: spec.revision.enhancePrompt
3468
+ enhancePrompt: spec.revision.enhancePrompt,
3469
+ keypointsSha256: spec.revision.keypointsSha256,
3470
+ skeletonTemplate: spec.revision.skeletonTemplate,
3471
+ description: spec.revision.description,
3472
+ engine: spec.revision.engine
3249
3473
  } : void 0
3250
3474
  })
3251
3475
  );
@@ -3301,8 +3525,11 @@ function proFlashCharacterCost(width, height, fromReference) {
3301
3525
  const side = Math.max(32, Math.ceil(Math.max(width, height) / 4) * 4);
3302
3526
  const rotations = Math.max(1, Math.ceil(side * side * 8 / 65536));
3303
3527
  if (fromReference) return rotations;
3304
- const image = side <= 96 ? 5 : side <= 208 ? 6 : 9;
3305
- return image + rotations;
3528
+ return proFlashImageCost(side, side) + rotations;
3529
+ }
3530
+ function proFlashImageCost(width, height) {
3531
+ const side = Math.max(width, height);
3532
+ return side <= 96 ? 5 : side <= 208 ? 6 : 9;
3306
3533
  }
3307
3534
  function characterAnimationName(spec) {
3308
3535
  return `pixelkiln:${spec.styleId}/${spec.assetId}`;
@@ -3330,6 +3557,18 @@ function objectProCost(spec) {
3330
3557
  const south = object.reference;
3331
3558
  return south ? proFlashObjectCost(south.width, south.height, object.directions, true) : proFlashObjectCost(spec.width, spec.height, object.directions, false);
3332
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
+ }
3333
3572
  var CHARACTER_TEMPLATES = ["mannequin", "bear", "cat", "dog", "horse", "lion"];
3334
3573
  var PRO_FLASH_TEMPLATES = [...CHARACTER_TEMPLATES, "custom"];
3335
3574
  function encodeAnimationJob(job) {
@@ -3378,7 +3617,7 @@ var PixelLabProvider = class _PixelLabProvider {
3378
3617
  return new _PixelLabProvider(new PixelLabClient("download-only"));
3379
3618
  }
3380
3619
  supports(generator) {
3381
- return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character" || generator === "isometricTile" || generator === "objectPro" || generator === "uiAsset" || generator === "uiElement";
3620
+ 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";
3382
3621
  }
3383
3622
  /**
3384
3623
  * `inpaint` (`/inpaint-v3`, a mask) and `image-to-image` (`/edit-images-v2`,
@@ -3388,7 +3627,9 @@ var PixelLabProvider = class _PixelLabProvider {
3388
3627
  * rather than a described edit — and `animate`/`animate-pixminimax`
3389
3628
  * (`/animate-with-text-v3`, `/animate-pixminimax`), which animate any loose
3390
3629
  * image from a text description with no character/object resource
3391
- * 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
3392
3633
  * from one keyframe to another) and `edit-animation`
3393
3634
  * (`/edit-animation-v2`, one text edit applied across a whole frame set).
3394
3635
  * `outpaint` does not: there is no canvas-expansion endpoint in the API,
@@ -3396,7 +3637,7 @@ var PixelLabProvider = class _PixelLabProvider {
3396
3637
  * outpaint path yet.
3397
3638
  */
3398
3639
  supportsRevision(mode2) {
3399
- 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";
3400
3641
  }
3401
3642
  /** PixelLab's own constraints: submissions must be >2s apart, and
3402
3643
  * background jobs in flight are capped by subscription tier (Tier 1=8,
@@ -3427,6 +3668,10 @@ var PixelLabProvider = class _PixelLabProvider {
3427
3668
  if (spec.revision?.mode === "reduce-colors" || spec.revision?.mode === "correct-pixelart") {
3428
3669
  return { unit: "generations", amount: 0.1, candidates: 1 };
3429
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
+ }
3430
3675
  if (spec.revision?.mode === "interpolate" || spec.revision?.mode === "edit-animation") {
3431
3676
  const width = spec.revision.sourceWidth ?? spec.width;
3432
3677
  const height = spec.revision.sourceHeight ?? spec.height;
@@ -3440,6 +3685,13 @@ var PixelLabProvider = class _PixelLabProvider {
3440
3685
  const base = Math.max(1, Math.ceil(width * height * frames / 65536));
3441
3686
  return { unit: "generations", amount: spec.revision.enhancePrompt ? base + 0.05 : base, candidates: 1 };
3442
3687
  }
3688
+ if (spec.revision?.engine === "pro-flash") {
3689
+ return {
3690
+ unit: "generations",
3691
+ amount: proFlashImageCost(spec.revision.sourceWidth ?? spec.width, spec.revision.sourceHeight ?? spec.height),
3692
+ candidates: 1
3693
+ };
3694
+ }
3443
3695
  if (spec.revision) {
3444
3696
  const width = spec.revision.sourceWidth ?? spec.width;
3445
3697
  const height = spec.revision.sourceHeight ?? spec.height;
@@ -3454,6 +3706,9 @@ var PixelLabProvider = class _PixelLabProvider {
3454
3706
  if (spec.generator === "objectPro") {
3455
3707
  return { unit: "generations", amount: objectProCost(spec), candidates: 1 };
3456
3708
  }
3709
+ if (spec.generator === "imageProFlash") {
3710
+ return { unit: "generations", amount: proFlashImageCost(spec.width, spec.height), candidates: 1 };
3711
+ }
3457
3712
  return {
3458
3713
  unit: "generations",
3459
3714
  amount: generationCost(spec.width, spec.height, spec.generator),
@@ -3467,7 +3722,17 @@ var PixelLabProvider = class _PixelLabProvider {
3467
3722
  `PixelLab ${spec.revision.mode} reads one source image; ${spec.revision.sourceAssetId} is a ${members.length}-member set (${members[0].role}, ...). Revise one member through a declared source, or use reduce-colors, correct-pixelart, or edit-animation, which take the whole set`
3468
3723
  );
3469
3724
  }
3470
- if (spec.revision?.mode === "inpaint") {
3725
+ if (spec.revision?.engine === "pro-flash") {
3726
+ const { sourceWidth: width, sourceHeight: height, mode: mode2 } = spec.revision;
3727
+ if (width != null && height != null && (width < 32 || height < 32 || width > 256 || height > 256 || width % 4 || height % 4)) {
3728
+ throw new Error(
3729
+ `PixelLab pro-flash ${mode2} source is ${width}x${height}; the API takes 32 to 256 pixels per side, in multiples of 4`
3730
+ );
3731
+ }
3732
+ if (spec.revision.strength != null) {
3733
+ throw new Error(`PixelLab pro-flash ${mode2} takes no strength; the edit applies in full`);
3734
+ }
3735
+ } else if (spec.revision?.mode === "inpaint") {
3471
3736
  const { sourceWidth: width, sourceHeight: height } = spec.revision;
3472
3737
  if (width != null && height != null && (width < 32 || height < 32 || width > 512 || height > 512)) {
3473
3738
  throw new Error(`PixelLab inpaint source is ${width}x${height}; the API takes 32 to 512 pixels per side`);
@@ -3488,6 +3753,18 @@ var PixelLabProvider = class _PixelLabProvider {
3488
3753
  throw new Error(`PixelLab correct-pixelart source is ${width}x${height}; the API takes at most 1024 pixels per side`);
3489
3754
  }
3490
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
+ }
3491
3768
  if (spec.revision?.mode === "animate" || spec.revision?.mode === "animate-pixminimax") {
3492
3769
  const { sourceWidth: width, sourceHeight: height, mode: mode2, frames, lastFrameWidth, lastFrameHeight } = spec.revision;
3493
3770
  if (width != null && height != null && (width > 256 || height > 256)) {
@@ -3548,6 +3825,19 @@ var PixelLabProvider = class _PixelLabProvider {
3548
3825
  `PixelLab uiAsset is ${spec.width}x${spec.height}; the API takes 192 to 688 pixels per side (the exact ceiling also depends on aspect ratio \u2014 square tops out at 512, 16:9 at 688x384, 9:16 at 384x688, 4:3 at 600x448, 3:4 at 448x600)`
3549
3826
  );
3550
3827
  }
3828
+ if (spec.generator === "imageProFlash") {
3829
+ if (spec.width < 16 || spec.height < 16 || spec.width > 256 || spec.height > 256 || spec.width % 4 || spec.height % 4) {
3830
+ throw new Error(
3831
+ `PixelLab imageProFlash is ${spec.width}x${spec.height}; the API takes 16 to 256 pixels per side, in multiples of 4`
3832
+ );
3833
+ }
3834
+ if (styleImages.length > 1) {
3835
+ throw new Error("PixelLab imageProFlash takes one style image; list at most one styleImages entry");
3836
+ }
3837
+ if (spec.styleTraits && !styleImages.length) {
3838
+ throw new Error("PixelLab imageProFlash: styleTraits choose what a style image lends; add a styleImages entry or drop them");
3839
+ }
3840
+ }
3551
3841
  if (spec.generator === "uiElement" && (spec.width < 16 || spec.height < 16 || spec.width > 792 || spec.height > 688)) {
3552
3842
  throw new Error(
3553
3843
  `PixelLab uiElement is ${spec.width}x${spec.height}; the API takes 16 to 792 wide and 16 to 688 tall (the exact ceiling depends on aspect ratio \u2014 square tops out at 512, 16:9 at 688x384)`
@@ -3901,7 +4191,8 @@ var PixelLabProvider = class _PixelLabProvider {
3901
4191
  concept: character.concept ? readPose(spec, "concept image", character.concept) : void 0,
3902
4192
  styleCharacterId: character.styleAnchor ? requireStyleObjectId(spec, context) : void 0,
3903
4193
  styleReferenceSize: styleImage ? { width: styleImage.width, height: styleImage.height } : void 0,
3904
- styleTraits: character.styleTraits
4194
+ styleTraits: character.styleTraits,
4195
+ sourceImageId: character.mode === "pro-flash" ? context?.referenceSourceImageId : void 0
3905
4196
  });
3906
4197
  return { jobId: res2.character_id, metadata: { character: { kind: "base", characterId: res2.character_id, mode: character.mode, directions: character.directions, backgroundJobId: res2.background_job_id } } };
3907
4198
  }
@@ -4036,7 +4327,8 @@ var PixelLabProvider = class _PixelLabProvider {
4036
4327
  reference: object.reference ? readPose(spec, "reference sprite", object.reference) : void 0,
4037
4328
  styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
4038
4329
  styleReferenceSize: styleImage ? { width: styleImage.width, height: styleImage.height } : void 0,
4039
- styleTraits: object.styleTraits
4330
+ styleTraits: object.styleTraits,
4331
+ sourceImageId: context?.referenceSourceImageId
4040
4332
  });
4041
4333
  return { jobId: res2.object_id, metadata: { objectPro: { kind: "base", objectId: res2.object_id, directions: object.directions, backgroundJobId: res2.background_job_id } } };
4042
4334
  }
@@ -4335,6 +4627,19 @@ var PixelLabProvider = class _PixelLabProvider {
4335
4627
  });
4336
4628
  return { jobId: res2.ui_asset_id, metadata: { backgroundJobId: res2.background_job_id } };
4337
4629
  }
4630
+ if (spec.generator === "imageProFlash") {
4631
+ const style = styleImages[0];
4632
+ const res2 = await this.client.createImageProFlash({
4633
+ description: spec.prompt,
4634
+ width: spec.width,
4635
+ height: spec.height,
4636
+ noBackground: spec.noBackground,
4637
+ seed: spec.seed,
4638
+ styleImage: style ? { image: { base64: style.base64, format: style.format }, width: style.width, height: style.height } : void 0,
4639
+ styleTraits: spec.styleTraits
4640
+ });
4641
+ return { jobId: res2.background_job_id, metadata: { imageProFlash: proFlashRecord(res2) } };
4642
+ }
4338
4643
  if (spec.generator === "uiElement") {
4339
4644
  const concept = styleImages[0];
4340
4645
  const res2 = await this.client.generateUiV2({
@@ -4488,6 +4793,32 @@ var PixelLabProvider = class _PixelLabProvider {
4488
4793
  const width = revision.sourceWidth;
4489
4794
  const height = revision.sourceHeight;
4490
4795
  const roles = revision.sourceMembers?.map((member) => member.role);
4796
+ if (revision.engine === "pro-flash") {
4797
+ if (revision.mode === "inpaint") {
4798
+ if (!revision.maskFile || !revision.maskSha256 || !revision.maskFormat) {
4799
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision mask is not ready`);
4800
+ }
4801
+ const maskBytes = readFileSync(revision.maskFile);
4802
+ if (sha256(maskBytes) !== revision.maskSha256) {
4803
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision mask changed after the manifest was resolved`);
4804
+ }
4805
+ const res3 = await this.client.inpaintImageProFlash({
4806
+ image,
4807
+ maskImage: { base64: maskBytes.toString("base64"), format: revision.maskFormat },
4808
+ description: spec.prompt,
4809
+ noBackground: spec.noBackground,
4810
+ seed: spec.seed
4811
+ });
4812
+ return { jobId: res3.background_job_id, metadata: { imageProFlash: proFlashRecord(res3) } };
4813
+ }
4814
+ const res2 = await this.client.editImageProFlash({
4815
+ image,
4816
+ description: spec.prompt,
4817
+ noBackground: spec.noBackground,
4818
+ seed: spec.seed
4819
+ });
4820
+ return { jobId: res2.background_job_id, metadata: { imageProFlash: proFlashRecord(res2) } };
4821
+ }
4491
4822
  if (revision.mode === "inpaint") {
4492
4823
  if (!revision.maskFile || !revision.maskSha256 || !revision.maskFormat) {
4493
4824
  throw new Error(`${spec.styleId}/${spec.assetId}: revision mask is not ready`);
@@ -4583,6 +4914,27 @@ var PixelLabProvider = class _PixelLabProvider {
4583
4914
  });
4584
4915
  return { jobId: res2.background_job_id };
4585
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
+ }
4586
4938
  if (revision.strength != null) {
4587
4939
  throw new Error(
4588
4940
  `${spec.styleId}/${spec.assetId}: PixelLab image-to-image revisions take no strength; edit-images-v2 always applies the full instruction`
@@ -4648,10 +5000,11 @@ var PixelLabProvider = class _PixelLabProvider {
4648
5000
  if (revisionMode === "reduce-colors" || revisionMode === "correct-pixelart") {
4649
5001
  return this.pollCachedRevision(jobId, context);
4650
5002
  }
4651
- 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") {
4652
5004
  return this.pollAnimateRevision(jobId, context);
4653
5005
  }
4654
5006
  if (context?.spec?.revision) return this.pollRevision(jobId);
5007
+ if (generator === "imageProFlash") return this.pollRevision(jobId);
4655
5008
  if (generator === "pixflux") {
4656
5009
  const file = path.join(_PixelLabProvider.cacheDir(), `${jobId}.png`);
4657
5010
  if (existsSync(file)) {
@@ -5232,6 +5585,13 @@ function readPose(spec, what, image) {
5232
5585
  }
5233
5586
  return { base64: bytes.toString("base64"), format: image.format };
5234
5587
  }
5588
+ function proFlashRecord(res) {
5589
+ return {
5590
+ imageId: res.image_id,
5591
+ sourceImageId: res.source_image_id,
5592
+ ...res.estimated_generations != null ? { estimatedGenerations: res.estimated_generations } : {}
5593
+ };
5594
+ }
5235
5595
  function editAnimationFrameCeiling(width, height) {
5236
5596
  const side = Math.max(width, height);
5237
5597
  if (side <= 64) return 16;
@@ -6375,11 +6735,12 @@ var ComfyUIProvider = class _ComfyUIProvider {
6375
6735
  /**
6376
6736
  * Everything else resolves generically to whatever the user's own
6377
6737
  * workflow does with `bindings.sourceImage`. The frame-set modes
6378
- * (`animate`, `animate-pixminimax`, `interpolate`, `edit-animation`) are
6379
- * the exception: they produce an ordered frame set, and this adapter's
6380
- * revision path always writes a single output image (see `submit`/`fetch`
6381
- * below) — a real structural gap, not a missing binding, so they are
6382
- * 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.
6383
6744
  */
6384
6745
  supportsRevision(mode2) {
6385
6746
  return !FRAME_SET_REVISION_MODES.includes(mode2);
@@ -6435,6 +6796,11 @@ var ComfyUIProvider = class _ComfyUIProvider {
6435
6796
  throw new Error("ComfyUI providerOptions.frames requires generator: frames");
6436
6797
  }
6437
6798
  if (spec.revision) {
6799
+ if (spec.revision.engine) {
6800
+ throw new Error(
6801
+ `ComfyUI has no "${spec.revision.engine}" revision engine; that selects PixelLab's Pro Flash edit/inpaint endpoints. Drop engine to run the revision through this style's own workflow.`
6802
+ );
6803
+ }
6438
6804
  if (spec.revision.sourceMembers) {
6439
6805
  throw new Error(
6440
6806
  `ComfyUI revisions read one source image; ${spec.revision.sourceAssetId} is a ${spec.revision.sourceMembers.length}-member set (directions or frames)`
@@ -8032,6 +8398,7 @@ async function resolveSpecs(loaded, filter) {
8032
8398
  uiPieces: generator === "uiAsset" ? asset.pieces : void 0,
8033
8399
  uiElements: generator === "uiAsset" ? asset.elements : void 0,
8034
8400
  uiColorPalette: generator === "uiAsset" || generator === "uiElement" ? style.uiColorPalette : void 0,
8401
+ ...generator === "imageProFlash" && style.styleTraits ? { styleTraits: style.styleTraits } : {},
8035
8402
  palette: style.palette,
8036
8403
  enforcePalette: style.enforcePalette,
8037
8404
  noBackground: style.noBackground,
@@ -8276,6 +8643,8 @@ async function resolveSpecs(loaded, filter) {
8276
8643
  const paletteImage = paletteImageFile ? await optionalRevisionImage(paletteImageFile, "revision palette image", "png") : null;
8277
8644
  const lastFrameFile = asset.revision.lastFrame ? path5.resolve(root, asset.revision.lastFrame) : void 0;
8278
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;
8279
8648
  resolved.revision = {
8280
8649
  mode: asset.revision.mode,
8281
8650
  sourceAssetId: asset.revision.from,
@@ -8315,7 +8684,20 @@ async function resolveSpecs(loaded, filter) {
8315
8684
  lastFrameFormat: lastFrame?.format ?? null
8316
8685
  } : {},
8317
8686
  ...asset.revision.direction ? { direction: asset.revision.direction } : {},
8318
- ...asset.revision.enhancePrompt == null ? {} : { enhancePrompt: asset.revision.enhancePrompt }
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 } : {},
8700
+ ...asset.revision.engine ? { engine: asset.revision.engine } : {}
8319
8701
  };
8320
8702
  }
8321
8703
  if (asset.batch) {
@@ -8436,6 +8818,17 @@ async function optionalRevisionImage(file, label, requiredFormat) {
8436
8818
  }
8437
8819
  return { hash: sha256(bytes), ...metadata };
8438
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
+ }
8439
8832
  async function revisionSourceMembers(stem, label) {
8440
8833
  if (existsSync4(stem)) return null;
8441
8834
  const at = (role) => memberPath(stem, role, 0, 2, MediaType.PNG);
@@ -10558,47 +10951,47 @@ import path14 from "path";
10558
10951
  import { existsSync as existsSync9 } from "fs";
10559
10952
  import { readdir, readFile as readFile7 } from "fs/promises";
10560
10953
  import { fileURLToPath } from "url";
10561
- import { z as z3 } from "zod";
10954
+ import { z as z4 } from "zod";
10562
10955
  var SHA256_RE = /^[0-9a-f]{64}$/;
10563
10956
  var RECIPE_ID_RE = /^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/;
10564
10957
  var VERSION_RE = /^\d+\.\d+\.\d+$/;
10565
10958
  var RECIPE_FILE = "pixelkiln.recipe.json";
10566
10959
  var RECIPE_DIR_TOKEN = "{{recipeDir}}";
10567
- var PortablePathSchema = z3.string().min(1).refine((value) => {
10960
+ var PortablePathSchema = z4.string().min(1).refine((value) => {
10568
10961
  if (value.includes("\\") || path14.posix.isAbsolute(value)) return false;
10569
10962
  const parts = value.split("/");
10570
10963
  return !parts.includes("") && !parts.includes(".") && !parts.includes("..");
10571
10964
  }, "expected a portable relative path without '.', '..', or backslashes");
10572
- var RecipeBindingSchema = z3.object({
10573
- nodeId: z3.string().min(1),
10574
- input: z3.string().min(1)
10965
+ var RecipeBindingSchema = z4.object({
10966
+ nodeId: z4.string().min(1),
10967
+ input: z4.string().min(1)
10575
10968
  }).strict();
10576
- var RecipeSchema = z3.object({
10577
- $schema: z3.string().url().optional(),
10578
- format: z3.literal("pixelkiln-recipe"),
10579
- schemaVersion: z3.literal(1),
10580
- id: z3.string().regex(RECIPE_ID_RE, "expected provider/name"),
10581
- version: z3.string().regex(VERSION_RE, "expected x.y.z"),
10582
- provider: z3.string().regex(/^[a-z0-9][a-z0-9-]*$/),
10583
- summary: z3.string().min(1).max(240),
10584
- 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({
10585
10978
  path: PortablePathSchema,
10586
- role: z3.enum(["workflow", "reference"]),
10587
- sha256: z3.string().regex(SHA256_RE)
10979
+ role: z4.enum(["workflow", "reference"]),
10980
+ sha256: z4.string().regex(SHA256_RE)
10588
10981
  }).strict()).default([]),
10589
- models: z3.array(z3.object({
10982
+ models: z4.array(z4.object({
10590
10983
  path: PortablePathSchema,
10591
- sha256: z3.string().regex(SHA256_RE),
10592
- source: z3.string().url(),
10593
- license: z3.string().min(1)
10984
+ sha256: z4.string().regex(SHA256_RE),
10985
+ source: z4.string().url(),
10986
+ license: z4.string().min(1)
10594
10987
  }).strict()).default([]),
10595
- styleId: z3.string().regex(/^[a-z0-9][a-z0-9-]*$/),
10988
+ styleId: z4.string().regex(/^[a-z0-9][a-z0-9-]*$/),
10596
10989
  style: StyleSchema,
10597
- workflow: z3.object({
10990
+ workflow: z4.object({
10598
10991
  path: PortablePathSchema,
10599
- outputNodeId: z3.string().min(1),
10600
- numImages: z3.number().int().min(1).max(16),
10601
- bindings: z3.object({
10992
+ outputNodeId: z4.string().min(1),
10993
+ numImages: z4.number().int().min(1).max(16),
10994
+ bindings: z4.object({
10602
10995
  prompt: RecipeBindingSchema,
10603
10996
  width: RecipeBindingSchema.optional(),
10604
10997
  height: RecipeBindingSchema.optional(),
@@ -10609,67 +11002,67 @@ var RecipeSchema = z3.object({
10609
11002
  strength: RecipeBindingSchema.optional()
10610
11003
  }).strict()
10611
11004
  }).strict().optional(),
10612
- quality: z3.object({
10613
- stage: z3.enum(["composition-source", "production-candidate"]),
10614
- workingCanvas: z3.object({
10615
- width: z3.number().int().min(16),
10616
- 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)
10617
11010
  }).strict(),
10618
- recommendedNativeSize: z3.object({
10619
- min: z3.number().int().min(1),
10620
- max: z3.number().int().min(1)
11011
+ recommendedNativeSize: z4.object({
11012
+ min: z4.number().int().min(1),
11013
+ max: z4.number().int().min(1)
10621
11014
  }).strict(),
10622
- paletteColors: z3.object({
10623
- min: z3.number().int().min(1),
10624
- max: z3.number().int().min(1)
11015
+ paletteColors: z4.object({
11016
+ min: z4.number().int().min(1),
11017
+ max: z4.number().int().min(1)
10625
11018
  }).strict(),
10626
- background: z3.enum(["opaque", "transparent", "full-bleed"]),
10627
- checks: z3.array(z3.enum([
11019
+ background: z4.enum(["opaque", "transparent", "full-bleed"]),
11020
+ checks: z4.array(z4.enum([
10628
11021
  "prompt-coverage",
10629
11022
  "native-grid",
10630
11023
  "final-palette",
10631
11024
  "alpha-edge",
10632
11025
  "human-1x"
10633
11026
  ])).min(1),
10634
- notes: z3.array(z3.string().min(1)).default([])
11027
+ notes: z4.array(z4.string().min(1)).default([])
10635
11028
  }).strict(),
10636
- integrity: z3.object({
10637
- algorithm: z3.literal("sha256"),
10638
- digest: z3.string().regex(SHA256_RE)
11029
+ integrity: z4.object({
11030
+ algorithm: z4.literal("sha256"),
11031
+ digest: z4.string().regex(SHA256_RE)
10639
11032
  }).strict()
10640
11033
  }).strict().superRefine((recipe, context) => {
10641
11034
  if (recipe.style.provider && recipe.style.provider !== recipe.provider) {
10642
11035
  context.addIssue({
10643
- code: z3.ZodIssueCode.custom,
11036
+ code: z4.ZodIssueCode.custom,
10644
11037
  path: ["style", "provider"],
10645
11038
  message: "must match the recipe provider"
10646
11039
  });
10647
11040
  }
10648
11041
  const filePaths = recipe.files.map((file) => file.path);
10649
11042
  if (new Set(filePaths).size !== filePaths.length) {
10650
- 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" });
10651
11044
  }
10652
11045
  const modelPaths = recipe.models.map((model) => model.path);
10653
11046
  if (new Set(modelPaths).size !== modelPaths.length) {
10654
- 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" });
10655
11048
  }
10656
11049
  if (filePaths.includes(RECIPE_FILE)) {
10657
11050
  context.addIssue({
10658
- code: z3.ZodIssueCode.custom,
11051
+ code: z4.ZodIssueCode.custom,
10659
11052
  path: ["files"],
10660
11053
  message: `${RECIPE_FILE} cannot hash itself`
10661
11054
  });
10662
11055
  }
10663
11056
  if (recipe.quality.recommendedNativeSize.min > recipe.quality.recommendedNativeSize.max) {
10664
11057
  context.addIssue({
10665
- code: z3.ZodIssueCode.custom,
11058
+ code: z4.ZodIssueCode.custom,
10666
11059
  path: ["quality", "recommendedNativeSize"],
10667
11060
  message: "min cannot exceed max"
10668
11061
  });
10669
11062
  }
10670
11063
  if (recipe.quality.paletteColors.min > recipe.quality.paletteColors.max) {
10671
11064
  context.addIssue({
10672
- code: z3.ZodIssueCode.custom,
11065
+ code: z4.ZodIssueCode.custom,
10673
11066
  path: ["quality", "paletteColors"],
10674
11067
  message: "min cannot exceed max"
10675
11068
  });
@@ -10678,7 +11071,7 @@ var RecipeSchema = z3.object({
10678
11071
  (file) => file.path === recipe.workflow.path && file.role === "workflow"
10679
11072
  )) {
10680
11073
  context.addIssue({
10681
- code: z3.ZodIssueCode.custom,
11074
+ code: z4.ZodIssueCode.custom,
10682
11075
  path: ["workflow", "path"],
10683
11076
  message: "must name a file whose role is workflow"
10684
11077
  });
@@ -10686,7 +11079,7 @@ var RecipeSchema = z3.object({
10686
11079
  if (recipe.provider === "comfyui") {
10687
11080
  if (!recipe.workflow) {
10688
11081
  context.addIssue({
10689
- code: z3.ZodIssueCode.custom,
11082
+ code: z4.ZodIssueCode.custom,
10690
11083
  path: ["workflow"],
10691
11084
  message: "ComfyUI recipes require workflow metadata"
10692
11085
  });
@@ -10695,7 +11088,7 @@ var RecipeSchema = z3.object({
10695
11088
  const options = recipe.style.providerOptions.comfyui;
10696
11089
  if (!options || typeof options !== "object" || Array.isArray(options)) {
10697
11090
  context.addIssue({
10698
- code: z3.ZodIssueCode.custom,
11091
+ code: z4.ZodIssueCode.custom,
10699
11092
  path: ["style", "providerOptions", "comfyui"],
10700
11093
  message: "ComfyUI recipes require ComfyUI provider options"
10701
11094
  });
@@ -10709,7 +11102,7 @@ var RecipeSchema = z3.object({
10709
11102
  };
10710
11103
  if (JSON.stringify(canonical3(options)) !== JSON.stringify(canonical3(expected))) {
10711
11104
  context.addIssue({
10712
- code: z3.ZodIssueCode.custom,
11105
+ code: z4.ZodIssueCode.custom,
10713
11106
  path: ["style", "providerOptions", "comfyui"],
10714
11107
  message: "must match workflow metadata and use {{recipeDir}} for workflowFile"
10715
11108
  });
@@ -11147,11 +11540,11 @@ async function generateFont(client2, opts) {
11147
11540
  import path17 from "path";
11148
11541
  import { existsSync as existsSync12 } from "fs";
11149
11542
  import { readFile as readFile10 } from "fs/promises";
11150
- import { z as z4 } from "zod";
11543
+ import { z as z5 } from "zod";
11151
11544
  var SHA256_RE2 = /^[0-9a-f]{64}$/;
11152
11545
  var HEX_RE = /^#[0-9a-f]{6}$/;
11153
11546
  var MAX_REDMEAN_DISTANCE = 765;
11154
- var QualityPathSchema = z4.string().min(1).refine(
11547
+ var QualityPathSchema = z5.string().min(1).refine(
11155
11548
  (value) => !path17.posix.isAbsolute(value) && !value.includes("\\"),
11156
11549
  "expected a portable relative path"
11157
11550
  );
@@ -11165,53 +11558,53 @@ var DEFAULT_QUALITY_TOLERANCES = {
11165
11558
  maxEdgeContrastDrop: 0.03,
11166
11559
  maxIsolatedPixelIncrease: 5e-3
11167
11560
  };
11168
- var QualityToleranceSchema = z4.object({
11169
- requireExactHash: z4.boolean(),
11170
- maxColorCountIncrease: z4.number().int().min(0),
11171
- maxNewColors: z4.number().int().min(0),
11172
- maxTransparencyDelta: z4.number().min(0).max(1),
11173
- maxPartialAlphaIncrease: z4.number().min(0).max(1),
11174
- maxEdgeDensityDelta: z4.number().min(0).max(1),
11175
- maxEdgeContrastDrop: z4.number().min(0).max(1),
11176
- 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)
11177
11570
  }).strict();
11178
- var ImageQualityMetricsSchema = z4.object({
11179
- sha256: z4.string().regex(SHA256_RE2),
11180
- width: z4.number().int().min(1),
11181
- height: z4.number().int().min(1),
11182
- colorCount: z4.number().int().min(0),
11183
- colors: z4.array(z4.string().regex(HEX_RE)),
11184
- transparency: z4.number().min(0).max(1),
11185
- partialAlpha: z4.number().min(0).max(1),
11186
- edgeDensity: z4.number().min(0).max(1),
11187
- meanEdgeContrast: z4.number().min(0).max(1),
11188
- 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)
11189
11582
  }).strict();
11190
- var QualityBaselineCaseSchema = z4.object({
11191
- 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/_-]*$/),
11192
11585
  file: QualityPathSchema,
11193
- record: z4.object({
11586
+ record: z5.object({
11194
11587
  path: QualityPathSchema,
11195
- sha256: z4.string().regex(SHA256_RE2)
11588
+ sha256: z5.string().regex(SHA256_RE2)
11196
11589
  }).strict().optional(),
11197
11590
  expected: ImageQualityMetricsSchema,
11198
11591
  tolerances: QualityToleranceSchema
11199
11592
  }).strict();
11200
- var QualityBaselineSchema = z4.object({
11201
- $schema: z4.string().url().optional(),
11202
- format: z4.literal("pixelkiln-quality-baseline"),
11203
- schemaVersion: z4.literal(1),
11204
- 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)
11205
11598
  }).strict().superRefine((baseline, context) => {
11206
11599
  const ids = baseline.cases.map((entry) => entry.id);
11207
11600
  if (new Set(ids).size !== ids.length) {
11208
- 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" });
11209
11602
  }
11210
11603
  });
11211
- var QualityInputSchema = z4.object({
11212
- id: z4.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
11213
- path: z4.string().min(1),
11214
- 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(),
11215
11608
  tolerances: QualityToleranceSchema.partial().optional()
11216
11609
  }).strict();
11217
11610
  function message4(error) {
@@ -11313,7 +11706,7 @@ async function measureImageQuality(file) {
11313
11706
  }
11314
11707
  }
11315
11708
  function resolveQualityInputs(raw, inputsFilePath) {
11316
- const parsed = z4.array(QualityInputSchema).min(1).safeParse(raw);
11709
+ const parsed = z5.array(QualityInputSchema).min(1).safeParse(raw);
11317
11710
  if (!parsed.success) {
11318
11711
  throw new Error(
11319
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")
@@ -11536,11 +11929,11 @@ import path20 from "path";
11536
11929
  import { mkdir as mkdir5, readFile as readFile11, rename as rename4, rm as rm4, writeFile as writeFile5 } from "fs/promises";
11537
11930
  import { existsSync as existsSync13 } from "fs";
11538
11931
  import pathModule from "path";
11539
- import { z as z5 } from "zod";
11540
- var HashCacheSchema = z5.object({
11541
- version: z5.literal(1),
11932
+ import { z as z6 } from "zod";
11933
+ var HashCacheSchema = z6.object({
11934
+ version: z6.literal(1),
11542
11935
  /** objectId → sha256 of that object's image bytes. */
11543
- hashes: z5.record(z5.string())
11936
+ hashes: z6.record(z6.string())
11544
11937
  });
11545
11938
  var SHA256_HEX = /^[a-f0-9]{64}$/;
11546
11939
  function isSha256Hash(value) {
@@ -13087,7 +13480,11 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
13087
13480
  ...spec.revision.fps == null ? {} : { fps: spec.revision.fps },
13088
13481
  ...spec.revision.lastFrameSha256 ? { lastFrameSha256: spec.revision.lastFrameSha256 } : {},
13089
13482
  ...spec.revision.direction ? { direction: spec.revision.direction } : {},
13090
- ...spec.revision.enhancePrompt == null ? {} : { enhancePrompt: spec.revision.enhancePrompt }
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 } : {},
13487
+ ...spec.revision.engine ? { engine: spec.revision.engine } : {}
13091
13488
  } : null,
13092
13489
  outfit: spec.character?.kind === "outfit" && outfitParentEntry ? {
13093
13490
  sourceAssetId: spec.character.parentAssetId,
@@ -13131,8 +13528,10 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
13131
13528
  path: currentEntryOutputPath(parentEntry, spec.character.parentSpec, index),
13132
13529
  role: output.role
13133
13530
  })) : void 0;
13531
+ const referenceSourceImageId = ownedReferenceImageId(spec, lock, provider.id);
13134
13532
  const { jobId, metadata } = await provider.submit(spec, refs, {
13135
13533
  ...previousJobId ? { previousJobId } : {},
13534
+ ...referenceSourceImageId ? { referenceSourceImageId } : {},
13136
13535
  ...parentEntry?.objectId ? { parentObjectId: parentEntry.objectId } : {},
13137
13536
  ...anchorEntry?.objectId ? { styleObjectId: anchorEntry.objectId } : {},
13138
13537
  ...parentOutputs ? { parentOutputs } : {},
@@ -13199,6 +13598,17 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
13199
13598
  }
13200
13599
  return { submitted, failed, spent, unit };
13201
13600
  }
13601
+ function ownedReferenceImageId(spec, lock, providerId) {
13602
+ const sha2562 = spec.character?.kind === "base" && spec.character.mode === "pro-flash" ? spec.character.reference?.south?.sha256 : spec.objectPro?.kind === "base" ? spec.objectPro.reference?.sha256 : void 0;
13603
+ if (!sha2562) return void 0;
13604
+ for (const entry of Object.values(lock.entries)) {
13605
+ if (entry.provider !== providerId || entry.generator !== "imageProFlash" || entry.status !== "downloaded") continue;
13606
+ if (entry.outputs.length !== 1 || entry.outputs[0].sha256 !== sha2562) continue;
13607
+ const recorded = entry.providerMetadata?.[providerId]?.imageProFlash;
13608
+ if (typeof recorded?.sourceImageId === "string" && recorded.sourceImageId) return recorded.sourceImageId;
13609
+ }
13610
+ return void 0;
13611
+ }
13202
13612
 
13203
13613
  // src/pipeline/poll.ts
13204
13614
  var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -14371,20 +14781,20 @@ import path29 from "path";
14371
14781
  import { mkdir as mkdir7, readFile as readFile18, rename as rename6, rm as rm7, writeFile as writeFile9 } from "fs/promises";
14372
14782
  import { existsSync as existsSync21 } from "fs";
14373
14783
  import path25 from "path";
14374
- import { z as z6 } from "zod";
14375
- var WorkspaceProjectSchema = z6.object({
14376
- id: z6.string().min(1),
14784
+ import { z as z7 } from "zod";
14785
+ var WorkspaceProjectSchema = z7.object({
14786
+ id: z7.string().min(1),
14377
14787
  /** Manifest path, relative to the catalog file's own directory. */
14378
- manifest: z6.string().min(1),
14788
+ manifest: z7.string().min(1),
14379
14789
  /** Lockfile path, relative to the catalog file's own directory. */
14380
- lock: z6.string().min(1),
14381
- provider: z6.string().min(1).default("pixellab"),
14790
+ lock: z7.string().min(1),
14791
+ provider: z7.string().min(1).default("pixellab"),
14382
14792
  /** Free-form label for a shared account, e.g. distinguishing sandboxes. */
14383
- account: z6.string().optional()
14793
+ account: z7.string().optional()
14384
14794
  }).strict();
14385
- var WorkspaceSchema = z6.object({
14386
- version: z6.literal(1),
14387
- projects: z6.array(WorkspaceProjectSchema).default([])
14795
+ var WorkspaceSchema = z7.object({
14796
+ version: z7.literal(1),
14797
+ projects: z7.array(WorkspaceProjectSchema).default([])
14388
14798
  }).strict();
14389
14799
  function parseWorkspace(raw) {
14390
14800
  const parsed = WorkspaceSchema.safeParse(raw);
@@ -14502,62 +14912,62 @@ function validateWorkspace(ws, dir) {
14502
14912
 
14503
14913
  // src/gallery/edit.ts
14504
14914
  import path28 from "path";
14505
- import { z as z9 } from "zod";
14915
+ import { z as z10 } from "zod";
14506
14916
 
14507
14917
  // src/manifest-edit.ts
14508
14918
  import { readFile as readFile19, rename as rename7, rm as rm8, writeFile as writeFile10 } from "fs/promises";
14509
14919
  import path26 from "path";
14510
- import { z as z7 } from "zod";
14511
- var HexSha = z7.string().regex(/^[0-9a-f]{64}$/);
14512
- var AssetPatchSchema = z7.object({
14513
- 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(),
14514
14924
  /** Per-style override for the style being viewed; null removes it. */
14515
- promptForStyle: z7.object({ styleId: z7.string().min(1), prompt: z7.string().nullable() }).optional(),
14516
- category: z7.string().nullable().optional(),
14517
- width: z7.number().int().nullable().optional(),
14518
- height: z7.number().int().nullable().optional(),
14519
- size: z7.number().int().nullable().optional(),
14520
- 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()
14521
14931
  }).strict();
14522
- var NewStateSchema = z7.object({
14932
+ var NewStateSchema = z8.object({
14523
14933
  /** Asset id of the parent this states; must already exist. */
14524
- of: z7.string().min(1),
14525
- paletteFromReference: z7.boolean().optional(),
14526
- 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()
14527
14937
  }).strict();
14528
- var NewAnimationSchema = z7.object({
14938
+ var NewAnimationSchema = z8.object({
14529
14939
  /** Asset id of the parent this animates; must already exist. */
14530
- of: z7.string().min(1),
14940
+ of: z8.string().min(1),
14531
14941
  /** A PixelLab template id (`character` only; `objectPro` has no template concept). */
14532
- template: z7.string().min(1).optional(),
14942
+ template: z8.string().min(1).optional(),
14533
14943
  direction: CharacterDirectionSchema.optional(),
14534
- frames: z7.number().int().min(4).max(16).optional(),
14535
- 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(),
14536
14946
  mode: CharacterAnimationModeSchema.optional(),
14537
14947
  /** What is being animated, when the parent's own description would mislead the model (`character` only). */
14538
- subject: z7.string().min(1).optional(),
14539
- enhancePrompt: z7.boolean().optional()
14948
+ subject: z8.string().min(1).optional(),
14949
+ enhancePrompt: z8.boolean().optional()
14540
14950
  }).strict();
14541
- var NewAssetSchema = z7.object({
14542
- prompt: z7.string(),
14543
- category: z7.string().optional(),
14544
- width: z7.number().int().optional(),
14545
- height: z7.number().int().optional(),
14546
- size: z7.number().int().optional(),
14547
- 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(),
14548
14958
  /** Restrict to these styles; empty or omitted means every style. */
14549
- styles: z7.array(z7.string().min(1)).optional(),
14959
+ styles: z8.array(z8.string().min(1)).optional(),
14550
14960
  /**
14551
14961
  * A controlled regeneration of another asset in the same manifest,
14552
14962
  * instead of a fresh generation. `inpaint` needs a mask upload the
14553
14963
  * gallery does not offer yet, so only `image-to-image` can be created
14554
14964
  * here; hand-edit the manifest for the other modes.
14555
14965
  */
14556
- revision: z7.object({
14557
- mode: z7.literal("image-to-image"),
14966
+ revision: z8.object({
14967
+ mode: z8.literal("image-to-image"),
14558
14968
  /** Asset id of the parent this revises; must already exist. */
14559
- from: z7.string().min(1),
14560
- strength: z7.number().min(0).max(1).optional()
14969
+ from: z8.string().min(1),
14970
+ strength: z8.number().min(0).max(1).optional()
14561
14971
  }).strict().optional(),
14562
14972
  /** A pose or outfit of an existing `character`/`objectPro` base or state. */
14563
14973
  state: NewStateSchema.optional(),
@@ -14573,58 +14983,58 @@ var CANDIDATE_OPTION = {
14573
14983
  comfyui: "numImages",
14574
14984
  scenario: "numOutputs"
14575
14985
  };
14576
- var ManifestEditSchema = z7.discriminatedUnion("action", [
14577
- z7.object({
14578
- action: z7.literal("patch-asset"),
14986
+ var ManifestEditSchema = z8.discriminatedUnion("action", [
14987
+ z8.object({
14988
+ action: z8.literal("patch-asset"),
14579
14989
  /** Workspace project id; omitted for a single-project gallery. */
14580
- project: z7.string().min(1).optional(),
14581
- assetId: z7.string().min(1),
14990
+ project: z8.string().min(1).optional(),
14991
+ assetId: z8.string().min(1),
14582
14992
  /** Manifest bytes the page last saw; a mismatch refuses the write. */
14583
14993
  expectedSha256: HexSha,
14584
14994
  patch: AssetPatchSchema
14585
14995
  }).strict(),
14586
- z7.object({
14587
- action: z7.literal("add-asset"),
14588
- project: z7.string().min(1).optional(),
14589
- 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"),
14590
15000
  expectedSha256: HexSha,
14591
15001
  asset: NewAssetSchema
14592
15002
  }).strict(),
14593
- z7.object({
14594
- action: z7.literal("set-source"),
14595
- project: z7.string().min(1).optional(),
14596
- assetId: z7.string().min(1),
14597
- 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),
14598
15008
  /** Manifest-relative path of the art that stands in for this asset in this style. */
14599
- source: z7.string().min(1),
15009
+ source: z8.string().min(1),
14600
15010
  expectedSha256: HexSha
14601
15011
  }).strict(),
14602
- z7.object({
14603
- action: z7.literal("clear-source"),
14604
- project: z7.string().min(1).optional(),
14605
- assetId: z7.string().min(1),
14606
- 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),
14607
15017
  expectedSha256: HexSha
14608
15018
  }).strict(),
14609
- z7.object({
14610
- action: z7.literal("patch-style"),
14611
- project: z7.string().min(1).optional(),
14612
- 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),
14613
15023
  expectedSha256: HexSha,
14614
- patch: z7.object({
15024
+ patch: z8.object({
14615
15025
  /** Candidates per generation, written to the provider's own option. */
14616
- candidates: z7.number().int().min(1).max(64).optional(),
15026
+ candidates: z8.number().int().min(1).max(64).optional(),
14617
15027
  /** Empty clears the style's own value (inherit, or the default). */
14618
- promptPrefix: z7.string().optional(),
14619
- promptSuffix: z7.string().optional(),
15028
+ promptPrefix: z8.string().optional(),
15029
+ promptSuffix: z8.string().optional(),
14620
15030
  /** `#rrggbb` values; null or empty clears the style's own palette. */
14621
- 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(),
14622
15032
  /** Snap downloaded art to the palette; false clears the style's own value. */
14623
- enforcePalette: z7.boolean().optional(),
15033
+ enforcePalette: z8.boolean().optional(),
14624
15034
  /** Provider view name (PixelLab: low top-down, high top-down, side); empty clears. */
14625
- view: z7.string().max(64).nullable().optional(),
15035
+ view: z8.string().max(64).nullable().optional(),
14626
15036
  /** pixflux only; null clears the style's own value. */
14627
- noBackground: z7.boolean().nullable().optional()
15037
+ noBackground: z8.boolean().nullable().optional()
14628
15038
  }).strict().refine((patch) => Object.keys(patch).length > 0, { message: "nothing to change" })
14629
15039
  }).strict()
14630
15040
  ]);
@@ -14818,7 +15228,7 @@ async function applyManifestEdit(manifestPath, edit) {
14818
15228
  import { existsSync as existsSync22 } from "fs";
14819
15229
  import { copyFile, mkdir as mkdir8, readFile as readFile20, rename as rename8, rm as rm9, writeFile as writeFile11 } from "fs/promises";
14820
15230
  import path27 from "path";
14821
- import { z as z8 } from "zod";
15231
+ import { z as z9 } from "zod";
14822
15232
  var HAND_EDIT_DIR = "edits";
14823
15233
  function handEditPath(loaded, spec) {
14824
15234
  const style = loaded.manifest.styles[spec.styleId];
@@ -14915,35 +15325,35 @@ async function detachHandEdit(loaded, spec, opts = {}) {
14915
15325
  });
14916
15326
  return { manifestSha256: result.sha256, changed: result.changed };
14917
15327
  }
14918
- var CompanionOutputSchema = z8.object({
15328
+ var CompanionOutputSchema = z9.object({
14919
15329
  /** Output role for a set member; null for a single image. */
14920
- role: z8.string().nullable(),
15330
+ role: z9.string().nullable(),
14921
15331
  /** sha256 of the generated file this member started from; null when it started from untracked art. */
14922
- basedOn: z8.string().regex(/^[0-9a-f]{64}$/).nullable(),
15332
+ basedOn: z9.string().regex(/^[0-9a-f]{64}$/).nullable(),
14923
15333
  /** sha256 of the member as saved, so a later change by another tool is visible. */
14924
- sha256: z8.string().regex(/^[0-9a-f]{64}$/)
15334
+ sha256: z9.string().regex(/^[0-9a-f]{64}$/)
14925
15335
  }).strict();
14926
- var CompanionV2Schema = z8.object({
14927
- version: z8.literal(2),
15336
+ var CompanionV2Schema = z9.object({
15337
+ version: z9.literal(2),
14928
15338
  /** Editor identity the bridge reported, e.g. `pixelorama@v1.2.2-stable`. */
14929
- editor: z8.string().min(1),
14930
- protocol: z8.number().int().positive(),
14931
- savedAt: z8.string().datetime(),
15339
+ editor: z9.string().min(1),
15340
+ protocol: z9.number().int().positive(),
15341
+ savedAt: z9.string().datetime(),
14932
15342
  /** Basename of the layered project file beside the edit, when one was kept. */
14933
- project: z8.string().min(1).nullable(),
15343
+ project: z9.string().min(1).nullable(),
14934
15344
  /** One entry per file, in output order. */
14935
- outputs: z8.array(CompanionOutputSchema).min(1)
15345
+ outputs: z9.array(CompanionOutputSchema).min(1)
14936
15346
  }).strict();
14937
- var CompanionV1Schema = z8.object({
14938
- version: z8.literal(1),
14939
- basedOn: z8.string().regex(/^[0-9a-f]{64}$/).nullable(),
14940
- sha256: z8.string().regex(/^[0-9a-f]{64}$/),
14941
- editor: z8.string().min(1),
14942
- protocol: z8.number().int().positive(),
14943
- savedAt: z8.string().datetime(),
14944
- 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()
14945
15355
  }).strict();
14946
- var HandEditCompanionSchema = z8.union([CompanionV2Schema, CompanionV1Schema]).transform(
15356
+ var HandEditCompanionSchema = z9.union([CompanionV2Schema, CompanionV1Schema]).transform(
14947
15357
  (value) => value.version === 2 ? value : {
14948
15358
  version: 2,
14949
15359
  editor: value.editor,
@@ -15063,40 +15473,40 @@ function openInEditor(file, editor = process.env.PIXELKILN_EDITOR) {
15063
15473
  }
15064
15474
 
15065
15475
  // src/gallery/edit.ts
15066
- var Base64 = z9.string().regex(/^[A-Za-z0-9+/]*={0,2}$/).max(Math.ceil(MAX_HAND_EDIT_BYTES / 3) * 4);
15067
- var HandEditRequestSchema = z9.discriminatedUnion("action", [
15068
- z9.object({
15069
- action: z9.literal("start-edit"),
15070
- project: z9.string().min(1).optional(),
15071
- assetId: z9.string().min(1),
15072
- styleId: z9.string().min(1),
15073
- 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}$/),
15074
15484
  /** Also launch the author's editor on the file. */
15075
- open: z9.boolean().optional()
15485
+ open: z10.boolean().optional()
15076
15486
  }).strict(),
15077
- z9.object({
15078
- action: z9.literal("detach-edit"),
15079
- project: z9.string().min(1).optional(),
15080
- assetId: z9.string().min(1),
15081
- styleId: z9.string().min(1),
15082
- 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}$/)
15083
15493
  }).strict(),
15084
15494
  /** From the in-browser editor: the page holds the bytes, the server validates and writes. */
15085
- z9.object({
15086
- action: z9.literal("save-edit"),
15087
- project: z9.string().min(1).optional(),
15088
- assetId: z9.string().min(1),
15089
- styleId: z9.string().min(1),
15090
- 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}$/),
15091
15501
  /** The flattened image; for a frame set, `frames` instead. */
15092
15502
  png: Base64.min(1).optional(),
15093
15503
  /** Every frame of a set, tagged with the role it was opened under (null for one added in the editor). */
15094
- 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(),
15095
15505
  /** The editor's layered project file, kept beside the edit. */
15096
15506
  pxo: Base64.optional(),
15097
15507
  /** Editor identity from the bridge's ready message. */
15098
- editor: z9.string().min(1).max(200),
15099
- protocol: z9.number().int().positive()
15508
+ editor: z10.string().min(1).max(200),
15509
+ protocol: z10.number().int().positive()
15100
15510
  }).strict()
15101
15511
  ]);
15102
15512
  function createGalleryEditHandler(opts) {
@@ -16430,27 +16840,27 @@ async function serveGallery(opts) {
16430
16840
 
16431
16841
  // src/gallery/generate.ts
16432
16842
  import { randomBytes as randomBytes2 } from "crypto";
16433
- import { z as z10 } from "zod";
16434
- var GenerateRequestSchema = z10.object({
16435
- project: z10.string().min(1).optional(),
16436
- 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),
16437
16847
  /**
16438
16848
  * Regenerate work that is up to date, as `gen --force` does, and let the
16439
16849
  * fetch replace a locally modified or untracked file. With `restore`, put
16440
16850
  * the recorded bytes back over a modified file.
16441
16851
  */
16442
- force: z10.boolean().optional(),
16852
+ force: z11.boolean().optional(),
16443
16853
  /** Advance in-flight, review, or selected work without submitting anything. */
16444
- resume: z10.boolean().optional(),
16854
+ resume: z11.boolean().optional(),
16445
16855
  /** Re-download downloaded outputs and replace files whose object changed upstream. */
16446
- refresh: z10.boolean().optional(),
16856
+ refresh: z11.boolean().optional(),
16447
16857
  /** Put recorded outputs back on disk from the cache or the provider, without generating. */
16448
- restore: z10.boolean().optional(),
16858
+ restore: z11.boolean().optional(),
16449
16859
  /**
16450
16860
  * Bring a replaced generation back: 1-based position in the record's
16451
16861
  * history (newest first) or an output hash prefix. One key at a time.
16452
16862
  */
16453
- revert: z10.string().min(1).max(64).optional()
16863
+ revert: z11.string().min(1).max(64).optional()
16454
16864
  }).strict().refine((request) => [request.resume, request.refresh, request.restore, request.revert !== void 0].filter(Boolean).length <= 1, {
16455
16865
  message: "resume, refresh, restore, and revert are exclusive"
16456
16866
  }).refine((request) => request.revert === void 0 || request.keys.length === 1, { message: "revert takes one key" });