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/cli.js CHANGED
@@ -68,7 +68,7 @@ function exitCodeFor(err) {
68
68
 
69
69
  // src/cli/main.ts
70
70
  import { existsSync as existsSync33 } from "fs";
71
- import { readFile as readFile30 } from "fs/promises";
71
+ import { readFile as readFile31 } from "fs/promises";
72
72
 
73
73
  // src/cli/args.ts
74
74
  import path from "path";
@@ -157,6 +157,7 @@ var COMMANDS = [
157
157
  "refine",
158
158
  "recipe",
159
159
  "workspace",
160
+ "estimate-skeleton",
160
161
  "unzoom",
161
162
  "font",
162
163
  "help",
@@ -251,6 +252,10 @@ function parseArgs(argv) {
251
252
  }
252
253
  rest = rest.slice(1);
253
254
  }
255
+ } else if (command === "estimate-skeleton") {
256
+ target = rest[0]?.startsWith("-") ? void 0 : rest[0];
257
+ if (target === void 0) throw new UsageError("estimate-skeleton needs an image path");
258
+ rest = rest.slice(1);
254
259
  } else if (command === "refine") {
255
260
  subcommand = rest[0]?.startsWith("-") || rest[0] === void 0 ? "run" : rest[0];
256
261
  if (!REFINE_SUBCOMMANDS.includes(subcommand)) {
@@ -456,6 +461,11 @@ Commands
456
461
  first. Local art is untouched.
457
462
  tag Push manifest tags to the objects upstream (free).
458
463
  balance Show the provider's remaining balance.
464
+ estimate-skeleton <image> [--out <file>]
465
+ PixelLab only: derive an 18-joint skeleton from an image, direct
466
+ and un-budgeted (like balance, not a generation). Prints the
467
+ keypoints JSON, or writes it with --out \u2014 the way to bootstrap
468
+ an animate-skeleton revision's keypointsFile.
459
469
  status Summarise the lockfile.
460
470
  edit Hand-edit an asset in your own editor: copies the generated PNG to
461
471
  <outDir>/edits/, declares it as the asset's source, and opens it.
@@ -638,10 +648,10 @@ import { createReadStream } from "fs";
638
648
  function sha256(data) {
639
649
  return createHash("sha256").update(data).digest("hex");
640
650
  }
641
- async function sha256File(path47) {
651
+ async function sha256File(path48) {
642
652
  return new Promise((resolve, reject) => {
643
653
  const hash = createHash("sha256");
644
- const stream = createReadStream(path47);
654
+ const stream = createReadStream(path48);
645
655
  stream.on("data", (chunk2) => hash.update(chunk2));
646
656
  stream.on("error", reject);
647
657
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -679,6 +689,7 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
679
689
  uiPieces: spec.uiPieces,
680
690
  uiElements: spec.uiElements,
681
691
  uiColorPalette: spec.uiColorPalette,
692
+ styleTraits: spec.styleTraits,
682
693
  styleImages: styleImageHashes,
683
694
  // A character family: the engine and rotations of a base, the parent's
684
695
  // generated bytes for a state or animation (so a regenerated parent
@@ -755,7 +766,11 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
755
766
  fps: spec.revision.fps,
756
767
  lastFrameSha256: spec.revision.lastFrameSha256,
757
768
  direction: spec.revision.direction,
758
- enhancePrompt: spec.revision.enhancePrompt
769
+ enhancePrompt: spec.revision.enhancePrompt,
770
+ keypointsSha256: spec.revision.keypointsSha256,
771
+ skeletonTemplate: spec.revision.skeletonTemplate,
772
+ description: spec.revision.description,
773
+ engine: spec.revision.engine
759
774
  } : void 0
760
775
  })
761
776
  );
@@ -773,7 +788,7 @@ import path2 from "path";
773
788
  // src/types.ts
774
789
  import { z } from "zod";
775
790
  var MediaTypeSchema = z.enum(["image/png", "image/gif"]);
776
- var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile", "objectPro", "uiAsset", "uiElement"]);
791
+ var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile", "objectPro", "uiAsset", "uiElement", "imageProFlash"]);
777
792
  var GridConfidenceSchema = z.enum(["low", "medium", "high"]);
778
793
  var RevisionModeSchema = z.enum([
779
794
  "image-to-image",
@@ -783,6 +798,7 @@ var RevisionModeSchema = z.enum([
783
798
  "correct-pixelart",
784
799
  "animate",
785
800
  "animate-pixminimax",
801
+ "animate-skeleton",
786
802
  "interpolate",
787
803
  "edit-animation"
788
804
  ]);
@@ -1100,8 +1116,46 @@ var RevisionSchema = z.object({
1100
1116
  /** `animate-pixminimax` only: facing direction, used only alongside `enhancePrompt` to hold the sprite's facing. */
1101
1117
  direction: CharacterDirectionSchema.optional(),
1102
1118
  /** `animate`/`animate-pixminimax` only: let PixelLab expand the action into a fuller motion description first. */
1103
- enhancePrompt: z.boolean().optional()
1119
+ enhancePrompt: z.boolean().optional(),
1120
+ /**
1121
+ * `animate-skeleton` only: manifest-relative path to a committed
1122
+ * `SkeletonSet` JSON file (see `src/skeleton.ts`) — the pose the parent
1123
+ * image is already in, plus 3-15 per-frame poses for the motion.
1124
+ * Authored via `pixelkiln estimate-skeleton`, then hand-tweaked; never
1125
+ * inline in the manifest, the same reasoning `mask`/`lastFrame` are
1126
+ * committed files rather than inline pixel data.
1127
+ */
1128
+ keypointsFile: z.string().min(1).optional(),
1129
+ /**
1130
+ * `animate-skeleton` only: the body a joint's missing `depth` is taken
1131
+ * from. PixelLab's default is `mannequin`; free string like `character`'s
1132
+ * own `template` field, validated at the provider layer against the
1133
+ * same `mannequin`/`bear`/`cat`/`dog`/`horse`/`lion` vocabulary.
1134
+ */
1135
+ skeletonTemplate: z.string().min(1).optional(),
1136
+ /**
1137
+ * `animate-skeleton` only: what the character *looks like*, a noun
1138
+ * phrase (colours, clothing, held items) — distinct from the asset's
1139
+ * own `prompt`, which this mode sends as the motion's short label
1140
+ * (PixelLab's `action`), the same `prompt`-as-motion convention
1141
+ * `animate`/`animate-pixminimax` already use.
1142
+ */
1143
+ description: z.string().min(1).optional(),
1144
+ /**
1145
+ * `image-to-image`/`inpaint` only: which provider engine draws the
1146
+ * edit. `pro-flash` selects PixelLab's Pro Flash edit and inpaint tier
1147
+ * (`/edit-image-pro-flash`, `/inpaint-image-pro-flash`) instead of the
1148
+ * default Pro endpoints; omit it for the default.
1149
+ */
1150
+ engine: z.enum(["pro-flash"]).optional()
1104
1151
  }).strict().superRefine((revision, context) => {
1152
+ if (revision.engine !== void 0 && revision.mode !== "image-to-image" && revision.mode !== "inpaint") {
1153
+ context.addIssue({
1154
+ code: z.ZodIssueCode.custom,
1155
+ message: "engine applies to image-to-image and inpaint revisions only",
1156
+ path: ["engine"]
1157
+ });
1158
+ }
1105
1159
  if (revision.mode === "inpaint" && !revision.mask) {
1106
1160
  context.addIssue({
1107
1161
  code: z.ZodIssueCode.custom,
@@ -1177,18 +1231,41 @@ var RevisionSchema = z.object({
1177
1231
  path: ["fps"]
1178
1232
  });
1179
1233
  }
1180
- if (revision.direction !== void 0 && revision.mode !== "animate-pixminimax") {
1234
+ if (revision.direction !== void 0 && revision.mode !== "animate-pixminimax" && revision.mode !== "animate-skeleton") {
1235
+ context.addIssue({
1236
+ code: z.ZodIssueCode.custom,
1237
+ message: "direction applies to animate-pixminimax/animate-skeleton revisions only",
1238
+ path: ["direction"]
1239
+ });
1240
+ }
1241
+ if (revision.mode === "animate-skeleton" && revision.direction === void 0) {
1181
1242
  context.addIssue({
1182
1243
  code: z.ZodIssueCode.custom,
1183
- message: "direction applies to animate-pixminimax revisions only",
1244
+ message: "animate-skeleton revisions require a direction",
1184
1245
  path: ["direction"]
1185
1246
  });
1186
1247
  }
1187
1248
  if (revision.frames !== void 0 && revision.frames % 2 !== 0) {
1188
1249
  context.addIssue({ code: z.ZodIssueCode.custom, message: "frames must be even", path: ["frames"] });
1189
1250
  }
1251
+ if (revision.mode === "animate-skeleton" && !revision.keypointsFile) {
1252
+ context.addIssue({
1253
+ code: z.ZodIssueCode.custom,
1254
+ message: "animate-skeleton revisions require keypointsFile",
1255
+ path: ["keypointsFile"]
1256
+ });
1257
+ }
1258
+ for (const field of ["keypointsFile", "skeletonTemplate", "description"]) {
1259
+ if (revision[field] !== void 0 && revision.mode !== "animate-skeleton") {
1260
+ context.addIssue({
1261
+ code: z.ZodIssueCode.custom,
1262
+ message: `${field} applies to animate-skeleton revisions only`,
1263
+ path: [field]
1264
+ });
1265
+ }
1266
+ }
1190
1267
  });
1191
- var FRAME_SET_REVISION_MODES = ["animate", "animate-pixminimax", "interpolate", "edit-animation"];
1268
+ var FRAME_SET_REVISION_MODES = ["animate", "animate-pixminimax", "animate-skeleton", "interpolate", "edit-animation"];
1192
1269
  var MEMBER_SET_REVISION_MODES = ["reduce-colors", "correct-pixelart", "edit-animation"];
1193
1270
  var StyleObjectSchema = z.object({
1194
1271
  /** Generation backend for this style. Omit to inherit the manifest default. */
@@ -1390,8 +1467,8 @@ var StyleObjectSchema = z.object({
1390
1467
  /** `character` only. Body template: `mannequin` (default), a quadruped (`bear`, `cat`, `dog`, `horse`, `lion`), or `custom` for `pro-flash`. */
1391
1468
  template: z.string().min(1).optional(),
1392
1469
  /**
1393
- * `character` only, `pro-flash` bases with a style image: which traits
1394
- * the style image lends. Each defaults to true.
1470
+ * `character` `pro-flash` bases and `imageProFlash` styles, with a
1471
+ * style image: which traits the style image lends. Each defaults to true.
1395
1472
  */
1396
1473
  styleTraits: z.object({
1397
1474
  palette: z.boolean().optional(),
@@ -1820,11 +1897,17 @@ var LockEntrySchema = z.object({
1820
1897
  paletteImageSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1821
1898
  dithering: RevisionDitheringSchema.optional(),
1822
1899
  ditheringStrength: z.number().min(0).max(10).optional(),
1823
- frames: z.number().int().min(4).max(40).optional(),
1900
+ // 4-40 for animate/animate-pixminimax; animate-skeleton's own 3-15
1901
+ // range extends the floor down to 3, hence min(3) here rather than 4.
1902
+ frames: z.number().int().min(3).max(40).optional(),
1824
1903
  fps: z.number().int().min(1).max(60).optional(),
1825
1904
  lastFrameSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1826
1905
  direction: CharacterDirectionSchema.optional(),
1827
- enhancePrompt: z.boolean().optional()
1906
+ enhancePrompt: z.boolean().optional(),
1907
+ keypointsSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1908
+ skeletonTemplate: z.string().min(1).optional(),
1909
+ description: z.string().min(1).optional(),
1910
+ engine: z.enum(["pro-flash"]).optional()
1828
1911
  }).strict().nullable().default(null),
1829
1912
  /** For a mirror: the asset it flips and a hash over that asset's output hashes when this was made. */
1830
1913
  mirror: z.object({
@@ -4976,11 +5059,12 @@ var ComfyUIProvider = class _ComfyUIProvider {
4976
5059
  /**
4977
5060
  * Everything else resolves generically to whatever the user's own
4978
5061
  * workflow does with `bindings.sourceImage`. The frame-set modes
4979
- * (`animate`, `animate-pixminimax`, `interpolate`, `edit-animation`) are
4980
- * the exception: they produce an ordered frame set, and this adapter's
4981
- * revision path always writes a single output image (see `submit`/`fetch`
4982
- * below) — a real structural gap, not a missing binding, so they are
4983
- * rejected here rather than only failing once `validate` runs.
5062
+ * (`animate`, `animate-pixminimax`, `animate-skeleton`, `interpolate`,
5063
+ * `edit-animation`) are the exception: they produce an ordered frame set,
5064
+ * and this adapter's revision path always writes a single output image
5065
+ * (see `submit`/`fetch` below) — a real structural gap, not a missing
5066
+ * binding, so they are rejected here rather than only failing once
5067
+ * `validate` runs.
4984
5068
  */
4985
5069
  supportsRevision(mode2) {
4986
5070
  return !FRAME_SET_REVISION_MODES.includes(mode2);
@@ -5036,6 +5120,11 @@ var ComfyUIProvider = class _ComfyUIProvider {
5036
5120
  throw new Error("ComfyUI providerOptions.frames requires generator: frames");
5037
5121
  }
5038
5122
  if (spec.revision) {
5123
+ if (spec.revision.engine) {
5124
+ throw new Error(
5125
+ `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.`
5126
+ );
5127
+ }
5039
5128
  if (spec.revision.sourceMembers) {
5040
5129
  throw new Error(
5041
5130
  `ComfyUI revisions read one source image; ${spec.revision.sourceAssetId} is a ${spec.revision.sourceMembers.length}-member set (directions or frames)`
@@ -5635,254 +5724,291 @@ import os from "os";
5635
5724
  import path12 from "path";
5636
5725
 
5637
5726
  // src/client.ts
5727
+ import { z as z3 } from "zod";
5728
+
5729
+ // src/skeleton.ts
5638
5730
  import { z as z2 } from "zod";
5731
+ var SkeletonKeypointSchema = z2.object({
5732
+ label: z2.string().min(1),
5733
+ x: z2.number().min(0).max(1),
5734
+ y: z2.number().min(0).max(1),
5735
+ z_index: z2.number(),
5736
+ depth: z2.number().optional()
5737
+ }).strict();
5738
+ var SkeletonFrameSchema = z2.array(SkeletonKeypointSchema).length(18);
5739
+ var SkeletonSetSchema = z2.object({
5740
+ firstFrameKeypoints: SkeletonFrameSchema,
5741
+ frames: z2.array(SkeletonFrameSchema).min(3).max(15)
5742
+ }).strict();
5743
+ function parseSkeletonSet(json, file) {
5744
+ const result = SkeletonSetSchema.safeParse(json);
5745
+ if (!result.success) {
5746
+ const issues = result.error.issues.slice(0, 4).map((issue) => `${issue.path.join(".") || "root"}: ${issue.message}`).join("; ");
5747
+ throw new Error(`revision keypoints file ${file} does not match the expected shape: ${issues}`);
5748
+ }
5749
+ return result.data;
5750
+ }
5751
+
5752
+ // src/client.ts
5639
5753
  var BASE = process.env.PIXELLAB_API_BASE ?? "https://api.pixellab.ai/v2";
5640
5754
  var MAX_DOWNLOAD_BYTES = 64 * 1024 * 1024;
5641
- var BalanceResponseSchema = z2.object({
5642
- credits: z2.object({ usd: z2.number() }).passthrough(),
5643
- subscription: z2.object({
5644
- generations: z2.number(),
5645
- total: z2.number(),
5646
- plan: z2.string().nullable().optional()
5755
+ var BalanceResponseSchema = z3.object({
5756
+ credits: z3.object({ usd: z3.number() }).passthrough(),
5757
+ subscription: z3.object({
5758
+ generations: z3.number(),
5759
+ total: z3.number(),
5760
+ plan: z3.string().nullable().optional()
5647
5761
  }).passthrough()
5648
5762
  }).passthrough();
5649
- var ObjectSubmitSchema = z2.object({
5650
- background_job_id: z2.string().min(1),
5651
- object_id: z2.string().min(1),
5652
- status: z2.string().default("queued"),
5653
- n_frames: z2.number().int().min(0)
5763
+ var ObjectSubmitSchema = z3.object({
5764
+ background_job_id: z3.string().min(1),
5765
+ object_id: z3.string().min(1),
5766
+ status: z3.string().default("queued"),
5767
+ n_frames: z3.number().int().min(0)
5654
5768
  }).passthrough();
5655
- var MapSubmitSchema = z2.object({
5656
- background_job_id: z2.string().min(1),
5657
- object_id: z2.string().min(1),
5658
- status: z2.string().default("processing")
5769
+ var MapSubmitSchema = z3.object({
5770
+ background_job_id: z3.string().min(1),
5771
+ object_id: z3.string().min(1),
5772
+ status: z3.string().default("processing")
5659
5773
  }).passthrough();
5660
- var UsageSchema = z2.object({
5661
- type: z2.string().optional(),
5662
- usd: z2.number().nullable().optional(),
5663
- generations: z2.number().nullable().optional()
5774
+ var UsageSchema = z3.object({
5775
+ type: z3.string().optional(),
5776
+ usd: z3.number().nullable().optional(),
5777
+ generations: z3.number().nullable().optional()
5664
5778
  }).passthrough().nullable().optional();
5665
- var TilesSubmitSchema = z2.object({
5666
- tile_id: z2.string().min(1),
5667
- background_job_id: z2.string().min(1),
5668
- status: z2.literal("processing").default("processing")
5779
+ var TilesSubmitSchema = z3.object({
5780
+ tile_id: z3.string().min(1),
5781
+ background_job_id: z3.string().min(1),
5782
+ status: z3.literal("processing").default("processing")
5669
5783
  }).passthrough();
5670
- var TilesProSchema = z2.object({
5671
- storage_urls: z2.record(z2.string()),
5672
- kind: z2.string().nullable().default(null),
5673
- tile_rules: z2.record(z2.unknown()).nullable().optional()
5784
+ var TilesProSchema = z3.object({
5785
+ storage_urls: z3.record(z3.string()),
5786
+ kind: z3.string().nullable().default(null),
5787
+ tile_rules: z3.record(z3.unknown()).nullable().optional()
5674
5788
  }).passthrough();
5675
- var TilesetSubmitSchema = z2.object({
5676
- tileset_id: z2.string().min(1),
5677
- background_job_id: z2.string().min(1),
5678
- status: z2.literal("processing").default("processing")
5789
+ var TilesetSubmitSchema = z3.object({
5790
+ tileset_id: z3.string().min(1),
5791
+ background_job_id: z3.string().min(1),
5792
+ status: z3.literal("processing").default("processing")
5679
5793
  }).passthrough();
5680
- var TilesetTileSchema = z2.object({
5681
- id: z2.string().min(1),
5682
- name: z2.string(),
5683
- image: z2.object({ base64: z2.string().min(1), format: z2.string().default("png") }).passthrough(),
5684
- corners: z2.object({ NW: z2.string(), NE: z2.string(), SW: z2.string(), SE: z2.string() }).passthrough(),
5685
- pattern_4x4: z2.object({
5686
- row_0: z2.array(z2.number()),
5687
- row_1: z2.array(z2.number()),
5688
- row_2: z2.array(z2.number()),
5689
- row_3: z2.array(z2.number())
5794
+ var TilesetTileSchema = z3.object({
5795
+ id: z3.string().min(1),
5796
+ name: z3.string(),
5797
+ image: z3.object({ base64: z3.string().min(1), format: z3.string().default("png") }).passthrough(),
5798
+ corners: z3.object({ NW: z3.string(), NE: z3.string(), SW: z3.string(), SE: z3.string() }).passthrough(),
5799
+ pattern_4x4: z3.object({
5800
+ row_0: z3.array(z3.number()),
5801
+ row_1: z3.array(z3.number()),
5802
+ row_2: z3.array(z3.number()),
5803
+ row_3: z3.array(z3.number())
5690
5804
  }).passthrough()
5691
5805
  }).passthrough();
5692
- var TilesetGetSchema = z2.object({
5693
- tileset: z2.object({
5694
- total_tiles: z2.number().int().min(1),
5695
- tile_size: z2.object({ width: z2.number().int().positive(), height: z2.number().int().positive() }).passthrough(),
5696
- terrain_types: z2.array(z2.string()),
5697
- tiles: z2.array(TilesetTileSchema).min(1)
5806
+ var TilesetGetSchema = z3.object({
5807
+ tileset: z3.object({
5808
+ total_tiles: z3.number().int().min(1),
5809
+ tile_size: z3.object({ width: z3.number().int().positive(), height: z3.number().int().positive() }).passthrough(),
5810
+ terrain_types: z3.array(z3.string()),
5811
+ tiles: z3.array(TilesetTileSchema).min(1)
5698
5812
  }).passthrough(),
5699
5813
  usage: UsageSchema
5700
5814
  }).passthrough();
5701
- var IsometricTileSubmitSchema = z2.object({
5702
- tile_id: z2.string().min(1),
5703
- background_job_id: z2.string().min(1),
5704
- status: z2.literal("processing").default("processing")
5815
+ var IsometricTileSubmitSchema = z3.object({
5816
+ tile_id: z3.string().min(1),
5817
+ background_job_id: z3.string().min(1),
5818
+ status: z3.literal("processing").default("processing")
5705
5819
  }).passthrough();
5706
- var IsometricTileGetSchema = z2.object({
5707
- image: z2.object({ base64: z2.string().min(1), format: z2.string().default("png") }).passthrough(),
5820
+ var IsometricTileGetSchema = z3.object({
5821
+ image: z3.object({ base64: z3.string().min(1), format: z3.string().default("png") }).passthrough(),
5708
5822
  usage: UsageSchema
5709
5823
  }).passthrough();
5710
- var UiAssetSubmitSchema = z2.object({
5711
- ui_asset_id: z2.string().min(1),
5712
- background_job_id: z2.string().min(1),
5713
- status: z2.string().default("processing"),
5824
+ var UiAssetSubmitSchema = z3.object({
5825
+ ui_asset_id: z3.string().min(1),
5826
+ background_job_id: z3.string().min(1),
5827
+ status: z3.string().default("processing"),
5714
5828
  usage: UsageSchema
5715
5829
  }).passthrough();
5716
- var UiAssetGetSchema = z2.object({
5717
- id: z2.string().min(1),
5718
- status: z2.string().nullable().default(null),
5719
- image_url: z2.string().nullable().optional(),
5720
- progress_percent: z2.number().nullable().optional(),
5721
- eta_seconds: z2.number().nullable().optional()
5830
+ var UiAssetGetSchema = z3.object({
5831
+ id: z3.string().min(1),
5832
+ status: z3.string().nullable().default(null),
5833
+ image_url: z3.string().nullable().optional(),
5834
+ progress_percent: z3.number().nullable().optional(),
5835
+ eta_seconds: z3.number().nullable().optional()
5722
5836
  }).passthrough();
5723
- var RevisionJobSubmitSchema = z2.object({
5724
- background_job_id: z2.string().min(1),
5725
- status: z2.string().default("processing")
5837
+ var RevisionJobSubmitSchema = z3.object({
5838
+ background_job_id: z3.string().min(1),
5839
+ status: z3.string().default("processing")
5726
5840
  }).passthrough();
5727
- var ObjectAnimationGroupSchema = z2.object({
5728
- animation_group_id: z2.string().min(1),
5729
- display_name: z2.string().nullable().default(null),
5730
- description: z2.string().default(""),
5731
- frame_count: z2.number().int().default(0),
5732
- directions: z2.array(
5733
- z2.object({
5734
- direction: z2.string(),
5735
- storage_urls: z2.record(z2.unknown()).default({}),
5736
- created_at: z2.string()
5841
+ var ObjectAnimationGroupSchema = z3.object({
5842
+ animation_group_id: z3.string().min(1),
5843
+ display_name: z3.string().nullable().default(null),
5844
+ description: z3.string().default(""),
5845
+ frame_count: z3.number().int().default(0),
5846
+ directions: z3.array(
5847
+ z3.object({
5848
+ direction: z3.string(),
5849
+ storage_urls: z3.record(z3.unknown()).default({}),
5850
+ created_at: z3.string()
5737
5851
  }).passthrough()
5738
5852
  ).default([])
5739
5853
  }).passthrough();
5740
- var PixelLabObjectSchema = z2.object({
5741
- id: z2.string().min(1),
5742
- name: z2.string().nullable().default(null),
5743
- state_name: z2.string().nullable().optional(),
5744
- prompt: z2.string().default(""),
5745
- size: z2.object({ width: z2.number().int().positive(), height: z2.number().int().positive() }),
5746
- directions: z2.number().default(0),
5747
- created_at: z2.string(),
5748
- view: z2.string().nullable().default(null),
5749
- preview_url: z2.string().nullable().optional(),
5750
- rotation_urls: z2.record(z2.string().nullable()).nullable().optional(),
5751
- frame_urls: z2.array(z2.string()).nullable().optional(),
5752
- tags: z2.array(z2.string()).default([]),
5753
- status: z2.string().nullable().default(null),
5754
- progress_percent: z2.number().nullable().optional(),
5755
- eta_seconds: z2.number().nullable().optional(),
5756
- group_id: z2.string().nullable().optional(),
5757
- animations: z2.array(ObjectAnimationGroupSchema).default([])
5854
+ var PixelLabObjectSchema = z3.object({
5855
+ id: z3.string().min(1),
5856
+ name: z3.string().nullable().default(null),
5857
+ state_name: z3.string().nullable().optional(),
5858
+ prompt: z3.string().default(""),
5859
+ size: z3.object({ width: z3.number().int().positive(), height: z3.number().int().positive() }),
5860
+ directions: z3.number().default(0),
5861
+ created_at: z3.string(),
5862
+ view: z3.string().nullable().default(null),
5863
+ preview_url: z3.string().nullable().optional(),
5864
+ rotation_urls: z3.record(z3.string().nullable()).nullable().optional(),
5865
+ frame_urls: z3.array(z3.string()).nullable().optional(),
5866
+ tags: z3.array(z3.string()).default([]),
5867
+ status: z3.string().nullable().default(null),
5868
+ progress_percent: z3.number().nullable().optional(),
5869
+ eta_seconds: z3.number().nullable().optional(),
5870
+ group_id: z3.string().nullable().optional(),
5871
+ animations: z3.array(ObjectAnimationGroupSchema).default([])
5758
5872
  }).passthrough();
5759
- var MapObjectSchema = z2.object({
5760
- object_id: z2.string().min(1),
5761
- status: z2.string(),
5762
- description: z2.string().nullable().default(null),
5763
- width: z2.number().nullable().default(null),
5764
- height: z2.number().nullable().default(null),
5765
- download_url: z2.string().nullable().default(null)
5873
+ var MapObjectSchema = z3.object({
5874
+ object_id: z3.string().min(1),
5875
+ status: z3.string(),
5876
+ description: z3.string().nullable().default(null),
5877
+ width: z3.number().nullable().default(null),
5878
+ height: z3.number().nullable().default(null),
5879
+ download_url: z3.string().nullable().default(null)
5766
5880
  }).passthrough();
5767
- var ObjectListSchema = z2.object({ objects: z2.array(PixelLabObjectSchema), total: z2.number().int().min(0) }).passthrough();
5768
- var PixfluxResponseSchema = z2.object({ image: z2.object({ base64: z2.string().min(1) }).passthrough(), usage: z2.unknown().optional() }).passthrough();
5769
- var ReduceColorsResponseSchema = z2.object({
5770
- images: z2.array(z2.object({ base64: z2.string().min(1) }).passthrough()).min(1),
5771
- palette: z2.object({ base64: z2.string().min(1) }).passthrough(),
5772
- n_colors: z2.number().int(),
5773
- usage: z2.unknown().optional()
5881
+ var ObjectListSchema = z3.object({ objects: z3.array(PixelLabObjectSchema), total: z3.number().int().min(0) }).passthrough();
5882
+ var PixfluxResponseSchema = z3.object({ image: z3.object({ base64: z3.string().min(1) }).passthrough(), usage: z3.unknown().optional() }).passthrough();
5883
+ var ReduceColorsResponseSchema = z3.object({
5884
+ images: z3.array(z3.object({ base64: z3.string().min(1) }).passthrough()).min(1),
5885
+ palette: z3.object({ base64: z3.string().min(1) }).passthrough(),
5886
+ n_colors: z3.number().int(),
5887
+ usage: z3.unknown().optional()
5774
5888
  }).passthrough();
5775
- var CorrectPixelartResponseSchema = z2.object({
5776
- images: z2.array(z2.object({ base64: z2.string().min(1) }).passthrough()).min(1),
5777
- usage: z2.unknown().optional()
5889
+ var CorrectPixelartResponseSchema = z3.object({
5890
+ images: z3.array(z3.object({ base64: z3.string().min(1) }).passthrough()).min(1),
5891
+ usage: z3.unknown().optional()
5778
5892
  }).passthrough();
5779
- var UnzoomResponseSchema = z2.object({
5780
- image: z2.object({ base64: z2.string().min(1) }).passthrough(),
5781
- original_size: z2.object({ width: z2.number().int(), height: z2.number().int() }).passthrough(),
5782
- unzoomed_size: z2.object({ width: z2.number().int(), height: z2.number().int() }).passthrough(),
5783
- zoom_factor_detected: z2.number(),
5784
- usage: z2.unknown().optional()
5893
+ var ProFlashImageSubmitSchema = z3.object({
5894
+ background_job_id: z3.string().min(1),
5895
+ image_id: z3.string().min(1),
5896
+ source_image_id: z3.string().min(1),
5897
+ status: z3.string().default("processing"),
5898
+ estimated_generations: z3.number().nullable().optional(),
5899
+ usage: z3.unknown().optional()
5785
5900
  }).passthrough();
5786
- var FontJobSchema = z2.object({
5787
- job_id: z2.string().min(1),
5788
- status: z2.string(),
5789
- glyph_px: z2.number().int().nullable().optional(),
5790
- download_atlas_url: z2.string().nullable().optional(),
5791
- download_ttf_url: z2.string().nullable().optional(),
5792
- usage: z2.unknown().optional()
5901
+ var UnzoomResponseSchema = z3.object({
5902
+ image: z3.object({ base64: z3.string().min(1) }).passthrough(),
5903
+ original_size: z3.object({ width: z3.number().int(), height: z3.number().int() }).passthrough(),
5904
+ unzoomed_size: z3.object({ width: z3.number().int(), height: z3.number().int() }).passthrough(),
5905
+ zoom_factor_detected: z3.number(),
5906
+ usage: z3.unknown().optional()
5793
5907
  }).passthrough();
5794
- var SelectFramesSchema = z2.object({ created_object_ids: z2.array(z2.string()) }).passthrough();
5795
- var CharacterSubmitSchema = z2.object({
5796
- background_job_id: z2.string().min(1),
5797
- character_id: z2.string().min(1),
5798
- status: z2.string().default("processing"),
5908
+ var FontJobSchema = z3.object({
5909
+ job_id: z3.string().min(1),
5910
+ status: z3.string(),
5911
+ glyph_px: z3.number().int().nullable().optional(),
5912
+ download_atlas_url: z3.string().nullable().optional(),
5913
+ download_ttf_url: z3.string().nullable().optional(),
5914
+ usage: z3.unknown().optional()
5915
+ }).passthrough();
5916
+ var SelectFramesSchema = z3.object({ created_object_ids: z3.array(z3.string()) }).passthrough();
5917
+ var EstimateSkeletonResponseSchema = z3.object({
5918
+ keypoints: z3.array(SkeletonKeypointSchema).length(18),
5919
+ usage: z3.unknown().optional()
5920
+ }).passthrough();
5921
+ var CharacterSubmitSchema = z3.object({
5922
+ background_job_id: z3.string().min(1),
5923
+ character_id: z3.string().min(1),
5924
+ status: z3.string().default("processing"),
5799
5925
  usage: UsageSchema
5800
5926
  }).passthrough();
5801
- var AnimateSubmitSchema = z2.object({
5802
- background_job_ids: z2.array(z2.string()),
5803
- directions: z2.array(z2.string()),
5804
- status: z2.string().default("processing"),
5927
+ var AnimateSubmitSchema = z3.object({
5928
+ background_job_ids: z3.array(z3.string()),
5929
+ directions: z3.array(z3.string()),
5930
+ status: z3.string().default("processing"),
5805
5931
  usage: UsageSchema
5806
5932
  }).passthrough();
5807
- var ObjectProSubmitSchema = z2.object({
5808
- object_id: z2.string().min(1),
5809
- background_job_id: z2.string().min(1),
5810
- status: z2.string().default("processing"),
5933
+ var ObjectProSubmitSchema = z3.object({
5934
+ object_id: z3.string().min(1),
5935
+ background_job_id: z3.string().min(1),
5936
+ status: z3.string().default("processing"),
5811
5937
  usage: UsageSchema
5812
5938
  }).passthrough();
5813
- var DirectionSubmissionSchema = z2.object({
5814
- direction: z2.string(),
5815
- status: z2.string(),
5816
- background_job_id: z2.string().nullable().default(null),
5817
- animation_id: z2.string().nullable().default(null)
5939
+ var DirectionSubmissionSchema = z3.object({
5940
+ direction: z3.string(),
5941
+ status: z3.string(),
5942
+ background_job_id: z3.string().nullable().default(null),
5943
+ animation_id: z3.string().nullable().default(null)
5818
5944
  }).passthrough();
5819
- var AnimateObjectSubmitSchema = z2.object({
5820
- animation_group_id: z2.string().min(1),
5821
- object_id: z2.string().min(1),
5822
- mode: z2.string(),
5823
- frame_count: z2.number().int(),
5824
- display_name: z2.string().nullable().default(null),
5825
- description: z2.string().default(""),
5826
- submissions: z2.array(DirectionSubmissionSchema),
5945
+ var AnimateObjectSubmitSchema = z3.object({
5946
+ animation_group_id: z3.string().min(1),
5947
+ object_id: z3.string().min(1),
5948
+ mode: z3.string(),
5949
+ frame_count: z3.number().int(),
5950
+ display_name: z3.string().nullable().default(null),
5951
+ description: z3.string().default(""),
5952
+ submissions: z3.array(DirectionSubmissionSchema),
5827
5953
  usage: UsageSchema
5828
5954
  }).passthrough();
5829
- var PortraitSubmitSchema = z2.object({
5830
- background_job_id: z2.string().min(1),
5831
- status: z2.string().default("processing"),
5955
+ var PortraitSubmitSchema = z3.object({
5956
+ background_job_id: z3.string().min(1),
5957
+ status: z3.string().default("processing"),
5832
5958
  usage: UsageSchema
5833
5959
  }).passthrough();
5834
- var PortraitJobSchema = z2.object({
5835
- status: z2.string(),
5836
- download_url: z2.string().nullable().optional(),
5837
- width: z2.number().int().nullable().optional(),
5838
- height: z2.number().int().nullable().optional()
5960
+ var PortraitJobSchema = z3.object({
5961
+ status: z3.string(),
5962
+ download_url: z3.string().nullable().optional(),
5963
+ width: z3.number().int().nullable().optional(),
5964
+ height: z3.number().int().nullable().optional()
5839
5965
  }).passthrough();
5840
- var SetPortraitSchema = z2.object({
5841
- character_id: z2.string().min(1),
5842
- size: z2.number().int().positive(),
5843
- url: z2.string().min(1),
5966
+ var SetPortraitSchema = z3.object({
5967
+ character_id: z3.string().min(1),
5968
+ size: z3.number().int().positive(),
5969
+ url: z3.string().min(1),
5844
5970
  usage: UsageSchema
5845
5971
  }).passthrough();
5846
- var TransferOutfitSubmitSchema = z2.object({
5847
- background_job_id: z2.string().min(1),
5848
- status: z2.string().default("processing"),
5972
+ var TransferOutfitSubmitSchema = z3.object({
5973
+ background_job_id: z3.string().min(1),
5974
+ status: z3.string().default("processing"),
5849
5975
  usage: UsageSchema
5850
5976
  }).passthrough();
5851
- var CharacterAnimationDirectionSchema = z2.object({
5852
- direction: z2.string(),
5853
- frame_count: z2.number().int().min(0),
5854
- frames: z2.array(z2.string())
5977
+ var CharacterAnimationDirectionSchema = z3.object({
5978
+ direction: z3.string(),
5979
+ frame_count: z3.number().int().min(0),
5980
+ frames: z3.array(z3.string())
5855
5981
  }).passthrough();
5856
- var CharacterAnimationSchema2 = z2.object({
5857
- animation_type: z2.string(),
5858
- display_name: z2.string().nullable().optional(),
5859
- animation_group_id: z2.string().nullable().optional(),
5860
- directions: z2.array(CharacterAnimationDirectionSchema)
5982
+ var CharacterAnimationSchema2 = z3.object({
5983
+ animation_type: z3.string(),
5984
+ display_name: z3.string().nullable().optional(),
5985
+ animation_group_id: z3.string().nullable().optional(),
5986
+ directions: z3.array(CharacterAnimationDirectionSchema)
5861
5987
  }).passthrough();
5862
- var CharacterSchema = z2.object({
5863
- id: z2.string().min(1),
5864
- name: z2.string().default(""),
5865
- state_name: z2.string().nullable().optional(),
5866
- prompt: z2.string().default(""),
5867
- size: z2.object({ width: z2.number().int().positive(), height: z2.number().int().positive() }),
5868
- directions: z2.number().int(),
5869
- created_at: z2.string(),
5870
- updated_at: z2.string().nullable().optional(),
5871
- animation_count: z2.number().int().default(0),
5872
- template_id: z2.string().default("mannequin"),
5873
- view: z2.string().nullable().optional(),
5874
- status: z2.string().default("completed"),
5875
- rotation_urls: z2.record(z2.string().nullable()).nullable().optional(),
5876
- tags: z2.array(z2.string()).default([]),
5877
- group_id: z2.string().nullable().optional(),
5878
- animations: z2.array(CharacterAnimationSchema2).default([]),
5879
- preview_url: z2.string().nullable().optional()
5988
+ var CharacterSchema = z3.object({
5989
+ id: z3.string().min(1),
5990
+ name: z3.string().default(""),
5991
+ state_name: z3.string().nullable().optional(),
5992
+ prompt: z3.string().default(""),
5993
+ size: z3.object({ width: z3.number().int().positive(), height: z3.number().int().positive() }),
5994
+ directions: z3.number().int(),
5995
+ created_at: z3.string(),
5996
+ updated_at: z3.string().nullable().optional(),
5997
+ animation_count: z3.number().int().default(0),
5998
+ template_id: z3.string().default("mannequin"),
5999
+ view: z3.string().nullable().optional(),
6000
+ status: z3.string().default("completed"),
6001
+ rotation_urls: z3.record(z3.string().nullable()).nullable().optional(),
6002
+ tags: z3.array(z3.string()).default([]),
6003
+ group_id: z3.string().nullable().optional(),
6004
+ animations: z3.array(CharacterAnimationSchema2).default([]),
6005
+ preview_url: z3.string().nullable().optional()
5880
6006
  }).passthrough();
5881
- var CharacterListSchema = z2.object({ characters: z2.array(CharacterSchema), total: z2.number().int().min(0) }).passthrough();
5882
- var BackgroundJobSchema = z2.object({
5883
- id: z2.string(),
5884
- status: z2.string(),
5885
- last_response: z2.record(z2.unknown()).nullable().optional(),
6007
+ var CharacterListSchema = z3.object({ characters: z3.array(CharacterSchema), total: z3.number().int().min(0) }).passthrough();
6008
+ var BackgroundJobSchema = z3.object({
6009
+ id: z3.string(),
6010
+ status: z3.string(),
6011
+ last_response: z3.record(z3.unknown()).nullable().optional(),
5886
6012
  /**
5887
6013
  * The billed amount, confirmed live for a `map` object and a standard
5888
6014
  * character base (docs/ENDPOINTS.md, "Limits and billing"); duplicated at
@@ -5890,7 +6016,7 @@ var BackgroundJobSchema = z2.object({
5890
6016
  */
5891
6017
  usage: UsageSchema
5892
6018
  }).passthrough();
5893
- var DeleteAnimationsSchema = z2.object({ success: z2.boolean(), deleted_count: z2.number().int().default(0), error: z2.string().nullable().optional() }).passthrough();
6019
+ var DeleteAnimationsSchema = z3.object({ success: z3.boolean(), deleted_count: z3.number().int().default(0), error: z3.string().nullable().optional() }).passthrough();
5894
6020
  function validateResponse(schema, raw, operation) {
5895
6021
  const parsed = schema.safeParse(raw);
5896
6022
  if (parsed.success) return parsed.data;
@@ -5936,9 +6062,9 @@ var PixelLabClient = class {
5936
6062
  apiKey;
5937
6063
  http;
5938
6064
  /** Retries transport failures, 429, and 5xx; see http.ts for the policy. */
5939
- async request(path47, init) {
6065
+ async request(path48, init) {
5940
6066
  const auth = this.apiKey.startsWith("Bearer ") ? this.apiKey : `Bearer ${this.apiKey}`;
5941
- const res = await this.http(`${BASE}${path47}`, {
6067
+ const res = await this.http(`${BASE}${path48}`, {
5942
6068
  ...init,
5943
6069
  headers: {
5944
6070
  Authorization: auth,
@@ -5948,13 +6074,13 @@ var PixelLabClient = class {
5948
6074
  });
5949
6075
  const text = await res.text();
5950
6076
  if (!res.ok) {
5951
- throw new PixelLabError(`${init?.method ?? "GET"} ${path47} \u2192 ${res.status}`, res.status, text);
6077
+ throw new PixelLabError(`${init?.method ?? "GET"} ${path48} \u2192 ${res.status}`, res.status, text);
5952
6078
  }
5953
6079
  if (!text) return {};
5954
6080
  try {
5955
6081
  return JSON.parse(text);
5956
6082
  } catch {
5957
- throw new Error(`${init?.method ?? "GET"} ${path47} returned invalid JSON`);
6083
+ throw new Error(`${init?.method ?? "GET"} ${path48} returned invalid JSON`);
5958
6084
  }
5959
6085
  }
5960
6086
  async balance() {
@@ -6216,7 +6342,7 @@ var PixelLabClient = class {
6216
6342
  const body = {
6217
6343
  description: args.description,
6218
6344
  n_directions: args.directions,
6219
- ...args.reference ? { first_frame: encode(args.reference) } : { image_size: { width: args.size, height: args.size } },
6345
+ ...args.sourceImageId ? { source_image_id: args.sourceImageId } : args.reference ? { first_frame: encode(args.reference) } : { image_size: { width: args.size, height: args.size } },
6220
6346
  ...args.view ? { view: args.view } : {},
6221
6347
  ...args.seed != null ? { seed: args.seed } : {},
6222
6348
  ...args.styleReference && args.styleReferenceSize ? {
@@ -6340,10 +6466,10 @@ var PixelLabClient = class {
6340
6466
  const palette = args.paletteSwatchBase64 ? { color_image: { type: "base64", base64: args.paletteSwatchBase64, format: "png" }, force_colors: true } : {};
6341
6467
  const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
6342
6468
  const south = args.reference?.south;
6343
- let path47;
6469
+ let path48;
6344
6470
  let body;
6345
6471
  if (args.mode === "v3") {
6346
- path47 = "/create-character-v3";
6472
+ path48 = "/create-character-v3";
6347
6473
  body = {
6348
6474
  description: args.description,
6349
6475
  image_size: imageSize,
@@ -6357,14 +6483,15 @@ var PixelLabClient = class {
6357
6483
  ...args.enhancePrompt ? { enhance_prompt: true } : {}
6358
6484
  };
6359
6485
  } else if (args.mode === "pro-flash") {
6360
- path47 = "/create-character-pro-flash";
6486
+ path48 = "/create-character-pro-flash";
6361
6487
  const traits = args.styleTraits;
6362
6488
  body = {
6363
6489
  description: args.description,
6364
6490
  template_id: args.template,
6365
6491
  n_directions: 8,
6366
- // The author's sprite sets the canvas; a size only applies to text creation.
6367
- ...south ? { first_frame: encode(south) } : { image_size: imageSize },
6492
+ // The author's sprite sets the canvas; a size only applies to text
6493
+ // creation. An owned copy of the same sprite is referenced by id.
6494
+ ...args.sourceImageId ? { source_image_id: args.sourceImageId } : south ? { first_frame: encode(south) } : { image_size: imageSize },
6368
6495
  ...args.view ? { view: args.view } : {},
6369
6496
  ...args.seed != null ? { seed: args.seed } : {},
6370
6497
  ...args.styleReference && args.styleReferenceSize ? {
@@ -6380,7 +6507,7 @@ var PixelLabClient = class {
6380
6507
  } : {}
6381
6508
  };
6382
6509
  } else if (args.mode === "pro") {
6383
- path47 = "/create-character-pro";
6510
+ path48 = "/create-character-pro";
6384
6511
  body = {
6385
6512
  description: args.description,
6386
6513
  image_size: imageSize,
@@ -6394,7 +6521,7 @@ var PixelLabClient = class {
6394
6521
  ...!south && args.styleCharacterId ? { style_character_id: args.styleCharacterId } : {}
6395
6522
  };
6396
6523
  } else {
6397
- path47 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
6524
+ path48 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
6398
6525
  body = {
6399
6526
  description: args.description,
6400
6527
  image_size: imageSize,
@@ -6411,8 +6538,8 @@ var PixelLabClient = class {
6411
6538
  ...palette
6412
6539
  };
6413
6540
  }
6414
- const raw = await this.request(path47, { method: "POST", body: JSON.stringify(body) });
6415
- return validateResponse(CharacterSubmitSchema, raw, path47.slice(1));
6541
+ const raw = await this.request(path48, { method: "POST", body: JSON.stringify(body) });
6542
+ return validateResponse(CharacterSubmitSchema, raw, path48.slice(1));
6416
6543
  }
6417
6544
  /** A text edit of an existing character, applied to every direction and stored as a sibling. */
6418
6545
  async createCharacterState(args) {
@@ -6636,6 +6763,66 @@ var PixelLabClient = class {
6636
6763
  "generate-image-v2"
6637
6764
  );
6638
6765
  }
6766
+ /**
6767
+ * `/create-image-pro-flash`: one native pixel-art image from a
6768
+ * description, optionally styled after a style image with per-trait
6769
+ * toggles, on the same Pro Flash model `character`/`objectPro` use for
6770
+ * their `pro-flash` engine. Sizes run 16 to 256 per side in multiples of 4.
6771
+ * A plain background job; `pollRevision`'s generic reader takes the
6772
+ * finished image. The response's `source_image_id` is what a Pro Flash
6773
+ * character or object can reuse instead of an upload.
6774
+ */
6775
+ async createImageProFlash(args) {
6776
+ const body = {
6777
+ description: args.description,
6778
+ image_size: { width: args.width, height: args.height }
6779
+ };
6780
+ if (args.noBackground != null) body.no_background = args.noBackground;
6781
+ if (args.seed != null) body.seed = args.seed;
6782
+ if (args.styleImage) {
6783
+ body.style_image = { image: args.styleImage.image, size: { width: args.styleImage.width, height: args.styleImage.height } };
6784
+ const traits = args.styleTraits;
6785
+ if (traits) {
6786
+ body.style_options = {
6787
+ ...traits.palette !== void 0 ? { color_palette: traits.palette } : {},
6788
+ ...traits.outline !== void 0 ? { outline: traits.outline } : {},
6789
+ ...traits.detail !== void 0 ? { detail: traits.detail } : {},
6790
+ ...traits.shading !== void 0 ? { shading: traits.shading } : {}
6791
+ };
6792
+ }
6793
+ }
6794
+ return validateResponse(
6795
+ ProFlashImageSubmitSchema,
6796
+ await this.request("/create-image-pro-flash", { method: "POST", body: JSON.stringify(body) }),
6797
+ "create-image-pro-flash"
6798
+ );
6799
+ }
6800
+ /** `/edit-image-pro-flash`, text method: edit one image at its own size. The canvas never grows or resizes. */
6801
+ async editImageProFlash(args) {
6802
+ const body = { image: args.image, method: "text", description: args.description };
6803
+ if (args.noBackground != null) body.no_background = args.noBackground;
6804
+ if (args.seed != null) body.seed = args.seed;
6805
+ return validateResponse(
6806
+ ProFlashImageSubmitSchema,
6807
+ await this.request("/edit-image-pro-flash", { method: "POST", body: JSON.stringify(body) }),
6808
+ "edit-image-pro-flash"
6809
+ );
6810
+ }
6811
+ /**
6812
+ * `/inpaint-image-pro-flash`: replace only the white pixels of a same-size
6813
+ * black/white mask, keeping everything else exactly. `Modify current
6814
+ * layer` (the endpoint's default) returns the full composite.
6815
+ */
6816
+ async inpaintImageProFlash(args) {
6817
+ const body = { image: args.image, mask_image: args.maskImage, description: args.description };
6818
+ if (args.noBackground != null) body.no_background = args.noBackground;
6819
+ if (args.seed != null) body.seed = args.seed;
6820
+ return validateResponse(
6821
+ ProFlashImageSubmitSchema,
6822
+ await this.request("/inpaint-image-pro-flash", { method: "POST", body: JSON.stringify(body) }),
6823
+ "inpaint-image-pro-flash"
6824
+ );
6825
+ }
6639
6826
  /**
6640
6827
  * `/generate-ui-v2` ("Generate UI (Pro)"): one UI element (a button, a
6641
6828
  * health bar, an inventory slot, a dialogue box) from a description,
@@ -6860,6 +7047,61 @@ var PixelLabClient = class {
6860
7047
  "animate-pixminimax"
6861
7048
  );
6862
7049
  }
7050
+ /**
7051
+ * `/animate-with-skeleton-v3`, beta (tier 1 subscription or higher): poses
7052
+ * one reference image frame-by-frame from a supplied skeleton per frame,
7053
+ * rather than a text motion description. Confirmed against the live
7054
+ * `animate_with_skeleton_v3` MCP tool schema (the same authoritative
7055
+ * source this codebase already treats as ground truth for the
7056
+ * character/object family) — `action`/`description` are both real,
7057
+ * optional wire fields, contrary to an initial guess that this endpoint
7058
+ * took no text input at all; `direction`/`keypoints`/`firstFrameKeypoints`
7059
+ * are required. The example keypoint from that same tool schema,
7060
+ * `{"label": "RIGHT KNEE", "x": 0.52, "y": 0.71, "z_index": 9}`, is what
7061
+ * `SkeletonKeypointSchema` (`src/skeleton.ts`) models. Same
7062
+ * unverified-completed-shape caveat as `animateWithTextV3`/
7063
+ * `animatePixminimax` above — this endpoint has never been called against
7064
+ * a live account by this codebase.
7065
+ */
7066
+ async animateWithSkeletonV3(args) {
7067
+ const body = {
7068
+ first_frame: args.firstFrame,
7069
+ first_frame_keypoints: args.firstFrameKeypoints,
7070
+ keypoints: args.keypoints,
7071
+ direction: args.direction
7072
+ };
7073
+ if (args.templateId) body.template_id = args.templateId;
7074
+ if (args.action) body.action = args.action;
7075
+ if (args.description) body.description = args.description;
7076
+ if (args.seed != null) body.seed = args.seed;
7077
+ if (args.noBackground != null) body.no_background = args.noBackground;
7078
+ return validateResponse(
7079
+ RevisionJobSubmitSchema,
7080
+ await this.request("/animate-with-skeleton-v3", { method: "POST", body: JSON.stringify(body) }),
7081
+ "animate-with-skeleton-v3"
7082
+ );
7083
+ }
7084
+ /**
7085
+ * `/estimate-skeleton`: auto-derives a reference image's own 18-joint
7086
+ * keypoints, directly usable as `animateWithSkeletonV3`'s
7087
+ * `firstFrameKeypoints` (or, run against a candidate pose image, as one
7088
+ * entry of its `keypoints` sequence). Not called anywhere in this
7089
+ * codebase's own submit/poll pipeline — deliberately kept outside it (see
7090
+ * `runEstimateSkeleton` in `src/cli/commands`) so the only PixelLab
7091
+ * endpoint composed into `animate-with-skeleton-v3`'s submission is that
7092
+ * one endpoint itself, not two unverified calls chained together.
7093
+ * Synchronous per this endpoint's own framing; unverified against a live
7094
+ * account (see `EstimateSkeletonResponseSchema`'s own caveat).
7095
+ */
7096
+ async estimateSkeleton(args) {
7097
+ const body = { image: args.image };
7098
+ const res = validateResponse(
7099
+ EstimateSkeletonResponseSchema,
7100
+ await this.request("/estimate-skeleton", { method: "POST", body: JSON.stringify(body) }),
7101
+ "estimate-skeleton"
7102
+ );
7103
+ return { keypoints: res.keypoints, usage: res.usage };
7104
+ }
6863
7105
  async getBackgroundJob(jobId) {
6864
7106
  const raw = await this.request(`/background-jobs/${encodeURIComponent(jobId)}`);
6865
7107
  return validateResponse(BackgroundJobSchema, raw, "background-jobs/{id}");
@@ -6961,8 +7203,11 @@ function proFlashCharacterCost(width, height, fromReference) {
6961
7203
  const side = Math.max(32, Math.ceil(Math.max(width, height) / 4) * 4);
6962
7204
  const rotations = Math.max(1, Math.ceil(side * side * 8 / 65536));
6963
7205
  if (fromReference) return rotations;
6964
- const image = side <= 96 ? 5 : side <= 208 ? 6 : 9;
6965
- return image + rotations;
7206
+ return proFlashImageCost(side, side) + rotations;
7207
+ }
7208
+ function proFlashImageCost(width, height) {
7209
+ const side = Math.max(width, height);
7210
+ return side <= 96 ? 5 : side <= 208 ? 6 : 9;
6966
7211
  }
6967
7212
  function characterAnimationName(spec) {
6968
7213
  return `pixelkiln:${spec.styleId}/${spec.assetId}`;
@@ -6990,6 +7235,18 @@ function objectProCost(spec) {
6990
7235
  const south = object.reference;
6991
7236
  return south ? proFlashObjectCost(south.width, south.height, object.directions, true) : proFlashObjectCost(spec.width, spec.height, object.directions, false);
6992
7237
  }
7238
+ function animateSkeletonCost(frames) {
7239
+ const anchors = [[3, 2], [8, 3], [15, 4]];
7240
+ for (let i = 0; i < anchors.length - 1; i++) {
7241
+ const [lowFrames, lowCost] = anchors[i];
7242
+ const [highFrames, highCost] = anchors[i + 1];
7243
+ if (frames >= lowFrames && frames <= highFrames) {
7244
+ const t = (frames - lowFrames) / (highFrames - lowFrames);
7245
+ return lowCost + t * (highCost - lowCost);
7246
+ }
7247
+ }
7248
+ return frames <= anchors[0][0] ? anchors[0][1] : anchors[anchors.length - 1][1];
7249
+ }
6993
7250
  var CHARACTER_TEMPLATES = ["mannequin", "bear", "cat", "dog", "horse", "lion"];
6994
7251
  var PRO_FLASH_TEMPLATES = [...CHARACTER_TEMPLATES, "custom"];
6995
7252
  function encodeAnimationJob(job) {
@@ -7038,7 +7295,7 @@ var PixelLabProvider = class _PixelLabProvider {
7038
7295
  return new _PixelLabProvider(new PixelLabClient("download-only"));
7039
7296
  }
7040
7297
  supports(generator) {
7041
- return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character" || generator === "isometricTile" || generator === "objectPro" || generator === "uiAsset" || generator === "uiElement";
7298
+ 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";
7042
7299
  }
7043
7300
  /**
7044
7301
  * `inpaint` (`/inpaint-v3`, a mask) and `image-to-image` (`/edit-images-v2`,
@@ -7048,7 +7305,9 @@ var PixelLabProvider = class _PixelLabProvider {
7048
7305
  * rather than a described edit — and `animate`/`animate-pixminimax`
7049
7306
  * (`/animate-with-text-v3`, `/animate-pixminimax`), which animate any loose
7050
7307
  * image from a text description with no character/object resource
7051
- * required — and `interpolate` (`/interpolation-v2`, the in-betweens
7308
+ * required — `animate-skeleton` (`/animate-with-skeleton-v3`, posing the
7309
+ * source from a supplied per-frame skeleton instead of a text description)
7310
+ * — and `interpolate` (`/interpolation-v2`, the in-betweens
7052
7311
  * from one keyframe to another) and `edit-animation`
7053
7312
  * (`/edit-animation-v2`, one text edit applied across a whole frame set).
7054
7313
  * `outpaint` does not: there is no canvas-expansion endpoint in the API,
@@ -7056,7 +7315,7 @@ var PixelLabProvider = class _PixelLabProvider {
7056
7315
  * outpaint path yet.
7057
7316
  */
7058
7317
  supportsRevision(mode2) {
7059
- return mode2 === "inpaint" || mode2 === "image-to-image" || mode2 === "reduce-colors" || mode2 === "correct-pixelart" || mode2 === "animate" || mode2 === "animate-pixminimax" || mode2 === "interpolate" || mode2 === "edit-animation";
7318
+ 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";
7060
7319
  }
7061
7320
  /** PixelLab's own constraints: submissions must be >2s apart, and
7062
7321
  * background jobs in flight are capped by subscription tier (Tier 1=8,
@@ -7087,6 +7346,10 @@ var PixelLabProvider = class _PixelLabProvider {
7087
7346
  if (spec.revision?.mode === "reduce-colors" || spec.revision?.mode === "correct-pixelart") {
7088
7347
  return { unit: "generations", amount: 0.1, candidates: 1 };
7089
7348
  }
7349
+ if (spec.revision?.mode === "animate-skeleton") {
7350
+ const frames = spec.revision.frames ?? 8;
7351
+ return { unit: "generations", amount: Math.ceil(animateSkeletonCost(frames)), candidates: 1 };
7352
+ }
7090
7353
  if (spec.revision?.mode === "interpolate" || spec.revision?.mode === "edit-animation") {
7091
7354
  const width = spec.revision.sourceWidth ?? spec.width;
7092
7355
  const height = spec.revision.sourceHeight ?? spec.height;
@@ -7100,6 +7363,13 @@ var PixelLabProvider = class _PixelLabProvider {
7100
7363
  const base = Math.max(1, Math.ceil(width * height * frames / 65536));
7101
7364
  return { unit: "generations", amount: spec.revision.enhancePrompt ? base + 0.05 : base, candidates: 1 };
7102
7365
  }
7366
+ if (spec.revision?.engine === "pro-flash") {
7367
+ return {
7368
+ unit: "generations",
7369
+ amount: proFlashImageCost(spec.revision.sourceWidth ?? spec.width, spec.revision.sourceHeight ?? spec.height),
7370
+ candidates: 1
7371
+ };
7372
+ }
7103
7373
  if (spec.revision) {
7104
7374
  const width = spec.revision.sourceWidth ?? spec.width;
7105
7375
  const height = spec.revision.sourceHeight ?? spec.height;
@@ -7114,6 +7384,9 @@ var PixelLabProvider = class _PixelLabProvider {
7114
7384
  if (spec.generator === "objectPro") {
7115
7385
  return { unit: "generations", amount: objectProCost(spec), candidates: 1 };
7116
7386
  }
7387
+ if (spec.generator === "imageProFlash") {
7388
+ return { unit: "generations", amount: proFlashImageCost(spec.width, spec.height), candidates: 1 };
7389
+ }
7117
7390
  return {
7118
7391
  unit: "generations",
7119
7392
  amount: generationCost(spec.width, spec.height, spec.generator),
@@ -7127,7 +7400,17 @@ var PixelLabProvider = class _PixelLabProvider {
7127
7400
  `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`
7128
7401
  );
7129
7402
  }
7130
- if (spec.revision?.mode === "inpaint") {
7403
+ if (spec.revision?.engine === "pro-flash") {
7404
+ const { sourceWidth: width, sourceHeight: height, mode: mode2 } = spec.revision;
7405
+ if (width != null && height != null && (width < 32 || height < 32 || width > 256 || height > 256 || width % 4 || height % 4)) {
7406
+ throw new Error(
7407
+ `PixelLab pro-flash ${mode2} source is ${width}x${height}; the API takes 32 to 256 pixels per side, in multiples of 4`
7408
+ );
7409
+ }
7410
+ if (spec.revision.strength != null) {
7411
+ throw new Error(`PixelLab pro-flash ${mode2} takes no strength; the edit applies in full`);
7412
+ }
7413
+ } else if (spec.revision?.mode === "inpaint") {
7131
7414
  const { sourceWidth: width, sourceHeight: height } = spec.revision;
7132
7415
  if (width != null && height != null && (width < 32 || height < 32 || width > 512 || height > 512)) {
7133
7416
  throw new Error(`PixelLab inpaint source is ${width}x${height}; the API takes 32 to 512 pixels per side`);
@@ -7148,6 +7431,18 @@ var PixelLabProvider = class _PixelLabProvider {
7148
7431
  throw new Error(`PixelLab correct-pixelart source is ${width}x${height}; the API takes at most 1024 pixels per side`);
7149
7432
  }
7150
7433
  }
7434
+ if (spec.revision?.mode === "animate-skeleton") {
7435
+ const { sourceWidth: width, sourceHeight: height, frames, skeletonTemplate } = spec.revision;
7436
+ if (width != null && height != null && (width > 256 || height > 256)) {
7437
+ throw new Error(`PixelLab animate-skeleton source is ${width}x${height}; the API takes at most 256 pixels per side`);
7438
+ }
7439
+ if (frames != null && (frames < 3 || frames > 15)) {
7440
+ throw new Error(`PixelLab animate-skeleton takes 3 to 15 frames; the keypoints file has ${frames}`);
7441
+ }
7442
+ if (skeletonTemplate && !CHARACTER_TEMPLATES.includes(skeletonTemplate)) {
7443
+ throw new Error(`PixelLab animate-skeleton template "${skeletonTemplate}" is not one of ${CHARACTER_TEMPLATES.join(", ")}`);
7444
+ }
7445
+ }
7151
7446
  if (spec.revision?.mode === "animate" || spec.revision?.mode === "animate-pixminimax") {
7152
7447
  const { sourceWidth: width, sourceHeight: height, mode: mode2, frames, lastFrameWidth, lastFrameHeight } = spec.revision;
7153
7448
  if (width != null && height != null && (width > 256 || height > 256)) {
@@ -7208,6 +7503,19 @@ var PixelLabProvider = class _PixelLabProvider {
7208
7503
  `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)`
7209
7504
  );
7210
7505
  }
7506
+ if (spec.generator === "imageProFlash") {
7507
+ if (spec.width < 16 || spec.height < 16 || spec.width > 256 || spec.height > 256 || spec.width % 4 || spec.height % 4) {
7508
+ throw new Error(
7509
+ `PixelLab imageProFlash is ${spec.width}x${spec.height}; the API takes 16 to 256 pixels per side, in multiples of 4`
7510
+ );
7511
+ }
7512
+ if (styleImages.length > 1) {
7513
+ throw new Error("PixelLab imageProFlash takes one style image; list at most one styleImages entry");
7514
+ }
7515
+ if (spec.styleTraits && !styleImages.length) {
7516
+ throw new Error("PixelLab imageProFlash: styleTraits choose what a style image lends; add a styleImages entry or drop them");
7517
+ }
7518
+ }
7211
7519
  if (spec.generator === "uiElement" && (spec.width < 16 || spec.height < 16 || spec.width > 792 || spec.height > 688)) {
7212
7520
  throw new Error(
7213
7521
  `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)`
@@ -7561,7 +7869,8 @@ var PixelLabProvider = class _PixelLabProvider {
7561
7869
  concept: character.concept ? readPose(spec, "concept image", character.concept) : void 0,
7562
7870
  styleCharacterId: character.styleAnchor ? requireStyleObjectId(spec, context) : void 0,
7563
7871
  styleReferenceSize: styleImage ? { width: styleImage.width, height: styleImage.height } : void 0,
7564
- styleTraits: character.styleTraits
7872
+ styleTraits: character.styleTraits,
7873
+ sourceImageId: character.mode === "pro-flash" ? context?.referenceSourceImageId : void 0
7565
7874
  });
7566
7875
  return { jobId: res2.character_id, metadata: { character: { kind: "base", characterId: res2.character_id, mode: character.mode, directions: character.directions, backgroundJobId: res2.background_job_id } } };
7567
7876
  }
@@ -7696,7 +8005,8 @@ var PixelLabProvider = class _PixelLabProvider {
7696
8005
  reference: object.reference ? readPose(spec, "reference sprite", object.reference) : void 0,
7697
8006
  styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
7698
8007
  styleReferenceSize: styleImage ? { width: styleImage.width, height: styleImage.height } : void 0,
7699
- styleTraits: object.styleTraits
8008
+ styleTraits: object.styleTraits,
8009
+ sourceImageId: context?.referenceSourceImageId
7700
8010
  });
7701
8011
  return { jobId: res2.object_id, metadata: { objectPro: { kind: "base", objectId: res2.object_id, directions: object.directions, backgroundJobId: res2.background_job_id } } };
7702
8012
  }
@@ -7995,6 +8305,19 @@ var PixelLabProvider = class _PixelLabProvider {
7995
8305
  });
7996
8306
  return { jobId: res2.ui_asset_id, metadata: { backgroundJobId: res2.background_job_id } };
7997
8307
  }
8308
+ if (spec.generator === "imageProFlash") {
8309
+ const style = styleImages[0];
8310
+ const res2 = await this.client.createImageProFlash({
8311
+ description: spec.prompt,
8312
+ width: spec.width,
8313
+ height: spec.height,
8314
+ noBackground: spec.noBackground,
8315
+ seed: spec.seed,
8316
+ styleImage: style ? { image: { base64: style.base64, format: style.format }, width: style.width, height: style.height } : void 0,
8317
+ styleTraits: spec.styleTraits
8318
+ });
8319
+ return { jobId: res2.background_job_id, metadata: { imageProFlash: proFlashRecord(res2) } };
8320
+ }
7998
8321
  if (spec.generator === "uiElement") {
7999
8322
  const concept = styleImages[0];
8000
8323
  const res2 = await this.client.generateUiV2({
@@ -8148,6 +8471,32 @@ var PixelLabProvider = class _PixelLabProvider {
8148
8471
  const width = revision.sourceWidth;
8149
8472
  const height = revision.sourceHeight;
8150
8473
  const roles = revision.sourceMembers?.map((member) => member.role);
8474
+ if (revision.engine === "pro-flash") {
8475
+ if (revision.mode === "inpaint") {
8476
+ if (!revision.maskFile || !revision.maskSha256 || !revision.maskFormat) {
8477
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision mask is not ready`);
8478
+ }
8479
+ const maskBytes = readFileSync2(revision.maskFile);
8480
+ if (sha256(maskBytes) !== revision.maskSha256) {
8481
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision mask changed after the manifest was resolved`);
8482
+ }
8483
+ const res3 = await this.client.inpaintImageProFlash({
8484
+ image,
8485
+ maskImage: { base64: maskBytes.toString("base64"), format: revision.maskFormat },
8486
+ description: spec.prompt,
8487
+ noBackground: spec.noBackground,
8488
+ seed: spec.seed
8489
+ });
8490
+ return { jobId: res3.background_job_id, metadata: { imageProFlash: proFlashRecord(res3) } };
8491
+ }
8492
+ const res2 = await this.client.editImageProFlash({
8493
+ image,
8494
+ description: spec.prompt,
8495
+ noBackground: spec.noBackground,
8496
+ seed: spec.seed
8497
+ });
8498
+ return { jobId: res2.background_job_id, metadata: { imageProFlash: proFlashRecord(res2) } };
8499
+ }
8151
8500
  if (revision.mode === "inpaint") {
8152
8501
  if (!revision.maskFile || !revision.maskSha256 || !revision.maskFormat) {
8153
8502
  throw new Error(`${spec.styleId}/${spec.assetId}: revision mask is not ready`);
@@ -8243,6 +8592,27 @@ var PixelLabProvider = class _PixelLabProvider {
8243
8592
  });
8244
8593
  return { jobId: res2.background_job_id };
8245
8594
  }
8595
+ if (revision.mode === "animate-skeleton") {
8596
+ if (!revision.keypointsFile || !revision.keypointsSha256 || !revision.skeleton) {
8597
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision keypoints are not ready`);
8598
+ }
8599
+ const keypointsBytes = readFileSync2(revision.keypointsFile);
8600
+ if (sha256(keypointsBytes) !== revision.keypointsSha256) {
8601
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision keypoints changed after the manifest was resolved`);
8602
+ }
8603
+ const res2 = await this.client.animateWithSkeletonV3({
8604
+ firstFrame: image,
8605
+ firstFrameKeypoints: revision.skeleton.firstFrameKeypoints,
8606
+ keypoints: revision.skeleton.frames,
8607
+ direction: revision.direction,
8608
+ templateId: revision.skeletonTemplate,
8609
+ action: spec.prompt,
8610
+ description: revision.description,
8611
+ seed: spec.seed,
8612
+ noBackground: spec.noBackground
8613
+ });
8614
+ return { jobId: res2.background_job_id };
8615
+ }
8246
8616
  if (revision.strength != null) {
8247
8617
  throw new Error(
8248
8618
  `${spec.styleId}/${spec.assetId}: PixelLab image-to-image revisions take no strength; edit-images-v2 always applies the full instruction`
@@ -8308,10 +8678,11 @@ var PixelLabProvider = class _PixelLabProvider {
8308
8678
  if (revisionMode === "reduce-colors" || revisionMode === "correct-pixelart") {
8309
8679
  return this.pollCachedRevision(jobId, context);
8310
8680
  }
8311
- if (revisionMode === "animate" || revisionMode === "animate-pixminimax" || revisionMode === "interpolate" || revisionMode === "edit-animation") {
8681
+ if (revisionMode === "animate" || revisionMode === "animate-pixminimax" || revisionMode === "animate-skeleton" || revisionMode === "interpolate" || revisionMode === "edit-animation") {
8312
8682
  return this.pollAnimateRevision(jobId, context);
8313
8683
  }
8314
8684
  if (context?.spec?.revision) return this.pollRevision(jobId);
8685
+ if (generator === "imageProFlash") return this.pollRevision(jobId);
8315
8686
  if (generator === "pixflux") {
8316
8687
  const file = path12.join(_PixelLabProvider.cacheDir(), `${jobId}.png`);
8317
8688
  if (existsSync7(file)) {
@@ -8892,6 +9263,13 @@ function readPose(spec, what, image) {
8892
9263
  }
8893
9264
  return { base64: bytes.toString("base64"), format: image.format };
8894
9265
  }
9266
+ function proFlashRecord(res) {
9267
+ return {
9268
+ imageId: res.image_id,
9269
+ sourceImageId: res.source_image_id,
9270
+ ...res.estimated_generations != null ? { estimatedGenerations: res.estimated_generations } : {}
9271
+ };
9272
+ }
8895
9273
  function editAnimationFrameCeiling(width, height) {
8896
9274
  const side = Math.max(width, height);
8897
9275
  if (side <= 64) return 16;
@@ -8990,9 +9368,9 @@ var RetroDiffusionClient = class {
8990
9368
  if (!response.ok) throw new ProviderError("retrodiffusion", `Retro Diffusion download failed (${response.status})`, { status: response.status });
8991
9369
  return Buffer.from(await response.arrayBuffer());
8992
9370
  }
8993
- async call(path47, init = {}) {
9371
+ async call(path48, init = {}) {
8994
9372
  if (!this.token) throw new Error("RD_API_KEY is not set");
8995
- const response = await this.request(`${this.baseUrl}${path47}`, {
9373
+ const response = await this.request(`${this.baseUrl}${path48}`, {
8996
9374
  ...init,
8997
9375
  headers: {
8998
9376
  "Content-Type": "application/json",
@@ -10291,6 +10669,7 @@ async function resolveSpecs(loaded, filter) {
10291
10669
  uiPieces: generator === "uiAsset" ? asset.pieces : void 0,
10292
10670
  uiElements: generator === "uiAsset" ? asset.elements : void 0,
10293
10671
  uiColorPalette: generator === "uiAsset" || generator === "uiElement" ? style.uiColorPalette : void 0,
10672
+ ...generator === "imageProFlash" && style.styleTraits ? { styleTraits: style.styleTraits } : {},
10294
10673
  palette: style.palette,
10295
10674
  enforcePalette: style.enforcePalette,
10296
10675
  noBackground: style.noBackground,
@@ -10535,6 +10914,8 @@ async function resolveSpecs(loaded, filter) {
10535
10914
  const paletteImage = paletteImageFile ? await optionalRevisionImage(paletteImageFile, "revision palette image", "png") : null;
10536
10915
  const lastFrameFile = asset.revision.lastFrame ? path13.resolve(root, asset.revision.lastFrame) : void 0;
10537
10916
  const lastFrame = lastFrameFile ? await optionalRevisionImage(lastFrameFile, "revision last frame") : null;
10917
+ const keypointsFile = asset.revision.keypointsFile ? path13.resolve(root, asset.revision.keypointsFile) : void 0;
10918
+ const keypoints = keypointsFile ? await optionalSkeletonSet(keypointsFile) : null;
10538
10919
  resolved.revision = {
10539
10920
  mode: asset.revision.mode,
10540
10921
  sourceAssetId: asset.revision.from,
@@ -10574,7 +10955,20 @@ async function resolveSpecs(loaded, filter) {
10574
10955
  lastFrameFormat: lastFrame?.format ?? null
10575
10956
  } : {},
10576
10957
  ...asset.revision.direction ? { direction: asset.revision.direction } : {},
10577
- ...asset.revision.enhancePrompt == null ? {} : { enhancePrompt: asset.revision.enhancePrompt }
10958
+ ...asset.revision.enhancePrompt == null ? {} : { enhancePrompt: asset.revision.enhancePrompt },
10959
+ ...keypointsFile ? {
10960
+ keypointsFile,
10961
+ keypointsSha256: keypoints?.hash ?? null,
10962
+ skeleton: keypoints?.skeleton ?? null,
10963
+ // The manifest's own `frames` field stays rejected for this
10964
+ // mode (superRefine, types.ts) — the real frame count comes
10965
+ // from the keypoints file once it exists, not a manifest
10966
+ // number, so plan can only know it once authored.
10967
+ ...keypoints ? { frames: keypoints.skeleton.frames.length } : {}
10968
+ } : {},
10969
+ ...asset.revision.skeletonTemplate ? { skeletonTemplate: asset.revision.skeletonTemplate } : {},
10970
+ ...asset.revision.description ? { description: asset.revision.description } : {},
10971
+ ...asset.revision.engine ? { engine: asset.revision.engine } : {}
10578
10972
  };
10579
10973
  }
10580
10974
  if (asset.batch) {
@@ -10695,6 +11089,17 @@ async function optionalRevisionImage(file, label, requiredFormat) {
10695
11089
  }
10696
11090
  return { hash: sha256(bytes), ...metadata };
10697
11091
  }
11092
+ async function optionalSkeletonSet(file) {
11093
+ if (!existsSync8(file)) return null;
11094
+ const bytes = await readFile6(file);
11095
+ let json;
11096
+ try {
11097
+ json = JSON.parse(bytes.toString("utf8"));
11098
+ } catch (error) {
11099
+ throw new Error(`revision keypoints file ${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
11100
+ }
11101
+ return { hash: sha256(bytes), skeleton: parseSkeletonSet(json, file) };
11102
+ }
10698
11103
  async function revisionSourceMembers(stem, label) {
10699
11104
  if (existsSync8(stem)) return null;
10700
11105
  const at = (role) => memberPath(stem, role, 0, 2, MediaType.PNG);
@@ -11230,11 +11635,11 @@ import path15 from "path";
11230
11635
  import { mkdir as mkdir5, readFile as readFile8, rename as rename5, rm as rm5, writeFile as writeFile5 } from "fs/promises";
11231
11636
  import { existsSync as existsSync10 } from "fs";
11232
11637
  import pathModule from "path";
11233
- import { z as z3 } from "zod";
11234
- var HashCacheSchema = z3.object({
11235
- version: z3.literal(1),
11638
+ import { z as z4 } from "zod";
11639
+ var HashCacheSchema = z4.object({
11640
+ version: z4.literal(1),
11236
11641
  /** objectId → sha256 of that object's image bytes. */
11237
- hashes: z3.record(z3.string())
11642
+ hashes: z4.record(z4.string())
11238
11643
  });
11239
11644
  var SHA256_HEX = /^[a-f0-9]{64}$/;
11240
11645
  function isSha256Hash(value) {
@@ -11243,10 +11648,10 @@ function isSha256Hash(value) {
11243
11648
  function parseCache(value) {
11244
11649
  return HashCacheSchema.parse(value);
11245
11650
  }
11246
- async function loadCache(path47) {
11247
- if (!existsSync10(path47)) return { version: 1, hashes: {} };
11651
+ async function loadCache(path48) {
11652
+ if (!existsSync10(path48)) return { version: 1, hashes: {} };
11248
11653
  try {
11249
- const parsed = HashCacheSchema.safeParse(JSON.parse(await readFile8(path47, "utf8")));
11654
+ const parsed = HashCacheSchema.safeParse(JSON.parse(await readFile8(path48, "utf8")));
11250
11655
  if (!parsed.success) return { version: 1, hashes: {} };
11251
11656
  return {
11252
11657
  version: 1,
@@ -11258,18 +11663,18 @@ async function loadCache(path47) {
11258
11663
  return { version: 1, hashes: {} };
11259
11664
  }
11260
11665
  }
11261
- async function saveCache(path47, cache) {
11666
+ async function saveCache(path48, cache) {
11262
11667
  const sorted = {};
11263
11668
  for (const key of Object.keys(cache.hashes).sort()) {
11264
11669
  const hash = cache.hashes[key];
11265
11670
  if (!isSha256Hash(hash)) throw new Error(`Refusing to cache invalid SHA-256 for ${key}`);
11266
11671
  sorted[key] = hash;
11267
11672
  }
11268
- await mkdir5(pathModule.dirname(pathModule.resolve(path47)), { recursive: true });
11269
- const tmp = `${path47}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
11673
+ await mkdir5(pathModule.dirname(pathModule.resolve(path48)), { recursive: true });
11674
+ const tmp = `${path48}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
11270
11675
  try {
11271
11676
  await writeFile5(tmp, JSON.stringify({ version: 1, hashes: sorted }, null, 2) + "\n");
11272
- await rename5(tmp, path47);
11677
+ await rename5(tmp, path48);
11273
11678
  } finally {
11274
11679
  await rm5(tmp, { force: true });
11275
11680
  }
@@ -12027,20 +12432,20 @@ async function applyTags(provider, decisions, existing, opts = {}) {
12027
12432
  import { mkdir as mkdir7, readFile as readFile12, rename as rename6, rm as rm6, writeFile as writeFile8 } from "fs/promises";
12028
12433
  import { existsSync as existsSync14 } from "fs";
12029
12434
  import path18 from "path";
12030
- import { z as z4 } from "zod";
12031
- var WorkspaceProjectSchema = z4.object({
12032
- id: z4.string().min(1),
12435
+ import { z as z5 } from "zod";
12436
+ var WorkspaceProjectSchema = z5.object({
12437
+ id: z5.string().min(1),
12033
12438
  /** Manifest path, relative to the catalog file's own directory. */
12034
- manifest: z4.string().min(1),
12439
+ manifest: z5.string().min(1),
12035
12440
  /** Lockfile path, relative to the catalog file's own directory. */
12036
- lock: z4.string().min(1),
12037
- provider: z4.string().min(1).default("pixellab"),
12441
+ lock: z5.string().min(1),
12442
+ provider: z5.string().min(1).default("pixellab"),
12038
12443
  /** Free-form label for a shared account, e.g. distinguishing sandboxes. */
12039
- account: z4.string().optional()
12444
+ account: z5.string().optional()
12040
12445
  }).strict();
12041
- var WorkspaceSchema = z4.object({
12042
- version: z4.literal(1),
12043
- projects: z4.array(WorkspaceProjectSchema).default([])
12446
+ var WorkspaceSchema = z5.object({
12447
+ version: z5.literal(1),
12448
+ projects: z5.array(WorkspaceProjectSchema).default([])
12044
12449
  }).strict();
12045
12450
  function parseWorkspace(raw) {
12046
12451
  const parsed = WorkspaceSchema.safeParse(raw);
@@ -13225,62 +13630,62 @@ import path25 from "path";
13225
13630
  import { existsSync as existsSync17 } from "fs";
13226
13631
  import { copyFile, mkdir as mkdir9, readFile as readFile16, rename as rename8, rm as rm8, writeFile as writeFile11 } from "fs/promises";
13227
13632
  import path24 from "path";
13228
- import { z as z6 } from "zod";
13633
+ import { z as z7 } from "zod";
13229
13634
 
13230
13635
  // src/manifest-edit.ts
13231
13636
  import { readFile as readFile15, rename as rename7, rm as rm7, writeFile as writeFile10 } from "fs/promises";
13232
13637
  import path23 from "path";
13233
- import { z as z5 } from "zod";
13234
- var HexSha = z5.string().regex(/^[0-9a-f]{64}$/);
13235
- var AssetPatchSchema = z5.object({
13236
- prompt: z5.string().optional(),
13638
+ import { z as z6 } from "zod";
13639
+ var HexSha = z6.string().regex(/^[0-9a-f]{64}$/);
13640
+ var AssetPatchSchema = z6.object({
13641
+ prompt: z6.string().optional(),
13237
13642
  /** Per-style override for the style being viewed; null removes it. */
13238
- promptForStyle: z5.object({ styleId: z5.string().min(1), prompt: z5.string().nullable() }).optional(),
13239
- category: z5.string().nullable().optional(),
13240
- width: z5.number().int().nullable().optional(),
13241
- height: z5.number().int().nullable().optional(),
13242
- size: z5.number().int().nullable().optional(),
13243
- tags: z5.array(z5.string()).optional()
13643
+ promptForStyle: z6.object({ styleId: z6.string().min(1), prompt: z6.string().nullable() }).optional(),
13644
+ category: z6.string().nullable().optional(),
13645
+ width: z6.number().int().nullable().optional(),
13646
+ height: z6.number().int().nullable().optional(),
13647
+ size: z6.number().int().nullable().optional(),
13648
+ tags: z6.array(z6.string()).optional()
13244
13649
  }).strict();
13245
- var NewStateSchema = z5.object({
13650
+ var NewStateSchema = z6.object({
13246
13651
  /** Asset id of the parent this states; must already exist. */
13247
- of: z5.string().min(1),
13248
- paletteFromReference: z5.boolean().optional(),
13249
- canvas: z5.object({ width: z5.number().int().min(16).max(256), height: z5.number().int().min(16).max(256) }).strict().optional()
13652
+ of: z6.string().min(1),
13653
+ paletteFromReference: z6.boolean().optional(),
13654
+ canvas: z6.object({ width: z6.number().int().min(16).max(256), height: z6.number().int().min(16).max(256) }).strict().optional()
13250
13655
  }).strict();
13251
- var NewAnimationSchema = z5.object({
13656
+ var NewAnimationSchema = z6.object({
13252
13657
  /** Asset id of the parent this animates; must already exist. */
13253
- of: z5.string().min(1),
13658
+ of: z6.string().min(1),
13254
13659
  /** A PixelLab template id (`character` only; `objectPro` has no template concept). */
13255
- template: z5.string().min(1).optional(),
13660
+ template: z6.string().min(1).optional(),
13256
13661
  direction: CharacterDirectionSchema.optional(),
13257
- frames: z5.number().int().min(4).max(16).optional(),
13258
- fps: z5.number().int().min(1).max(60).optional(),
13662
+ frames: z6.number().int().min(4).max(16).optional(),
13663
+ fps: z6.number().int().min(1).max(60).optional(),
13259
13664
  mode: CharacterAnimationModeSchema.optional(),
13260
13665
  /** What is being animated, when the parent's own description would mislead the model (`character` only). */
13261
- subject: z5.string().min(1).optional(),
13262
- enhancePrompt: z5.boolean().optional()
13666
+ subject: z6.string().min(1).optional(),
13667
+ enhancePrompt: z6.boolean().optional()
13263
13668
  }).strict();
13264
- var NewAssetSchema = z5.object({
13265
- prompt: z5.string(),
13266
- category: z5.string().optional(),
13267
- width: z5.number().int().optional(),
13268
- height: z5.number().int().optional(),
13269
- size: z5.number().int().optional(),
13270
- tags: z5.array(z5.string()).optional(),
13669
+ var NewAssetSchema = z6.object({
13670
+ prompt: z6.string(),
13671
+ category: z6.string().optional(),
13672
+ width: z6.number().int().optional(),
13673
+ height: z6.number().int().optional(),
13674
+ size: z6.number().int().optional(),
13675
+ tags: z6.array(z6.string()).optional(),
13271
13676
  /** Restrict to these styles; empty or omitted means every style. */
13272
- styles: z5.array(z5.string().min(1)).optional(),
13677
+ styles: z6.array(z6.string().min(1)).optional(),
13273
13678
  /**
13274
13679
  * A controlled regeneration of another asset in the same manifest,
13275
13680
  * instead of a fresh generation. `inpaint` needs a mask upload the
13276
13681
  * gallery does not offer yet, so only `image-to-image` can be created
13277
13682
  * here; hand-edit the manifest for the other modes.
13278
13683
  */
13279
- revision: z5.object({
13280
- mode: z5.literal("image-to-image"),
13684
+ revision: z6.object({
13685
+ mode: z6.literal("image-to-image"),
13281
13686
  /** Asset id of the parent this revises; must already exist. */
13282
- from: z5.string().min(1),
13283
- strength: z5.number().min(0).max(1).optional()
13687
+ from: z6.string().min(1),
13688
+ strength: z6.number().min(0).max(1).optional()
13284
13689
  }).strict().optional(),
13285
13690
  /** A pose or outfit of an existing `character`/`objectPro` base or state. */
13286
13691
  state: NewStateSchema.optional(),
@@ -13296,58 +13701,58 @@ var CANDIDATE_OPTION = {
13296
13701
  comfyui: "numImages",
13297
13702
  scenario: "numOutputs"
13298
13703
  };
13299
- var ManifestEditSchema = z5.discriminatedUnion("action", [
13300
- z5.object({
13301
- action: z5.literal("patch-asset"),
13704
+ var ManifestEditSchema = z6.discriminatedUnion("action", [
13705
+ z6.object({
13706
+ action: z6.literal("patch-asset"),
13302
13707
  /** Workspace project id; omitted for a single-project gallery. */
13303
- project: z5.string().min(1).optional(),
13304
- assetId: z5.string().min(1),
13708
+ project: z6.string().min(1).optional(),
13709
+ assetId: z6.string().min(1),
13305
13710
  /** Manifest bytes the page last saw; a mismatch refuses the write. */
13306
13711
  expectedSha256: HexSha,
13307
13712
  patch: AssetPatchSchema
13308
13713
  }).strict(),
13309
- z5.object({
13310
- action: z5.literal("add-asset"),
13311
- project: z5.string().min(1).optional(),
13312
- assetId: z5.string().min(1).regex(/^[^/\\]+$/, "asset ids cannot contain slashes"),
13714
+ z6.object({
13715
+ action: z6.literal("add-asset"),
13716
+ project: z6.string().min(1).optional(),
13717
+ assetId: z6.string().min(1).regex(/^[^/\\]+$/, "asset ids cannot contain slashes"),
13313
13718
  expectedSha256: HexSha,
13314
13719
  asset: NewAssetSchema
13315
13720
  }).strict(),
13316
- z5.object({
13317
- action: z5.literal("set-source"),
13318
- project: z5.string().min(1).optional(),
13319
- assetId: z5.string().min(1),
13320
- styleId: z5.string().min(1),
13721
+ z6.object({
13722
+ action: z6.literal("set-source"),
13723
+ project: z6.string().min(1).optional(),
13724
+ assetId: z6.string().min(1),
13725
+ styleId: z6.string().min(1),
13321
13726
  /** Manifest-relative path of the art that stands in for this asset in this style. */
13322
- source: z5.string().min(1),
13727
+ source: z6.string().min(1),
13323
13728
  expectedSha256: HexSha
13324
13729
  }).strict(),
13325
- z5.object({
13326
- action: z5.literal("clear-source"),
13327
- project: z5.string().min(1).optional(),
13328
- assetId: z5.string().min(1),
13329
- styleId: z5.string().min(1),
13730
+ z6.object({
13731
+ action: z6.literal("clear-source"),
13732
+ project: z6.string().min(1).optional(),
13733
+ assetId: z6.string().min(1),
13734
+ styleId: z6.string().min(1),
13330
13735
  expectedSha256: HexSha
13331
13736
  }).strict(),
13332
- z5.object({
13333
- action: z5.literal("patch-style"),
13334
- project: z5.string().min(1).optional(),
13335
- styleId: z5.string().min(1),
13737
+ z6.object({
13738
+ action: z6.literal("patch-style"),
13739
+ project: z6.string().min(1).optional(),
13740
+ styleId: z6.string().min(1),
13336
13741
  expectedSha256: HexSha,
13337
- patch: z5.object({
13742
+ patch: z6.object({
13338
13743
  /** Candidates per generation, written to the provider's own option. */
13339
- candidates: z5.number().int().min(1).max(64).optional(),
13744
+ candidates: z6.number().int().min(1).max(64).optional(),
13340
13745
  /** Empty clears the style's own value (inherit, or the default). */
13341
- promptPrefix: z5.string().optional(),
13342
- promptSuffix: z5.string().optional(),
13746
+ promptPrefix: z6.string().optional(),
13747
+ promptSuffix: z6.string().optional(),
13343
13748
  /** `#rrggbb` values; null or empty clears the style's own palette. */
13344
- palette: z5.array(z5.string().regex(/^#?[0-9a-f]{6}$/i, "expected a six-digit hex colour")).max(256).nullable().optional(),
13749
+ palette: z6.array(z6.string().regex(/^#?[0-9a-f]{6}$/i, "expected a six-digit hex colour")).max(256).nullable().optional(),
13345
13750
  /** Snap downloaded art to the palette; false clears the style's own value. */
13346
- enforcePalette: z5.boolean().optional(),
13751
+ enforcePalette: z6.boolean().optional(),
13347
13752
  /** Provider view name (PixelLab: low top-down, high top-down, side); empty clears. */
13348
- view: z5.string().max(64).nullable().optional(),
13753
+ view: z6.string().max(64).nullable().optional(),
13349
13754
  /** pixflux only; null clears the style's own value. */
13350
- noBackground: z5.boolean().nullable().optional()
13755
+ noBackground: z6.boolean().nullable().optional()
13351
13756
  }).strict().refine((patch) => Object.keys(patch).length > 0, { message: "nothing to change" })
13352
13757
  }).strict()
13353
13758
  ]);
@@ -13630,35 +14035,35 @@ async function detachHandEdit(loaded, spec, opts = {}) {
13630
14035
  });
13631
14036
  return { manifestSha256: result.sha256, changed: result.changed };
13632
14037
  }
13633
- var CompanionOutputSchema = z6.object({
14038
+ var CompanionOutputSchema = z7.object({
13634
14039
  /** Output role for a set member; null for a single image. */
13635
- role: z6.string().nullable(),
14040
+ role: z7.string().nullable(),
13636
14041
  /** sha256 of the generated file this member started from; null when it started from untracked art. */
13637
- basedOn: z6.string().regex(/^[0-9a-f]{64}$/).nullable(),
14042
+ basedOn: z7.string().regex(/^[0-9a-f]{64}$/).nullable(),
13638
14043
  /** sha256 of the member as saved, so a later change by another tool is visible. */
13639
- sha256: z6.string().regex(/^[0-9a-f]{64}$/)
14044
+ sha256: z7.string().regex(/^[0-9a-f]{64}$/)
13640
14045
  }).strict();
13641
- var CompanionV2Schema = z6.object({
13642
- version: z6.literal(2),
14046
+ var CompanionV2Schema = z7.object({
14047
+ version: z7.literal(2),
13643
14048
  /** Editor identity the bridge reported, e.g. `pixelorama@v1.2.2-stable`. */
13644
- editor: z6.string().min(1),
13645
- protocol: z6.number().int().positive(),
13646
- savedAt: z6.string().datetime(),
14049
+ editor: z7.string().min(1),
14050
+ protocol: z7.number().int().positive(),
14051
+ savedAt: z7.string().datetime(),
13647
14052
  /** Basename of the layered project file beside the edit, when one was kept. */
13648
- project: z6.string().min(1).nullable(),
14053
+ project: z7.string().min(1).nullable(),
13649
14054
  /** One entry per file, in output order. */
13650
- outputs: z6.array(CompanionOutputSchema).min(1)
14055
+ outputs: z7.array(CompanionOutputSchema).min(1)
13651
14056
  }).strict();
13652
- var CompanionV1Schema = z6.object({
13653
- version: z6.literal(1),
13654
- basedOn: z6.string().regex(/^[0-9a-f]{64}$/).nullable(),
13655
- sha256: z6.string().regex(/^[0-9a-f]{64}$/),
13656
- editor: z6.string().min(1),
13657
- protocol: z6.number().int().positive(),
13658
- savedAt: z6.string().datetime(),
13659
- project: z6.string().min(1).nullable()
14057
+ var CompanionV1Schema = z7.object({
14058
+ version: z7.literal(1),
14059
+ basedOn: z7.string().regex(/^[0-9a-f]{64}$/).nullable(),
14060
+ sha256: z7.string().regex(/^[0-9a-f]{64}$/),
14061
+ editor: z7.string().min(1),
14062
+ protocol: z7.number().int().positive(),
14063
+ savedAt: z7.string().datetime(),
14064
+ project: z7.string().min(1).nullable()
13660
14065
  }).strict();
13661
- var HandEditCompanionSchema = z6.union([CompanionV2Schema, CompanionV1Schema]).transform(
14066
+ var HandEditCompanionSchema = z7.union([CompanionV2Schema, CompanionV1Schema]).transform(
13662
14067
  (value) => value.version === 2 ? value : {
13663
14068
  version: 2,
13664
14069
  editor: value.editor,
@@ -13817,41 +14222,41 @@ import path28 from "path";
13817
14222
 
13818
14223
  // src/gallery/edit.ts
13819
14224
  import path26 from "path";
13820
- import { z as z7 } from "zod";
13821
- var Base64 = z7.string().regex(/^[A-Za-z0-9+/]*={0,2}$/).max(Math.ceil(MAX_HAND_EDIT_BYTES / 3) * 4);
13822
- var HandEditRequestSchema = z7.discriminatedUnion("action", [
13823
- z7.object({
13824
- action: z7.literal("start-edit"),
13825
- project: z7.string().min(1).optional(),
13826
- assetId: z7.string().min(1),
13827
- styleId: z7.string().min(1),
13828
- expectedSha256: z7.string().regex(/^[0-9a-f]{64}$/),
14225
+ import { z as z8 } from "zod";
14226
+ var Base64 = z8.string().regex(/^[A-Za-z0-9+/]*={0,2}$/).max(Math.ceil(MAX_HAND_EDIT_BYTES / 3) * 4);
14227
+ var HandEditRequestSchema = z8.discriminatedUnion("action", [
14228
+ z8.object({
14229
+ action: z8.literal("start-edit"),
14230
+ project: z8.string().min(1).optional(),
14231
+ assetId: z8.string().min(1),
14232
+ styleId: z8.string().min(1),
14233
+ expectedSha256: z8.string().regex(/^[0-9a-f]{64}$/),
13829
14234
  /** Also launch the author's editor on the file. */
13830
- open: z7.boolean().optional()
14235
+ open: z8.boolean().optional()
13831
14236
  }).strict(),
13832
- z7.object({
13833
- action: z7.literal("detach-edit"),
13834
- project: z7.string().min(1).optional(),
13835
- assetId: z7.string().min(1),
13836
- styleId: z7.string().min(1),
13837
- expectedSha256: z7.string().regex(/^[0-9a-f]{64}$/)
14237
+ z8.object({
14238
+ action: z8.literal("detach-edit"),
14239
+ project: z8.string().min(1).optional(),
14240
+ assetId: z8.string().min(1),
14241
+ styleId: z8.string().min(1),
14242
+ expectedSha256: z8.string().regex(/^[0-9a-f]{64}$/)
13838
14243
  }).strict(),
13839
14244
  /** From the in-browser editor: the page holds the bytes, the server validates and writes. */
13840
- z7.object({
13841
- action: z7.literal("save-edit"),
13842
- project: z7.string().min(1).optional(),
13843
- assetId: z7.string().min(1),
13844
- styleId: z7.string().min(1),
13845
- expectedSha256: z7.string().regex(/^[0-9a-f]{64}$/),
14245
+ z8.object({
14246
+ action: z8.literal("save-edit"),
14247
+ project: z8.string().min(1).optional(),
14248
+ assetId: z8.string().min(1),
14249
+ styleId: z8.string().min(1),
14250
+ expectedSha256: z8.string().regex(/^[0-9a-f]{64}$/),
13846
14251
  /** The flattened image; for a frame set, `frames` instead. */
13847
14252
  png: Base64.min(1).optional(),
13848
14253
  /** Every frame of a set, tagged with the role it was opened under (null for one added in the editor). */
13849
- frames: z7.array(z7.object({ role: z7.string().min(1).nullable(), png: Base64.min(1) }).strict()).min(1).max(256).optional(),
14254
+ frames: z8.array(z8.object({ role: z8.string().min(1).nullable(), png: Base64.min(1) }).strict()).min(1).max(256).optional(),
13850
14255
  /** The editor's layered project file, kept beside the edit. */
13851
14256
  pxo: Base64.optional(),
13852
14257
  /** Editor identity from the bridge's ready message. */
13853
- editor: z7.string().min(1).max(200),
13854
- protocol: z7.number().int().positive()
14258
+ editor: z8.string().min(1).max(200),
14259
+ protocol: z8.number().int().positive()
13855
14260
  }).strict()
13856
14261
  ]);
13857
14262
  function createGalleryEditHandler(opts) {
@@ -15035,7 +15440,7 @@ async function serveGallery(opts) {
15035
15440
 
15036
15441
  // src/gallery/generate.ts
15037
15442
  import { randomBytes as randomBytes2 } from "crypto";
15038
- import { z as z8 } from "zod";
15443
+ import { z as z9 } from "zod";
15039
15444
 
15040
15445
  // src/pipeline/poll.ts
15041
15446
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -15315,7 +15720,11 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
15315
15720
  ...spec.revision.fps == null ? {} : { fps: spec.revision.fps },
15316
15721
  ...spec.revision.lastFrameSha256 ? { lastFrameSha256: spec.revision.lastFrameSha256 } : {},
15317
15722
  ...spec.revision.direction ? { direction: spec.revision.direction } : {},
15318
- ...spec.revision.enhancePrompt == null ? {} : { enhancePrompt: spec.revision.enhancePrompt }
15723
+ ...spec.revision.enhancePrompt == null ? {} : { enhancePrompt: spec.revision.enhancePrompt },
15724
+ ...spec.revision.keypointsSha256 ? { keypointsSha256: spec.revision.keypointsSha256 } : {},
15725
+ ...spec.revision.skeletonTemplate ? { skeletonTemplate: spec.revision.skeletonTemplate } : {},
15726
+ ...spec.revision.description ? { description: spec.revision.description } : {},
15727
+ ...spec.revision.engine ? { engine: spec.revision.engine } : {}
15319
15728
  } : null,
15320
15729
  outfit: spec.character?.kind === "outfit" && outfitParentEntry ? {
15321
15730
  sourceAssetId: spec.character.parentAssetId,
@@ -15359,8 +15768,10 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
15359
15768
  path: currentEntryOutputPath(parentEntry, spec.character.parentSpec, index),
15360
15769
  role: output.role
15361
15770
  })) : void 0;
15771
+ const referenceSourceImageId = ownedReferenceImageId(spec, lock, provider.id);
15362
15772
  const { jobId, metadata } = await provider.submit(spec, refs, {
15363
15773
  ...previousJobId ? { previousJobId } : {},
15774
+ ...referenceSourceImageId ? { referenceSourceImageId } : {},
15364
15775
  ...parentEntry?.objectId ? { parentObjectId: parentEntry.objectId } : {},
15365
15776
  ...anchorEntry?.objectId ? { styleObjectId: anchorEntry.objectId } : {},
15366
15777
  ...parentOutputs ? { parentOutputs } : {},
@@ -15427,6 +15838,17 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
15427
15838
  }
15428
15839
  return { submitted, failed, spent, unit };
15429
15840
  }
15841
+ function ownedReferenceImageId(spec, lock, providerId) {
15842
+ 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;
15843
+ if (!sha2562) return void 0;
15844
+ for (const entry of Object.values(lock.entries)) {
15845
+ if (entry.provider !== providerId || entry.generator !== "imageProFlash" || entry.status !== "downloaded") continue;
15846
+ if (entry.outputs.length !== 1 || entry.outputs[0].sha256 !== sha2562) continue;
15847
+ const recorded = entry.providerMetadata?.[providerId]?.imageProFlash;
15848
+ if (typeof recorded?.sourceImageId === "string" && recorded.sourceImageId) return recorded.sourceImageId;
15849
+ }
15850
+ return void 0;
15851
+ }
15430
15852
 
15431
15853
  // src/pick/server.ts
15432
15854
  import { existsSync as existsSync21 } from "fs";
@@ -16118,26 +16540,26 @@ async function runPicker(provider, lock, lockPath, opts = {}) {
16118
16540
  }
16119
16541
 
16120
16542
  // src/gallery/generate.ts
16121
- var GenerateRequestSchema = z8.object({
16122
- project: z8.string().min(1).optional(),
16123
- keys: z8.array(z8.string().min(1)).min(1).max(500),
16543
+ var GenerateRequestSchema = z9.object({
16544
+ project: z9.string().min(1).optional(),
16545
+ keys: z9.array(z9.string().min(1)).min(1).max(500),
16124
16546
  /**
16125
16547
  * Regenerate work that is up to date, as `gen --force` does, and let the
16126
16548
  * fetch replace a locally modified or untracked file. With `restore`, put
16127
16549
  * the recorded bytes back over a modified file.
16128
16550
  */
16129
- force: z8.boolean().optional(),
16551
+ force: z9.boolean().optional(),
16130
16552
  /** Advance in-flight, review, or selected work without submitting anything. */
16131
- resume: z8.boolean().optional(),
16553
+ resume: z9.boolean().optional(),
16132
16554
  /** Re-download downloaded outputs and replace files whose object changed upstream. */
16133
- refresh: z8.boolean().optional(),
16555
+ refresh: z9.boolean().optional(),
16134
16556
  /** Put recorded outputs back on disk from the cache or the provider, without generating. */
16135
- restore: z8.boolean().optional(),
16557
+ restore: z9.boolean().optional(),
16136
16558
  /**
16137
16559
  * Bring a replaced generation back: 1-based position in the record's
16138
16560
  * history (newest first) or an output hash prefix. One key at a time.
16139
16561
  */
16140
- revert: z8.string().min(1).max(64).optional()
16562
+ revert: z9.string().min(1).max(64).optional()
16141
16563
  }).strict().refine((request) => [request.resume, request.refresh, request.restore, request.revert !== void 0].filter(Boolean).length <= 1, {
16142
16564
  message: "resume, refresh, restore, and revert are exclusive"
16143
16565
  }).refine((request) => request.revert === void 0 || request.keys.length === 1, { message: "revert takes one key" });
@@ -19023,11 +19445,11 @@ import { readFile as readFile28 } from "fs/promises";
19023
19445
  import path41 from "path";
19024
19446
  import { existsSync as existsSync30 } from "fs";
19025
19447
  import { readFile as readFile27 } from "fs/promises";
19026
- import { z as z9 } from "zod";
19448
+ import { z as z10 } from "zod";
19027
19449
  var SHA256_RE = /^[0-9a-f]{64}$/;
19028
19450
  var HEX_RE = /^#[0-9a-f]{6}$/;
19029
19451
  var MAX_REDMEAN_DISTANCE = 765;
19030
- var QualityPathSchema = z9.string().min(1).refine(
19452
+ var QualityPathSchema = z10.string().min(1).refine(
19031
19453
  (value) => !path41.posix.isAbsolute(value) && !value.includes("\\"),
19032
19454
  "expected a portable relative path"
19033
19455
  );
@@ -19041,53 +19463,53 @@ var DEFAULT_QUALITY_TOLERANCES = {
19041
19463
  maxEdgeContrastDrop: 0.03,
19042
19464
  maxIsolatedPixelIncrease: 5e-3
19043
19465
  };
19044
- var QualityToleranceSchema = z9.object({
19045
- requireExactHash: z9.boolean(),
19046
- maxColorCountIncrease: z9.number().int().min(0),
19047
- maxNewColors: z9.number().int().min(0),
19048
- maxTransparencyDelta: z9.number().min(0).max(1),
19049
- maxPartialAlphaIncrease: z9.number().min(0).max(1),
19050
- maxEdgeDensityDelta: z9.number().min(0).max(1),
19051
- maxEdgeContrastDrop: z9.number().min(0).max(1),
19052
- maxIsolatedPixelIncrease: z9.number().min(0).max(1)
19466
+ var QualityToleranceSchema = z10.object({
19467
+ requireExactHash: z10.boolean(),
19468
+ maxColorCountIncrease: z10.number().int().min(0),
19469
+ maxNewColors: z10.number().int().min(0),
19470
+ maxTransparencyDelta: z10.number().min(0).max(1),
19471
+ maxPartialAlphaIncrease: z10.number().min(0).max(1),
19472
+ maxEdgeDensityDelta: z10.number().min(0).max(1),
19473
+ maxEdgeContrastDrop: z10.number().min(0).max(1),
19474
+ maxIsolatedPixelIncrease: z10.number().min(0).max(1)
19053
19475
  }).strict();
19054
- var ImageQualityMetricsSchema = z9.object({
19055
- sha256: z9.string().regex(SHA256_RE),
19056
- width: z9.number().int().min(1),
19057
- height: z9.number().int().min(1),
19058
- colorCount: z9.number().int().min(0),
19059
- colors: z9.array(z9.string().regex(HEX_RE)),
19060
- transparency: z9.number().min(0).max(1),
19061
- partialAlpha: z9.number().min(0).max(1),
19062
- edgeDensity: z9.number().min(0).max(1),
19063
- meanEdgeContrast: z9.number().min(0).max(1),
19064
- isolatedPixelRatio: z9.number().min(0).max(1)
19476
+ var ImageQualityMetricsSchema = z10.object({
19477
+ sha256: z10.string().regex(SHA256_RE),
19478
+ width: z10.number().int().min(1),
19479
+ height: z10.number().int().min(1),
19480
+ colorCount: z10.number().int().min(0),
19481
+ colors: z10.array(z10.string().regex(HEX_RE)),
19482
+ transparency: z10.number().min(0).max(1),
19483
+ partialAlpha: z10.number().min(0).max(1),
19484
+ edgeDensity: z10.number().min(0).max(1),
19485
+ meanEdgeContrast: z10.number().min(0).max(1),
19486
+ isolatedPixelRatio: z10.number().min(0).max(1)
19065
19487
  }).strict();
19066
- var QualityBaselineCaseSchema = z9.object({
19067
- id: z9.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
19488
+ var QualityBaselineCaseSchema = z10.object({
19489
+ id: z10.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
19068
19490
  file: QualityPathSchema,
19069
- record: z9.object({
19491
+ record: z10.object({
19070
19492
  path: QualityPathSchema,
19071
- sha256: z9.string().regex(SHA256_RE)
19493
+ sha256: z10.string().regex(SHA256_RE)
19072
19494
  }).strict().optional(),
19073
19495
  expected: ImageQualityMetricsSchema,
19074
19496
  tolerances: QualityToleranceSchema
19075
19497
  }).strict();
19076
- var QualityBaselineSchema = z9.object({
19077
- $schema: z9.string().url().optional(),
19078
- format: z9.literal("pixelkiln-quality-baseline"),
19079
- schemaVersion: z9.literal(1),
19080
- cases: z9.array(QualityBaselineCaseSchema).min(1)
19498
+ var QualityBaselineSchema = z10.object({
19499
+ $schema: z10.string().url().optional(),
19500
+ format: z10.literal("pixelkiln-quality-baseline"),
19501
+ schemaVersion: z10.literal(1),
19502
+ cases: z10.array(QualityBaselineCaseSchema).min(1)
19081
19503
  }).strict().superRefine((baseline, context) => {
19082
19504
  const ids = baseline.cases.map((entry) => entry.id);
19083
19505
  if (new Set(ids).size !== ids.length) {
19084
- context.addIssue({ code: z9.ZodIssueCode.custom, path: ["cases"], message: "duplicate case id" });
19506
+ context.addIssue({ code: z10.ZodIssueCode.custom, path: ["cases"], message: "duplicate case id" });
19085
19507
  }
19086
19508
  });
19087
- var QualityInputSchema = z9.object({
19088
- id: z9.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
19089
- path: z9.string().min(1),
19090
- record: z9.string().min(1).optional(),
19509
+ var QualityInputSchema = z10.object({
19510
+ id: z10.string().regex(/^[a-z0-9][a-z0-9/_-]*$/),
19511
+ path: z10.string().min(1),
19512
+ record: z10.string().min(1).optional(),
19091
19513
  tolerances: QualityToleranceSchema.partial().optional()
19092
19514
  }).strict();
19093
19515
  function message4(error) {
@@ -19189,7 +19611,7 @@ async function measureImageQuality(file) {
19189
19611
  }
19190
19612
  }
19191
19613
  function resolveQualityInputs(raw, inputsFilePath) {
19192
- const parsed = z9.array(QualityInputSchema).min(1).safeParse(raw);
19614
+ const parsed = z10.array(QualityInputSchema).min(1).safeParse(raw);
19193
19615
  if (!parsed.success) {
19194
19616
  throw new Error(
19195
19617
  "--inputs must be a non-empty JSON array of quality cases:\n" + parsed.error.issues.map((issue) => ` ${issue.path.join(".") || "$"}: ${issue.message}`).join("\n")
@@ -19464,47 +19886,47 @@ import path43 from "path";
19464
19886
  import { existsSync as existsSync31 } from "fs";
19465
19887
  import { readdir as readdir3, readFile as readFile29 } from "fs/promises";
19466
19888
  import { fileURLToPath } from "url";
19467
- import { z as z10 } from "zod";
19889
+ import { z as z11 } from "zod";
19468
19890
  var SHA256_RE2 = /^[0-9a-f]{64}$/;
19469
19891
  var RECIPE_ID_RE = /^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/;
19470
19892
  var VERSION_RE = /^\d+\.\d+\.\d+$/;
19471
19893
  var RECIPE_FILE = "pixelkiln.recipe.json";
19472
19894
  var RECIPE_DIR_TOKEN = "{{recipeDir}}";
19473
- var PortablePathSchema = z10.string().min(1).refine((value) => {
19895
+ var PortablePathSchema = z11.string().min(1).refine((value) => {
19474
19896
  if (value.includes("\\") || path43.posix.isAbsolute(value)) return false;
19475
19897
  const parts = value.split("/");
19476
19898
  return !parts.includes("") && !parts.includes(".") && !parts.includes("..");
19477
19899
  }, "expected a portable relative path without '.', '..', or backslashes");
19478
- var RecipeBindingSchema = z10.object({
19479
- nodeId: z10.string().min(1),
19480
- input: z10.string().min(1)
19900
+ var RecipeBindingSchema = z11.object({
19901
+ nodeId: z11.string().min(1),
19902
+ input: z11.string().min(1)
19481
19903
  }).strict();
19482
- var RecipeSchema = z10.object({
19483
- $schema: z10.string().url().optional(),
19484
- format: z10.literal("pixelkiln-recipe"),
19485
- schemaVersion: z10.literal(1),
19486
- id: z10.string().regex(RECIPE_ID_RE, "expected provider/name"),
19487
- version: z10.string().regex(VERSION_RE, "expected x.y.z"),
19488
- provider: z10.string().regex(/^[a-z0-9][a-z0-9-]*$/),
19489
- summary: z10.string().min(1).max(240),
19490
- files: z10.array(z10.object({
19904
+ var RecipeSchema = z11.object({
19905
+ $schema: z11.string().url().optional(),
19906
+ format: z11.literal("pixelkiln-recipe"),
19907
+ schemaVersion: z11.literal(1),
19908
+ id: z11.string().regex(RECIPE_ID_RE, "expected provider/name"),
19909
+ version: z11.string().regex(VERSION_RE, "expected x.y.z"),
19910
+ provider: z11.string().regex(/^[a-z0-9][a-z0-9-]*$/),
19911
+ summary: z11.string().min(1).max(240),
19912
+ files: z11.array(z11.object({
19491
19913
  path: PortablePathSchema,
19492
- role: z10.enum(["workflow", "reference"]),
19493
- sha256: z10.string().regex(SHA256_RE2)
19914
+ role: z11.enum(["workflow", "reference"]),
19915
+ sha256: z11.string().regex(SHA256_RE2)
19494
19916
  }).strict()).default([]),
19495
- models: z10.array(z10.object({
19917
+ models: z11.array(z11.object({
19496
19918
  path: PortablePathSchema,
19497
- sha256: z10.string().regex(SHA256_RE2),
19498
- source: z10.string().url(),
19499
- license: z10.string().min(1)
19919
+ sha256: z11.string().regex(SHA256_RE2),
19920
+ source: z11.string().url(),
19921
+ license: z11.string().min(1)
19500
19922
  }).strict()).default([]),
19501
- styleId: z10.string().regex(/^[a-z0-9][a-z0-9-]*$/),
19923
+ styleId: z11.string().regex(/^[a-z0-9][a-z0-9-]*$/),
19502
19924
  style: StyleSchema,
19503
- workflow: z10.object({
19925
+ workflow: z11.object({
19504
19926
  path: PortablePathSchema,
19505
- outputNodeId: z10.string().min(1),
19506
- numImages: z10.number().int().min(1).max(16),
19507
- bindings: z10.object({
19927
+ outputNodeId: z11.string().min(1),
19928
+ numImages: z11.number().int().min(1).max(16),
19929
+ bindings: z11.object({
19508
19930
  prompt: RecipeBindingSchema,
19509
19931
  width: RecipeBindingSchema.optional(),
19510
19932
  height: RecipeBindingSchema.optional(),
@@ -19515,67 +19937,67 @@ var RecipeSchema = z10.object({
19515
19937
  strength: RecipeBindingSchema.optional()
19516
19938
  }).strict()
19517
19939
  }).strict().optional(),
19518
- quality: z10.object({
19519
- stage: z10.enum(["composition-source", "production-candidate"]),
19520
- workingCanvas: z10.object({
19521
- width: z10.number().int().min(16),
19522
- height: z10.number().int().min(16)
19940
+ quality: z11.object({
19941
+ stage: z11.enum(["composition-source", "production-candidate"]),
19942
+ workingCanvas: z11.object({
19943
+ width: z11.number().int().min(16),
19944
+ height: z11.number().int().min(16)
19523
19945
  }).strict(),
19524
- recommendedNativeSize: z10.object({
19525
- min: z10.number().int().min(1),
19526
- max: z10.number().int().min(1)
19946
+ recommendedNativeSize: z11.object({
19947
+ min: z11.number().int().min(1),
19948
+ max: z11.number().int().min(1)
19527
19949
  }).strict(),
19528
- paletteColors: z10.object({
19529
- min: z10.number().int().min(1),
19530
- max: z10.number().int().min(1)
19950
+ paletteColors: z11.object({
19951
+ min: z11.number().int().min(1),
19952
+ max: z11.number().int().min(1)
19531
19953
  }).strict(),
19532
- background: z10.enum(["opaque", "transparent", "full-bleed"]),
19533
- checks: z10.array(z10.enum([
19954
+ background: z11.enum(["opaque", "transparent", "full-bleed"]),
19955
+ checks: z11.array(z11.enum([
19534
19956
  "prompt-coverage",
19535
19957
  "native-grid",
19536
19958
  "final-palette",
19537
19959
  "alpha-edge",
19538
19960
  "human-1x"
19539
19961
  ])).min(1),
19540
- notes: z10.array(z10.string().min(1)).default([])
19962
+ notes: z11.array(z11.string().min(1)).default([])
19541
19963
  }).strict(),
19542
- integrity: z10.object({
19543
- algorithm: z10.literal("sha256"),
19544
- digest: z10.string().regex(SHA256_RE2)
19964
+ integrity: z11.object({
19965
+ algorithm: z11.literal("sha256"),
19966
+ digest: z11.string().regex(SHA256_RE2)
19545
19967
  }).strict()
19546
19968
  }).strict().superRefine((recipe, context) => {
19547
19969
  if (recipe.style.provider && recipe.style.provider !== recipe.provider) {
19548
19970
  context.addIssue({
19549
- code: z10.ZodIssueCode.custom,
19971
+ code: z11.ZodIssueCode.custom,
19550
19972
  path: ["style", "provider"],
19551
19973
  message: "must match the recipe provider"
19552
19974
  });
19553
19975
  }
19554
19976
  const filePaths = recipe.files.map((file) => file.path);
19555
19977
  if (new Set(filePaths).size !== filePaths.length) {
19556
- context.addIssue({ code: z10.ZodIssueCode.custom, path: ["files"], message: "duplicate file path" });
19978
+ context.addIssue({ code: z11.ZodIssueCode.custom, path: ["files"], message: "duplicate file path" });
19557
19979
  }
19558
19980
  const modelPaths = recipe.models.map((model) => model.path);
19559
19981
  if (new Set(modelPaths).size !== modelPaths.length) {
19560
- context.addIssue({ code: z10.ZodIssueCode.custom, path: ["models"], message: "duplicate model path" });
19982
+ context.addIssue({ code: z11.ZodIssueCode.custom, path: ["models"], message: "duplicate model path" });
19561
19983
  }
19562
19984
  if (filePaths.includes(RECIPE_FILE)) {
19563
19985
  context.addIssue({
19564
- code: z10.ZodIssueCode.custom,
19986
+ code: z11.ZodIssueCode.custom,
19565
19987
  path: ["files"],
19566
19988
  message: `${RECIPE_FILE} cannot hash itself`
19567
19989
  });
19568
19990
  }
19569
19991
  if (recipe.quality.recommendedNativeSize.min > recipe.quality.recommendedNativeSize.max) {
19570
19992
  context.addIssue({
19571
- code: z10.ZodIssueCode.custom,
19993
+ code: z11.ZodIssueCode.custom,
19572
19994
  path: ["quality", "recommendedNativeSize"],
19573
19995
  message: "min cannot exceed max"
19574
19996
  });
19575
19997
  }
19576
19998
  if (recipe.quality.paletteColors.min > recipe.quality.paletteColors.max) {
19577
19999
  context.addIssue({
19578
- code: z10.ZodIssueCode.custom,
20000
+ code: z11.ZodIssueCode.custom,
19579
20001
  path: ["quality", "paletteColors"],
19580
20002
  message: "min cannot exceed max"
19581
20003
  });
@@ -19584,7 +20006,7 @@ var RecipeSchema = z10.object({
19584
20006
  (file) => file.path === recipe.workflow.path && file.role === "workflow"
19585
20007
  )) {
19586
20008
  context.addIssue({
19587
- code: z10.ZodIssueCode.custom,
20009
+ code: z11.ZodIssueCode.custom,
19588
20010
  path: ["workflow", "path"],
19589
20011
  message: "must name a file whose role is workflow"
19590
20012
  });
@@ -19592,7 +20014,7 @@ var RecipeSchema = z10.object({
19592
20014
  if (recipe.provider === "comfyui") {
19593
20015
  if (!recipe.workflow) {
19594
20016
  context.addIssue({
19595
- code: z10.ZodIssueCode.custom,
20017
+ code: z11.ZodIssueCode.custom,
19596
20018
  path: ["workflow"],
19597
20019
  message: "ComfyUI recipes require workflow metadata"
19598
20020
  });
@@ -19601,7 +20023,7 @@ var RecipeSchema = z10.object({
19601
20023
  const options = recipe.style.providerOptions.comfyui;
19602
20024
  if (!options || typeof options !== "object" || Array.isArray(options)) {
19603
20025
  context.addIssue({
19604
- code: z10.ZodIssueCode.custom,
20026
+ code: z11.ZodIssueCode.custom,
19605
20027
  path: ["style", "providerOptions", "comfyui"],
19606
20028
  message: "ComfyUI recipes require ComfyUI provider options"
19607
20029
  });
@@ -19615,7 +20037,7 @@ var RecipeSchema = z10.object({
19615
20037
  };
19616
20038
  if (JSON.stringify(canonical3(options)) !== JSON.stringify(canonical3(expected))) {
19617
20039
  context.addIssue({
19618
- code: z10.ZodIssueCode.custom,
20040
+ code: z11.ZodIssueCode.custom,
19619
20041
  path: ["style", "providerOptions", "comfyui"],
19620
20042
  message: "must match workflow metadata and use {{recipeDir}} for workflowFile"
19621
20043
  });
@@ -20034,6 +20456,30 @@ async function runRefine(args) {
20034
20456
  if (result.failed) process.exitCode = 1;
20035
20457
  }
20036
20458
 
20459
+ // src/cli/commands/skeleton.ts
20460
+ import path46 from "path";
20461
+ import { readFile as readFile30, writeFile as writeFile15 } from "fs/promises";
20462
+ async function runEstimateSkeleton(args) {
20463
+ const image = args.target;
20464
+ if (!image) throw new UsageError("estimate-skeleton needs an image path: pixelkiln estimate-skeleton <image> [--out keypoints.json]");
20465
+ loadEnvFiles(path46.dirname(path46.resolve(args.manifest)));
20466
+ loadEnvFiles(process.cwd());
20467
+ const bytes = await readFile30(path46.resolve(image));
20468
+ const metadata = imageMetadata(bytes);
20469
+ if (!metadata) throw new UsageError(`${image} is not a readable PNG or JPEG`);
20470
+ const client2 = clientFromEnv();
20471
+ const res = await client2.estimateSkeleton({
20472
+ image: { base64: bytes.toString("base64"), format: metadata.format }
20473
+ });
20474
+ const json = JSON.stringify(res.keypoints, null, 2);
20475
+ if (args.out) {
20476
+ await writeFile15(path46.resolve(args.out), json + "\n");
20477
+ log(` wrote ${res.keypoints.length} keypoints to ${args.out}`);
20478
+ } else {
20479
+ log(json);
20480
+ }
20481
+ }
20482
+
20037
20483
  // src/cli/commands/tools.ts
20038
20484
  async function runTools(args) {
20039
20485
  const mb = (bytes) => bytes < 1e6 ? `${(bytes / 1e3).toFixed(0)} kB` : `${(bytes / 1e6).toFixed(1)} MB`;
@@ -20076,15 +20522,15 @@ async function runTools(args) {
20076
20522
  }
20077
20523
 
20078
20524
  // src/cli/commands/workspace.ts
20079
- import path46 from "path";
20525
+ import path47 from "path";
20080
20526
  import { existsSync as existsSync32 } from "fs";
20081
20527
  async function runWorkspace(args) {
20082
- const workspacePath = path46.resolve(args.workspace ?? "pixelkiln.workspace.json");
20083
- const dir = path46.dirname(workspacePath);
20528
+ const workspacePath = path47.resolve(args.workspace ?? "pixelkiln.workspace.json");
20529
+ const dir = path47.dirname(workspacePath);
20084
20530
  if (args.subcommand === "add") {
20085
- const manifestPath = path46.resolve(args.target);
20531
+ const manifestPath = path47.resolve(args.target);
20086
20532
  const loadedTarget = await loadManifest(manifestPath);
20087
- const lockPath = args.explicitLock ? path46.resolve(args.explicitLock) : path46.join(path46.dirname(manifestPath), "pixelkiln.lock.json");
20533
+ const lockPath = args.explicitLock ? path47.resolve(args.explicitLock) : path47.join(path47.dirname(manifestPath), "pixelkiln.lock.json");
20088
20534
  const ws = await loadWorkspace(workspacePath);
20089
20535
  const id = args.name ?? loadedTarget.manifest.name;
20090
20536
  if (ws.projects.some((p) => p.id === id)) {
@@ -20104,7 +20550,7 @@ async function runWorkspace(args) {
20104
20550
  ...args.account ? { account: args.account } : {}
20105
20551
  };
20106
20552
  await saveWorkspace(workspacePath, { version: 1, projects: [...ws.projects, project] });
20107
- log(` registered "${id}" in ${path46.relative(process.cwd(), workspacePath)}`);
20553
+ log(` registered "${id}" in ${path47.relative(process.cwd(), workspacePath)}`);
20108
20554
  log(` manifest: ${project.manifest}`);
20109
20555
  log(` lock: ${project.lock}`);
20110
20556
  if (!existsSync32(lockPath)) {
@@ -20116,7 +20562,7 @@ async function runWorkspace(args) {
20116
20562
  }
20117
20563
  if (args.subcommand === "remove") {
20118
20564
  const ws = await loadWorkspace(workspacePath);
20119
- const resolvedTarget = path46.resolve(args.target);
20565
+ const resolvedTarget = path47.resolve(args.target);
20120
20566
  const match = ws.projects.find(
20121
20567
  (p) => p.id === args.target || resolveProject(dir, p).manifestPath === resolvedTarget
20122
20568
  );
@@ -20127,7 +20573,7 @@ async function runWorkspace(args) {
20127
20573
  version: 1,
20128
20574
  projects: ws.projects.filter((p) => p !== match)
20129
20575
  });
20130
- log(` removed "${match.id}" from ${path46.relative(process.cwd(), workspacePath)}`);
20576
+ log(` removed "${match.id}" from ${path47.relative(process.cwd(), workspacePath)}`);
20131
20577
  return;
20132
20578
  }
20133
20579
  if (args.subcommand === "list") {
@@ -20139,9 +20585,9 @@ async function runWorkspace(args) {
20139
20585
  if (args.json) {
20140
20586
  log(JSON.stringify({ version: 1, workspace: workspacePath, projects: ws.projects, diagnostics }, null, 2));
20141
20587
  } else if (!ws.projects.length) {
20142
- log(` no projects registered in ${path46.relative(process.cwd(), workspacePath)}`);
20588
+ log(` no projects registered in ${path47.relative(process.cwd(), workspacePath)}`);
20143
20589
  } else {
20144
- log(` ${ws.projects.length} project(s) in ${path46.relative(process.cwd(), workspacePath)}:`);
20590
+ log(` ${ws.projects.length} project(s) in ${path47.relative(process.cwd(), workspacePath)}:`);
20145
20591
  for (const p of ws.projects) {
20146
20592
  log(` ${p.id.padEnd(24)} ${p.manifest.padEnd(40)} (${p.provider}${p.account ? `, ${p.account}` : ""})`);
20147
20593
  }
@@ -20159,7 +20605,7 @@ async function runWorkspace(args) {
20159
20605
  if (args.json) {
20160
20606
  log(JSON.stringify({ ...report, workspace: workspacePath }, null, 2));
20161
20607
  } else {
20162
- log(` workspace: ${path46.relative(process.cwd(), workspacePath)}`);
20608
+ log(` workspace: ${path47.relative(process.cwd(), workspacePath)}`);
20163
20609
  for (const p of report.projects) {
20164
20610
  if (p.error) {
20165
20611
  log(`
@@ -20214,7 +20660,7 @@ async function runVersion() {
20214
20660
  for (const up of ["../package.json", "../../package.json"]) {
20215
20661
  const url = new URL(up, import.meta.url);
20216
20662
  if (!existsSync33(url)) continue;
20217
- const pkg = JSON.parse(await readFile30(url, "utf8"));
20663
+ const pkg = JSON.parse(await readFile31(url, "utf8"));
20218
20664
  if (pkg.name === "pixelkiln") {
20219
20665
  log(`${pkg.name} ${pkg.version}`);
20220
20666
  return;
@@ -20258,6 +20704,7 @@ var RUNNERS = {
20258
20704
  refine: runRefine,
20259
20705
  recipe: runRecipe,
20260
20706
  workspace: runWorkspace,
20707
+ "estimate-skeleton": runEstimateSkeleton,
20261
20708
  unzoom: runUnzoom,
20262
20709
  font: runFont
20263
20710
  };