pixelkiln 0.1.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.
@@ -0,0 +1,2679 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Which PixelLab endpoint produces the asset. The choice is mostly about cost,
5
+ * and the gap is enormous — all figures measured against a live account.
6
+ *
7
+ * map POST /map-objects — THE DEFAULT.
8
+ * A flat 1 generation at any size. Purpose-built for standalone props
9
+ * with transparent backgrounds, which is what an icon or a prop is.
10
+ * Arbitrary width x height. Returns exactly one result, so there is no
11
+ * selection step: if you dislike it, re-roll for 1 more.
12
+ *
13
+ * 1dir POST /create-1-direction-object — 20-40 generations.
14
+ * The single-facing sibling of create-8-direction-object, meant for
15
+ * objects you may later want rotations or animations of. Square only.
16
+ * Returns 4-64 candidates for its one fixed price, which is genuinely
17
+ * useful when you want to compare options side by side — but at 40x the
18
+ * cost of a map object, re-rolling a map object forty times is the same
19
+ * money. Reach for this when you need rotations, or when the extra
20
+ * rendering detail is worth 40x.
21
+ *
22
+ * Rule of thumb: if the asset is a standalone image and you are not going to
23
+ * animate or rotate it, `map` is the right call. Generating 65 icons costs 65
24
+ * generations that way and 2,600 the other.
25
+ *
26
+ * Not yet implemented, but measured and worth knowing (see README):
27
+ * POST /create-image-pixflux also costs 1 generation, returns the image
28
+ * INLINE with no polling, and accepts `color_image` — a forced palette that
29
+ * constrains output to exact hex values. Verified: a four-colour Game Boy
30
+ * swatch produced output containing precisely those four colours. The same
31
+ * parameter on /map-objects returns a 500, so the palette lock is
32
+ * pixflux-only. Its rendering is flatter than 1dir's.
33
+ */
34
+ declare const GeneratorSchema: z.ZodEnum<["1dir", "map", "pixflux", "tiles"]>;
35
+ type Generator = z.infer<typeof GeneratorSchema>;
36
+ /** A decoded style reference ready for a provider-specific request body. */
37
+ interface ResolvedStyleImage {
38
+ base64: string;
39
+ width: number;
40
+ height: number;
41
+ format: "png" | "jpeg";
42
+ }
43
+ /**
44
+ * `tiles` is the odd one out: the unit of work is a *set*, not a sprite.
45
+ *
46
+ * POST /create-tiles-pro draws tile shape outlines and fills them, returning
47
+ * many variations from one call. Independent variations land in the same
48
+ * review-then-pick flow as `1dir`; connectable features are structural sets
49
+ * and every returned storage URL is retained. There is no select-frames step
50
+ * to run for either form.
51
+ *
52
+ * Two properties make it worth a generator of its own rather than a flag on
53
+ * `1dir`:
54
+ *
55
+ * - **Style mode overrides geometry.** Passing `styleImages` makes the API
56
+ * copy tile shape and dimensions from the reference and ignore
57
+ * `tileType`/`tileView` entirely. For an existing tileset that is the
58
+ * point: it is the only way to land new art on the same ground plane as
59
+ * the tiles already in the sheet.
60
+ * - **`tileFeature` generates connectable sets.** `"tileset"` returns a
61
+ * 16-tile Wang corner set for a terrain transition, `"roads"` an
62
+ * 18-configuration path set. Those are structural outputs; a consumer
63
+ * slices them by index, not by eye.
64
+ */
65
+ /**
66
+ * Variations a `tiles` call returns. The API derives this from the tile size
67
+ * and how many numbered descriptions the prompt contains; observed at 4 per
68
+ * numbered description for 32px isometric tiles. Clamped so a prompt with no
69
+ * numbering still reports at least one.
70
+ */
71
+ declare function tileVariationCount(descriptions: number): number;
72
+ /** Known structural output counts for connectable tile features. */
73
+ declare function tileFeatureOutputCount(feature: string | undefined): number | null;
74
+ /**
75
+ * Numbered items in a `tiles` prompt. The endpoint documents `"1). grass
76
+ * 2). dirt"` as the way to control what comes back, and returns a group of
77
+ * variations per number.
78
+ */
79
+ declare function countNumberedDescriptions(prompt: string): number;
80
+ /** Candidate frames returned per call, derived from size. Extra candidates are free. */
81
+ declare function candidateCount(size: number): number;
82
+ /**
83
+ * Generation cost per call. Measured against a live account, not inferred.
84
+ *
85
+ * The two generators are priced completely differently, and the gap is wide
86
+ * enough to change which one you should reach for:
87
+ *
88
+ * map FLAT 1 generation, any size. Verified: a 32x36 and a 64x96 map object
89
+ * each cost exactly 1 (balance 4751 → 4750 → 4749). Single result.
90
+ *
91
+ * 1dir 20-40 by canvas tier (1K=20, 2K=25, 4K=40), returning 4-64
92
+ * candidates for that one price.
93
+ *
94
+ * tiles 20-40 on the same canvas tiers as `1dir`, but the canvas is picked
95
+ * from tile size x variation count rather than a single sprite, so a
96
+ * small tile in a large set can still reach the top tier. Reported by
97
+ * the API at submit time; estimated here from the widest canvas the
98
+ * request can produce, which is the honest direction to be wrong in
99
+ * for a `--budget` check.
100
+ *
101
+ * So `1dir` buys candidate variety at 20-40x the price, and `map` buys
102
+ * arbitrary (non-square) dimensions nearly free. For a single-result asset,
103
+ * forty re-rolls of a map object cost the same as one 1dir call.
104
+ */
105
+ declare function generationCost(width: number, height: number, generator?: Generator): number;
106
+ /**
107
+ * Cost of one `tiles` call. Same canvas tiers as `1dir`, but the canvas is the
108
+ * sheet the API lays the variations out on, not one tile — so tile size alone
109
+ * under-reads it badly (a 32px tile is 1024px on its own and would always
110
+ * price at the floor).
111
+ *
112
+ * Estimated from tileSize^2 x variations, which is the area actually drawn.
113
+ * `plan` prints this before anything is spent and `--budget` refuses on it, so
114
+ * over-reading is the safe direction: a call that comes in cheaper than
115
+ * budgeted is a pleasant surprise, one that comes in dearer is an overspend.
116
+ */
117
+ declare function tilesCost(tileSize: number, variations: number): number;
118
+ declare const StyleSchema: z.ZodEffects<z.ZodObject<{
119
+ generator: z.ZodDefault<z.ZodEnum<["1dir", "map", "pixflux", "tiles"]>>;
120
+ /** Square edge length for `1dir`. 32-256. */
121
+ size: z.ZodOptional<z.ZodNumber>;
122
+ view: z.ZodOptional<z.ZodString>;
123
+ /** Appended to every asset prompt in this style. Where the look is defined. */
124
+ promptSuffix: z.ZodDefault<z.ZodString>;
125
+ /** Prepended to every asset prompt in this style. */
126
+ promptPrefix: z.ZodDefault<z.ZodString>;
127
+ /** Style reference images. */
128
+ styleImages: z.ZodDefault<z.ZodArray<z.ZodObject<{
129
+ /** Path to a PNG/JPEG, relative to the manifest file. Max 256x256. */
130
+ path: z.ZodString;
131
+ }, "strip", z.ZodTypeAny, {
132
+ path: string;
133
+ }, {
134
+ path: string;
135
+ }>, "many">>;
136
+ /** Output root for this style, relative to the manifest. */
137
+ outDir: z.ZodString;
138
+ /** `map` generator only. */
139
+ outline: z.ZodOptional<z.ZodString>;
140
+ shading: z.ZodOptional<z.ZodString>;
141
+ detail: z.ZodOptional<z.ZodString>;
142
+ /**
143
+ * `tiles` generator only. Edge length of one tile, 16-256.
144
+ *
145
+ * Ignored when `styleImages` is set — style mode takes the tile's shape
146
+ * and dimensions from the reference image, which is the whole reason to
147
+ * use it against an existing sheet.
148
+ */
149
+ tileSize: z.ZodOptional<z.ZodNumber>;
150
+ /** `tiles` generator only. Defaults to the API's `isometric`. */
151
+ tileType: z.ZodOptional<z.ZodEnum<["hex", "hex_pointy", "isometric", "oblique", "octagon", "square_topdown"]>>;
152
+ /** `tiles` generator only. Defaults to the API's `low top-down`. */
153
+ tileView: z.ZodOptional<z.ZodEnum<["top-down", "high top-down", "low top-down", "side"]>>;
154
+ /**
155
+ * `tiles` generator only. Asks for a connectable set instead of
156
+ * independent variations:
157
+ *
158
+ * roads 18-configuration path set
159
+ * tileset 16-tile Wang corner set for a terrain transition — describe
160
+ * the asset as the transition itself ("fairway grass to rough
161
+ * meadow"), not as one terrain
162
+ * building floor/wall/doorway construction kit
163
+ *
164
+ * A consumer slices these by index, so the order the API returns them in
165
+ * is load-bearing; do not sort a connectable set by anything else.
166
+ */
167
+ tileFeature: z.ZodOptional<z.ZodEnum<["roads", "tileset", "building"]>>;
168
+ /**
169
+ * `tiles` generator only. How tile edges are drawn.
170
+ *
171
+ * The API default is `outline`, which draws a dark border around every
172
+ * tile. That is right for tiles meant to read as discrete objects and
173
+ * wrong for ground: laid on a grid, the per-tile borders turn a continuous
174
+ * surface into visible quilting, with a dark seam at every cell edge.
175
+ * `segmentation` omits them and the same set tiles seamlessly.
176
+ *
177
+ * Measured on a fairway-to-rough terrain set — the difference decided
178
+ * whether the art was usable at all, so it is worth setting deliberately
179
+ * rather than inheriting.
180
+ */
181
+ outlineMode: z.ZodOptional<z.ZodEnum<["outline", "segmentation"]>>;
182
+ /**
183
+ * `pixflux` only. Whether to strip the generated background.
184
+ *
185
+ * Defaults to true, which is right for the sprites this tool was built
186
+ * for — a prop or an icon wants to sit on whatever is behind it. It is
187
+ * wrong for anything that IS a scene: a cover banner, a splash, a
188
+ * backdrop. The API's own default is false; forcing it true unconditionally
189
+ * meant a full-bleed image came back as a small subject floating in a
190
+ * mostly-empty frame, and prose asking for an "opaque background" does not
191
+ * override it.
192
+ */
193
+ noBackground: z.ZodDefault<z.ZodBoolean>;
194
+ /** Fixed seed for reproducibility where the endpoint supports it. */
195
+ seed: z.ZodOptional<z.ZodNumber>;
196
+ /**
197
+ * Forced palette, as `#rrggbb` values. `pixflux` only.
198
+ *
199
+ * Unlike prose, this is a hard constraint — the API is handed a swatch
200
+ * image and the output is limited to those colours. Verified: a four-colour
201
+ * Game Boy palette produced output containing exactly those four values.
202
+ * Prose asking for the same thing does not reliably hold, which is why the
203
+ * map-generated monochrome sets came back with a yellow star and a brown
204
+ * chocolate bar.
205
+ */
206
+ palette: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
207
+ /**
208
+ * Composites this style's assets into declared cells of a sheet, rather
209
+ * than letting `pack` derive a layout. Use it when the atlas coordinates
210
+ * are already load-bearing somewhere else — a tile engine naming tiles by
211
+ * cell, or saved data storing cell indices — since `pack`'s id-sorted grid
212
+ * moves every position when an asset is added or renamed.
213
+ *
214
+ * Assets in a mounted style declare their own `cell`; ones that do not are
215
+ * left out of the sheet.
216
+ */
217
+ mount: z.ZodOptional<z.ZodObject<{
218
+ /**
219
+ * Existing sheet to composite into, relative to the manifest. Every
220
+ * pixel outside a declared cell survives byte-for-byte, so a
221
+ * hand-authored sheet can be part generated and part drawn. Omit to
222
+ * start from transparent.
223
+ */
224
+ base: z.ZodOptional<z.ZodString>;
225
+ cellWidth: z.ZodNumber;
226
+ cellHeight: z.ZodNumber;
227
+ /** Where the composited sheet is written, relative to the manifest. */
228
+ out: z.ZodString;
229
+ }, "strict", z.ZodTypeAny, {
230
+ cellWidth: number;
231
+ cellHeight: number;
232
+ out: string;
233
+ base?: string | undefined;
234
+ }, {
235
+ cellWidth: number;
236
+ cellHeight: number;
237
+ out: string;
238
+ base?: string | undefined;
239
+ }>>;
240
+ /** Tags applied to every object generated in this style, for server-side filtering. */
241
+ tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
242
+ }, "strict", z.ZodTypeAny, {
243
+ generator: "1dir" | "map" | "pixflux" | "tiles";
244
+ promptSuffix: string;
245
+ promptPrefix: string;
246
+ styleImages: {
247
+ path: string;
248
+ }[];
249
+ outDir: string;
250
+ noBackground: boolean;
251
+ palette: string[];
252
+ tags: string[];
253
+ size?: number | undefined;
254
+ view?: string | undefined;
255
+ outline?: string | undefined;
256
+ shading?: string | undefined;
257
+ detail?: string | undefined;
258
+ tileSize?: number | undefined;
259
+ tileType?: "hex" | "hex_pointy" | "isometric" | "oblique" | "octagon" | "square_topdown" | undefined;
260
+ tileView?: "top-down" | "high top-down" | "low top-down" | "side" | undefined;
261
+ tileFeature?: "roads" | "tileset" | "building" | undefined;
262
+ outlineMode?: "outline" | "segmentation" | undefined;
263
+ seed?: number | undefined;
264
+ mount?: {
265
+ cellWidth: number;
266
+ cellHeight: number;
267
+ out: string;
268
+ base?: string | undefined;
269
+ } | undefined;
270
+ }, {
271
+ outDir: string;
272
+ generator?: "1dir" | "map" | "pixflux" | "tiles" | undefined;
273
+ size?: number | undefined;
274
+ view?: string | undefined;
275
+ promptSuffix?: string | undefined;
276
+ promptPrefix?: string | undefined;
277
+ styleImages?: {
278
+ path: string;
279
+ }[] | undefined;
280
+ outline?: string | undefined;
281
+ shading?: string | undefined;
282
+ detail?: string | undefined;
283
+ tileSize?: number | undefined;
284
+ tileType?: "hex" | "hex_pointy" | "isometric" | "oblique" | "octagon" | "square_topdown" | undefined;
285
+ tileView?: "top-down" | "high top-down" | "low top-down" | "side" | undefined;
286
+ tileFeature?: "roads" | "tileset" | "building" | undefined;
287
+ outlineMode?: "outline" | "segmentation" | undefined;
288
+ noBackground?: boolean | undefined;
289
+ seed?: number | undefined;
290
+ palette?: string[] | undefined;
291
+ mount?: {
292
+ cellWidth: number;
293
+ cellHeight: number;
294
+ out: string;
295
+ base?: string | undefined;
296
+ } | undefined;
297
+ tags?: string[] | undefined;
298
+ }>, {
299
+ generator: "1dir" | "map" | "pixflux" | "tiles";
300
+ promptSuffix: string;
301
+ promptPrefix: string;
302
+ styleImages: {
303
+ path: string;
304
+ }[];
305
+ outDir: string;
306
+ noBackground: boolean;
307
+ palette: string[];
308
+ tags: string[];
309
+ size?: number | undefined;
310
+ view?: string | undefined;
311
+ outline?: string | undefined;
312
+ shading?: string | undefined;
313
+ detail?: string | undefined;
314
+ tileSize?: number | undefined;
315
+ tileType?: "hex" | "hex_pointy" | "isometric" | "oblique" | "octagon" | "square_topdown" | undefined;
316
+ tileView?: "top-down" | "high top-down" | "low top-down" | "side" | undefined;
317
+ tileFeature?: "roads" | "tileset" | "building" | undefined;
318
+ outlineMode?: "outline" | "segmentation" | undefined;
319
+ seed?: number | undefined;
320
+ mount?: {
321
+ cellWidth: number;
322
+ cellHeight: number;
323
+ out: string;
324
+ base?: string | undefined;
325
+ } | undefined;
326
+ }, {
327
+ outDir: string;
328
+ generator?: "1dir" | "map" | "pixflux" | "tiles" | undefined;
329
+ size?: number | undefined;
330
+ view?: string | undefined;
331
+ promptSuffix?: string | undefined;
332
+ promptPrefix?: string | undefined;
333
+ styleImages?: {
334
+ path: string;
335
+ }[] | undefined;
336
+ outline?: string | undefined;
337
+ shading?: string | undefined;
338
+ detail?: string | undefined;
339
+ tileSize?: number | undefined;
340
+ tileType?: "hex" | "hex_pointy" | "isometric" | "oblique" | "octagon" | "square_topdown" | undefined;
341
+ tileView?: "top-down" | "high top-down" | "low top-down" | "side" | undefined;
342
+ tileFeature?: "roads" | "tileset" | "building" | undefined;
343
+ outlineMode?: "outline" | "segmentation" | undefined;
344
+ noBackground?: boolean | undefined;
345
+ seed?: number | undefined;
346
+ palette?: string[] | undefined;
347
+ mount?: {
348
+ cellWidth: number;
349
+ cellHeight: number;
350
+ out: string;
351
+ base?: string | undefined;
352
+ } | undefined;
353
+ tags?: string[] | undefined;
354
+ }>;
355
+ declare const AssetSchema: z.ZodObject<{
356
+ /** The subject. Style wrapping comes from the style's prefix/suffix. */
357
+ prompt: z.ZodString;
358
+ /** Subdirectory under the style's outDir. Optional. */
359
+ category: z.ZodOptional<z.ZodString>;
360
+ /** Overrides the style default. `map` generator only. */
361
+ width: z.ZodOptional<z.ZodNumber>;
362
+ height: z.ZodOptional<z.ZodNumber>;
363
+ /** Overrides the style default. `1dir` generator only. */
364
+ size: z.ZodOptional<z.ZodNumber>;
365
+ /** Explicit output path relative to outDir. Defaults to `<category>/<id>.png`. */
366
+ file: z.ZodOptional<z.ZodString>;
367
+ /**
368
+ * Grid cell this asset owns in a mounted style, as [column, row].
369
+ *
370
+ * Declared rather than derived, which is the point of `mount`: the
371
+ * coordinate is a contract with whatever already reads the sheet, so it
372
+ * must not move when the asset set changes. Assets without one are left
373
+ * out of the mounted sheet.
374
+ */
375
+ cell: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
376
+ /**
377
+ * Path, relative to the manifest, of the art that goes on a mounted
378
+ * sheet — when that is not the raw generated output.
379
+ *
380
+ * `mount` otherwise takes its pixels from the lockfile, which records
381
+ * what the API returned. That is the wrong file whenever the art needs a
382
+ * step pixelkiln does not perform: a palette reduction onto a sheet's
383
+ * closed palette, a hand touch-up, an alignment shift. Without somewhere
384
+ * to say so, the choice is to mount the unprocessed art or to abandon
385
+ * `mount` and composite by hand — and the hand-composited sheet is
386
+ * exactly the unreproducible artifact `mount` exists to replace.
387
+ *
388
+ * An asset with a `source` needs no lockfile entry at all, so art
389
+ * generated outside pixelkiln can still be placed by cell alongside art
390
+ * that wasn't. `prompt` still records what was asked for.
391
+ */
392
+ source: z.ZodOptional<z.ZodString>;
393
+ /**
394
+ * Generated output role to place in `cell` when this asset expands to
395
+ * several files. Omit for ordinary single-output assets. A structural set
396
+ * is otherwise ambiguous and `mount` refuses to guess by taking index zero.
397
+ */
398
+ outputRole: z.ZodOptional<z.ZodString>;
399
+ tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
400
+ /** Restrict this asset to specific styles. Empty means all styles. */
401
+ styles: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
402
+ /**
403
+ * Per-style replacement for `prompt`, keyed by style id.
404
+ *
405
+ * A style can need different wording rather than different styling. A
406
+ * monochrome style is the clear case: prompts that name colours
407
+ * ("green purple gold", "colorful rainbow") override the palette
408
+ * instruction and survive into the output, while the same prompt is
409
+ * exactly right for a colour style. Editing the shared prompt to suit one
410
+ * style would invalidate every other style's already-generated art.
411
+ */
412
+ promptByStyle: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
413
+ }, "strict", z.ZodTypeAny, {
414
+ tags: string[];
415
+ prompt: string;
416
+ styles: string[];
417
+ promptByStyle: Record<string, string>;
418
+ size?: number | undefined;
419
+ category?: string | undefined;
420
+ width?: number | undefined;
421
+ height?: number | undefined;
422
+ file?: string | undefined;
423
+ cell?: [number, number] | undefined;
424
+ source?: string | undefined;
425
+ outputRole?: string | undefined;
426
+ }, {
427
+ prompt: string;
428
+ size?: number | undefined;
429
+ tags?: string[] | undefined;
430
+ category?: string | undefined;
431
+ width?: number | undefined;
432
+ height?: number | undefined;
433
+ file?: string | undefined;
434
+ cell?: [number, number] | undefined;
435
+ source?: string | undefined;
436
+ outputRole?: string | undefined;
437
+ styles?: string[] | undefined;
438
+ promptByStyle?: Record<string, string> | undefined;
439
+ }>;
440
+ declare const ManifestSchema: z.ZodObject<{
441
+ $schema: z.ZodOptional<z.ZodString>;
442
+ name: z.ZodString;
443
+ styles: z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
444
+ generator: z.ZodDefault<z.ZodEnum<["1dir", "map", "pixflux", "tiles"]>>;
445
+ /** Square edge length for `1dir`. 32-256. */
446
+ size: z.ZodOptional<z.ZodNumber>;
447
+ view: z.ZodOptional<z.ZodString>;
448
+ /** Appended to every asset prompt in this style. Where the look is defined. */
449
+ promptSuffix: z.ZodDefault<z.ZodString>;
450
+ /** Prepended to every asset prompt in this style. */
451
+ promptPrefix: z.ZodDefault<z.ZodString>;
452
+ /** Style reference images. */
453
+ styleImages: z.ZodDefault<z.ZodArray<z.ZodObject<{
454
+ /** Path to a PNG/JPEG, relative to the manifest file. Max 256x256. */
455
+ path: z.ZodString;
456
+ }, "strip", z.ZodTypeAny, {
457
+ path: string;
458
+ }, {
459
+ path: string;
460
+ }>, "many">>;
461
+ /** Output root for this style, relative to the manifest. */
462
+ outDir: z.ZodString;
463
+ /** `map` generator only. */
464
+ outline: z.ZodOptional<z.ZodString>;
465
+ shading: z.ZodOptional<z.ZodString>;
466
+ detail: z.ZodOptional<z.ZodString>;
467
+ /**
468
+ * `tiles` generator only. Edge length of one tile, 16-256.
469
+ *
470
+ * Ignored when `styleImages` is set — style mode takes the tile's shape
471
+ * and dimensions from the reference image, which is the whole reason to
472
+ * use it against an existing sheet.
473
+ */
474
+ tileSize: z.ZodOptional<z.ZodNumber>;
475
+ /** `tiles` generator only. Defaults to the API's `isometric`. */
476
+ tileType: z.ZodOptional<z.ZodEnum<["hex", "hex_pointy", "isometric", "oblique", "octagon", "square_topdown"]>>;
477
+ /** `tiles` generator only. Defaults to the API's `low top-down`. */
478
+ tileView: z.ZodOptional<z.ZodEnum<["top-down", "high top-down", "low top-down", "side"]>>;
479
+ /**
480
+ * `tiles` generator only. Asks for a connectable set instead of
481
+ * independent variations:
482
+ *
483
+ * roads 18-configuration path set
484
+ * tileset 16-tile Wang corner set for a terrain transition — describe
485
+ * the asset as the transition itself ("fairway grass to rough
486
+ * meadow"), not as one terrain
487
+ * building floor/wall/doorway construction kit
488
+ *
489
+ * A consumer slices these by index, so the order the API returns them in
490
+ * is load-bearing; do not sort a connectable set by anything else.
491
+ */
492
+ tileFeature: z.ZodOptional<z.ZodEnum<["roads", "tileset", "building"]>>;
493
+ /**
494
+ * `tiles` generator only. How tile edges are drawn.
495
+ *
496
+ * The API default is `outline`, which draws a dark border around every
497
+ * tile. That is right for tiles meant to read as discrete objects and
498
+ * wrong for ground: laid on a grid, the per-tile borders turn a continuous
499
+ * surface into visible quilting, with a dark seam at every cell edge.
500
+ * `segmentation` omits them and the same set tiles seamlessly.
501
+ *
502
+ * Measured on a fairway-to-rough terrain set — the difference decided
503
+ * whether the art was usable at all, so it is worth setting deliberately
504
+ * rather than inheriting.
505
+ */
506
+ outlineMode: z.ZodOptional<z.ZodEnum<["outline", "segmentation"]>>;
507
+ /**
508
+ * `pixflux` only. Whether to strip the generated background.
509
+ *
510
+ * Defaults to true, which is right for the sprites this tool was built
511
+ * for — a prop or an icon wants to sit on whatever is behind it. It is
512
+ * wrong for anything that IS a scene: a cover banner, a splash, a
513
+ * backdrop. The API's own default is false; forcing it true unconditionally
514
+ * meant a full-bleed image came back as a small subject floating in a
515
+ * mostly-empty frame, and prose asking for an "opaque background" does not
516
+ * override it.
517
+ */
518
+ noBackground: z.ZodDefault<z.ZodBoolean>;
519
+ /** Fixed seed for reproducibility where the endpoint supports it. */
520
+ seed: z.ZodOptional<z.ZodNumber>;
521
+ /**
522
+ * Forced palette, as `#rrggbb` values. `pixflux` only.
523
+ *
524
+ * Unlike prose, this is a hard constraint — the API is handed a swatch
525
+ * image and the output is limited to those colours. Verified: a four-colour
526
+ * Game Boy palette produced output containing exactly those four values.
527
+ * Prose asking for the same thing does not reliably hold, which is why the
528
+ * map-generated monochrome sets came back with a yellow star and a brown
529
+ * chocolate bar.
530
+ */
531
+ palette: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
532
+ /**
533
+ * Composites this style's assets into declared cells of a sheet, rather
534
+ * than letting `pack` derive a layout. Use it when the atlas coordinates
535
+ * are already load-bearing somewhere else — a tile engine naming tiles by
536
+ * cell, or saved data storing cell indices — since `pack`'s id-sorted grid
537
+ * moves every position when an asset is added or renamed.
538
+ *
539
+ * Assets in a mounted style declare their own `cell`; ones that do not are
540
+ * left out of the sheet.
541
+ */
542
+ mount: z.ZodOptional<z.ZodObject<{
543
+ /**
544
+ * Existing sheet to composite into, relative to the manifest. Every
545
+ * pixel outside a declared cell survives byte-for-byte, so a
546
+ * hand-authored sheet can be part generated and part drawn. Omit to
547
+ * start from transparent.
548
+ */
549
+ base: z.ZodOptional<z.ZodString>;
550
+ cellWidth: z.ZodNumber;
551
+ cellHeight: z.ZodNumber;
552
+ /** Where the composited sheet is written, relative to the manifest. */
553
+ out: z.ZodString;
554
+ }, "strict", z.ZodTypeAny, {
555
+ cellWidth: number;
556
+ cellHeight: number;
557
+ out: string;
558
+ base?: string | undefined;
559
+ }, {
560
+ cellWidth: number;
561
+ cellHeight: number;
562
+ out: string;
563
+ base?: string | undefined;
564
+ }>>;
565
+ /** Tags applied to every object generated in this style, for server-side filtering. */
566
+ tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
567
+ }, "strict", z.ZodTypeAny, {
568
+ generator: "1dir" | "map" | "pixflux" | "tiles";
569
+ promptSuffix: string;
570
+ promptPrefix: string;
571
+ styleImages: {
572
+ path: string;
573
+ }[];
574
+ outDir: string;
575
+ noBackground: boolean;
576
+ palette: string[];
577
+ tags: string[];
578
+ size?: number | undefined;
579
+ view?: string | undefined;
580
+ outline?: string | undefined;
581
+ shading?: string | undefined;
582
+ detail?: string | undefined;
583
+ tileSize?: number | undefined;
584
+ tileType?: "hex" | "hex_pointy" | "isometric" | "oblique" | "octagon" | "square_topdown" | undefined;
585
+ tileView?: "top-down" | "high top-down" | "low top-down" | "side" | undefined;
586
+ tileFeature?: "roads" | "tileset" | "building" | undefined;
587
+ outlineMode?: "outline" | "segmentation" | undefined;
588
+ seed?: number | undefined;
589
+ mount?: {
590
+ cellWidth: number;
591
+ cellHeight: number;
592
+ out: string;
593
+ base?: string | undefined;
594
+ } | undefined;
595
+ }, {
596
+ outDir: string;
597
+ generator?: "1dir" | "map" | "pixflux" | "tiles" | undefined;
598
+ size?: number | undefined;
599
+ view?: string | undefined;
600
+ promptSuffix?: string | undefined;
601
+ promptPrefix?: string | undefined;
602
+ styleImages?: {
603
+ path: string;
604
+ }[] | undefined;
605
+ outline?: string | undefined;
606
+ shading?: string | undefined;
607
+ detail?: string | undefined;
608
+ tileSize?: number | undefined;
609
+ tileType?: "hex" | "hex_pointy" | "isometric" | "oblique" | "octagon" | "square_topdown" | undefined;
610
+ tileView?: "top-down" | "high top-down" | "low top-down" | "side" | undefined;
611
+ tileFeature?: "roads" | "tileset" | "building" | undefined;
612
+ outlineMode?: "outline" | "segmentation" | undefined;
613
+ noBackground?: boolean | undefined;
614
+ seed?: number | undefined;
615
+ palette?: string[] | undefined;
616
+ mount?: {
617
+ cellWidth: number;
618
+ cellHeight: number;
619
+ out: string;
620
+ base?: string | undefined;
621
+ } | undefined;
622
+ tags?: string[] | undefined;
623
+ }>, {
624
+ generator: "1dir" | "map" | "pixflux" | "tiles";
625
+ promptSuffix: string;
626
+ promptPrefix: string;
627
+ styleImages: {
628
+ path: string;
629
+ }[];
630
+ outDir: string;
631
+ noBackground: boolean;
632
+ palette: string[];
633
+ tags: string[];
634
+ size?: number | undefined;
635
+ view?: string | undefined;
636
+ outline?: string | undefined;
637
+ shading?: string | undefined;
638
+ detail?: string | undefined;
639
+ tileSize?: number | undefined;
640
+ tileType?: "hex" | "hex_pointy" | "isometric" | "oblique" | "octagon" | "square_topdown" | undefined;
641
+ tileView?: "top-down" | "high top-down" | "low top-down" | "side" | undefined;
642
+ tileFeature?: "roads" | "tileset" | "building" | undefined;
643
+ outlineMode?: "outline" | "segmentation" | undefined;
644
+ seed?: number | undefined;
645
+ mount?: {
646
+ cellWidth: number;
647
+ cellHeight: number;
648
+ out: string;
649
+ base?: string | undefined;
650
+ } | undefined;
651
+ }, {
652
+ outDir: string;
653
+ generator?: "1dir" | "map" | "pixflux" | "tiles" | undefined;
654
+ size?: number | undefined;
655
+ view?: string | undefined;
656
+ promptSuffix?: string | undefined;
657
+ promptPrefix?: string | undefined;
658
+ styleImages?: {
659
+ path: string;
660
+ }[] | undefined;
661
+ outline?: string | undefined;
662
+ shading?: string | undefined;
663
+ detail?: string | undefined;
664
+ tileSize?: number | undefined;
665
+ tileType?: "hex" | "hex_pointy" | "isometric" | "oblique" | "octagon" | "square_topdown" | undefined;
666
+ tileView?: "top-down" | "high top-down" | "low top-down" | "side" | undefined;
667
+ tileFeature?: "roads" | "tileset" | "building" | undefined;
668
+ outlineMode?: "outline" | "segmentation" | undefined;
669
+ noBackground?: boolean | undefined;
670
+ seed?: number | undefined;
671
+ palette?: string[] | undefined;
672
+ mount?: {
673
+ cellWidth: number;
674
+ cellHeight: number;
675
+ out: string;
676
+ base?: string | undefined;
677
+ } | undefined;
678
+ tags?: string[] | undefined;
679
+ }>>;
680
+ assets: z.ZodRecord<z.ZodString, z.ZodObject<{
681
+ /** The subject. Style wrapping comes from the style's prefix/suffix. */
682
+ prompt: z.ZodString;
683
+ /** Subdirectory under the style's outDir. Optional. */
684
+ category: z.ZodOptional<z.ZodString>;
685
+ /** Overrides the style default. `map` generator only. */
686
+ width: z.ZodOptional<z.ZodNumber>;
687
+ height: z.ZodOptional<z.ZodNumber>;
688
+ /** Overrides the style default. `1dir` generator only. */
689
+ size: z.ZodOptional<z.ZodNumber>;
690
+ /** Explicit output path relative to outDir. Defaults to `<category>/<id>.png`. */
691
+ file: z.ZodOptional<z.ZodString>;
692
+ /**
693
+ * Grid cell this asset owns in a mounted style, as [column, row].
694
+ *
695
+ * Declared rather than derived, which is the point of `mount`: the
696
+ * coordinate is a contract with whatever already reads the sheet, so it
697
+ * must not move when the asset set changes. Assets without one are left
698
+ * out of the mounted sheet.
699
+ */
700
+ cell: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
701
+ /**
702
+ * Path, relative to the manifest, of the art that goes on a mounted
703
+ * sheet — when that is not the raw generated output.
704
+ *
705
+ * `mount` otherwise takes its pixels from the lockfile, which records
706
+ * what the API returned. That is the wrong file whenever the art needs a
707
+ * step pixelkiln does not perform: a palette reduction onto a sheet's
708
+ * closed palette, a hand touch-up, an alignment shift. Without somewhere
709
+ * to say so, the choice is to mount the unprocessed art or to abandon
710
+ * `mount` and composite by hand — and the hand-composited sheet is
711
+ * exactly the unreproducible artifact `mount` exists to replace.
712
+ *
713
+ * An asset with a `source` needs no lockfile entry at all, so art
714
+ * generated outside pixelkiln can still be placed by cell alongside art
715
+ * that wasn't. `prompt` still records what was asked for.
716
+ */
717
+ source: z.ZodOptional<z.ZodString>;
718
+ /**
719
+ * Generated output role to place in `cell` when this asset expands to
720
+ * several files. Omit for ordinary single-output assets. A structural set
721
+ * is otherwise ambiguous and `mount` refuses to guess by taking index zero.
722
+ */
723
+ outputRole: z.ZodOptional<z.ZodString>;
724
+ tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
725
+ /** Restrict this asset to specific styles. Empty means all styles. */
726
+ styles: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
727
+ /**
728
+ * Per-style replacement for `prompt`, keyed by style id.
729
+ *
730
+ * A style can need different wording rather than different styling. A
731
+ * monochrome style is the clear case: prompts that name colours
732
+ * ("green purple gold", "colorful rainbow") override the palette
733
+ * instruction and survive into the output, while the same prompt is
734
+ * exactly right for a colour style. Editing the shared prompt to suit one
735
+ * style would invalidate every other style's already-generated art.
736
+ */
737
+ promptByStyle: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
738
+ }, "strict", z.ZodTypeAny, {
739
+ tags: string[];
740
+ prompt: string;
741
+ styles: string[];
742
+ promptByStyle: Record<string, string>;
743
+ size?: number | undefined;
744
+ category?: string | undefined;
745
+ width?: number | undefined;
746
+ height?: number | undefined;
747
+ file?: string | undefined;
748
+ cell?: [number, number] | undefined;
749
+ source?: string | undefined;
750
+ outputRole?: string | undefined;
751
+ }, {
752
+ prompt: string;
753
+ size?: number | undefined;
754
+ tags?: string[] | undefined;
755
+ category?: string | undefined;
756
+ width?: number | undefined;
757
+ height?: number | undefined;
758
+ file?: string | undefined;
759
+ cell?: [number, number] | undefined;
760
+ source?: string | undefined;
761
+ outputRole?: string | undefined;
762
+ styles?: string[] | undefined;
763
+ promptByStyle?: Record<string, string> | undefined;
764
+ }>>;
765
+ }, "strict", z.ZodTypeAny, {
766
+ styles: Record<string, {
767
+ generator: "1dir" | "map" | "pixflux" | "tiles";
768
+ promptSuffix: string;
769
+ promptPrefix: string;
770
+ styleImages: {
771
+ path: string;
772
+ }[];
773
+ outDir: string;
774
+ noBackground: boolean;
775
+ palette: string[];
776
+ tags: string[];
777
+ size?: number | undefined;
778
+ view?: string | undefined;
779
+ outline?: string | undefined;
780
+ shading?: string | undefined;
781
+ detail?: string | undefined;
782
+ tileSize?: number | undefined;
783
+ tileType?: "hex" | "hex_pointy" | "isometric" | "oblique" | "octagon" | "square_topdown" | undefined;
784
+ tileView?: "top-down" | "high top-down" | "low top-down" | "side" | undefined;
785
+ tileFeature?: "roads" | "tileset" | "building" | undefined;
786
+ outlineMode?: "outline" | "segmentation" | undefined;
787
+ seed?: number | undefined;
788
+ mount?: {
789
+ cellWidth: number;
790
+ cellHeight: number;
791
+ out: string;
792
+ base?: string | undefined;
793
+ } | undefined;
794
+ }>;
795
+ name: string;
796
+ assets: Record<string, {
797
+ tags: string[];
798
+ prompt: string;
799
+ styles: string[];
800
+ promptByStyle: Record<string, string>;
801
+ size?: number | undefined;
802
+ category?: string | undefined;
803
+ width?: number | undefined;
804
+ height?: number | undefined;
805
+ file?: string | undefined;
806
+ cell?: [number, number] | undefined;
807
+ source?: string | undefined;
808
+ outputRole?: string | undefined;
809
+ }>;
810
+ $schema?: string | undefined;
811
+ }, {
812
+ styles: Record<string, {
813
+ outDir: string;
814
+ generator?: "1dir" | "map" | "pixflux" | "tiles" | undefined;
815
+ size?: number | undefined;
816
+ view?: string | undefined;
817
+ promptSuffix?: string | undefined;
818
+ promptPrefix?: string | undefined;
819
+ styleImages?: {
820
+ path: string;
821
+ }[] | undefined;
822
+ outline?: string | undefined;
823
+ shading?: string | undefined;
824
+ detail?: string | undefined;
825
+ tileSize?: number | undefined;
826
+ tileType?: "hex" | "hex_pointy" | "isometric" | "oblique" | "octagon" | "square_topdown" | undefined;
827
+ tileView?: "top-down" | "high top-down" | "low top-down" | "side" | undefined;
828
+ tileFeature?: "roads" | "tileset" | "building" | undefined;
829
+ outlineMode?: "outline" | "segmentation" | undefined;
830
+ noBackground?: boolean | undefined;
831
+ seed?: number | undefined;
832
+ palette?: string[] | undefined;
833
+ mount?: {
834
+ cellWidth: number;
835
+ cellHeight: number;
836
+ out: string;
837
+ base?: string | undefined;
838
+ } | undefined;
839
+ tags?: string[] | undefined;
840
+ }>;
841
+ name: string;
842
+ assets: Record<string, {
843
+ prompt: string;
844
+ size?: number | undefined;
845
+ tags?: string[] | undefined;
846
+ category?: string | undefined;
847
+ width?: number | undefined;
848
+ height?: number | undefined;
849
+ file?: string | undefined;
850
+ cell?: [number, number] | undefined;
851
+ source?: string | undefined;
852
+ outputRole?: string | undefined;
853
+ styles?: string[] | undefined;
854
+ promptByStyle?: Record<string, string> | undefined;
855
+ }>;
856
+ $schema?: string | undefined;
857
+ }>;
858
+ type Manifest = z.infer<typeof ManifestSchema>;
859
+ type Style = z.infer<typeof StyleSchema>;
860
+ type Asset = z.infer<typeof AssetSchema>;
861
+ /**
862
+ * One line of the lockfile: the mapping from a spec to the PixelLab object that
863
+ * satisfies it and the file on disk that came from it. This is the record that
864
+ * did not exist before — without it, generated objects and downloaded files are
865
+ * two unrelated piles.
866
+ */
867
+ declare const LockEntrySchema: z.ZodObject<{
868
+ styleId: z.ZodString;
869
+ assetId: z.ZodString;
870
+ /** sha256 of the resolved spec. Changing a prompt/size/style invalidates it. */
871
+ specHash: z.ZodString;
872
+ generator: z.ZodEnum<["1dir", "map", "pixflux", "tiles"]>;
873
+ /** Connectable tiles must remain multi-output even when polling is resumed later. */
874
+ tileFeature: z.ZodDefault<z.ZodNullable<z.ZodString>>;
875
+ /** The resolved prompt actually sent, kept for auditing and for adopt matching. */
876
+ prompt: z.ZodString;
877
+ width: z.ZodNumber;
878
+ height: z.ZodNumber;
879
+ /** Set at submit time, before the request is awaited, so a crash is recoverable. */
880
+ jobId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
881
+ /** For `1dir`: the multi-candidate parent object awaiting selection. */
882
+ reviewObjectId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
883
+ /** The chosen candidate's own object id, once selected. */
884
+ objectId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
885
+ candidateIndex: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
886
+ status: z.ZodDefault<z.ZodEnum<["pending", "processing", "review", "selected", "downloaded", "download-failed", "failed"]>>;
887
+ error: z.ZodDefault<z.ZodNullable<z.ZodString>>;
888
+ sourceUrl: z.ZodDefault<z.ZodNullable<z.ZodString>>;
889
+ /** Every source in a structural multi-output result, in provider order. */
890
+ sourceUrls: z.ZodDefault<z.ZodArray<z.ZodObject<{
891
+ url: z.ZodString;
892
+ role: z.ZodOptional<z.ZodString>;
893
+ }, "strip", z.ZodTypeAny, {
894
+ url: string;
895
+ role?: string | undefined;
896
+ }, {
897
+ url: string;
898
+ role?: string | undefined;
899
+ }>, "many">>;
900
+ /**
901
+ * Files this entry produced. A plain object generates one; asset kinds that
902
+ * expand into many — an animated character is ~35 spritesheets plus an engine
903
+ * resource — need the list, which is why v1's single `file` became this.
904
+ *
905
+ * `role` labels non-primary artifacts (e.g. "portrait", "spriteframes") so a
906
+ * consumer can find the one it wants without pattern-matching on paths.
907
+ */
908
+ outputs: z.ZodDefault<z.ZodArray<z.ZodObject<{
909
+ path: z.ZodString;
910
+ sha256: z.ZodString;
911
+ role: z.ZodOptional<z.ZodString>;
912
+ }, "strip", z.ZodTypeAny, {
913
+ path: string;
914
+ sha256: string;
915
+ role?: string | undefined;
916
+ }, {
917
+ path: string;
918
+ sha256: string;
919
+ role?: string | undefined;
920
+ }>, "many">>;
921
+ /**
922
+ * Provider-owned data retained for downstream consumers, namespaced by
923
+ * provider id. PixelLab connectable sets keep their exact `tileRules` here
924
+ * so exporters can map images to adjacency rules.
925
+ */
926
+ providerMetadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
927
+ submittedAt: z.ZodDefault<z.ZodNullable<z.ZodString>>;
928
+ downloadedAt: z.ZodDefault<z.ZodNullable<z.ZodString>>;
929
+ /** Successful-submission estimate in `costUnit`; may be fractional USD. */
930
+ cost: z.ZodDefault<z.ZodNumber>;
931
+ /** Unit for `cost`. Defaults preserve pre-unit PixelLab lockfiles. */
932
+ costUnit: z.ZodDefault<z.ZodEnum<["generations", "usd", "free"]>>;
933
+ /** Which provider produced this. Absent on entries written before providers. */
934
+ provider: z.ZodDefault<z.ZodString>;
935
+ }, "strip", z.ZodTypeAny, {
936
+ status: "pending" | "processing" | "review" | "selected" | "downloaded" | "download-failed" | "failed";
937
+ generator: "1dir" | "map" | "pixflux" | "tiles";
938
+ tileFeature: string | null;
939
+ prompt: string;
940
+ width: number;
941
+ height: number;
942
+ styleId: string;
943
+ assetId: string;
944
+ specHash: string;
945
+ jobId: string | null;
946
+ reviewObjectId: string | null;
947
+ objectId: string | null;
948
+ candidateIndex: number | null;
949
+ error: string | null;
950
+ sourceUrl: string | null;
951
+ sourceUrls: {
952
+ url: string;
953
+ role?: string | undefined;
954
+ }[];
955
+ outputs: {
956
+ path: string;
957
+ sha256: string;
958
+ role?: string | undefined;
959
+ }[];
960
+ providerMetadata: Record<string, Record<string, unknown>>;
961
+ submittedAt: string | null;
962
+ downloadedAt: string | null;
963
+ cost: number;
964
+ costUnit: "generations" | "usd" | "free";
965
+ provider: string;
966
+ }, {
967
+ generator: "1dir" | "map" | "pixflux" | "tiles";
968
+ prompt: string;
969
+ width: number;
970
+ height: number;
971
+ styleId: string;
972
+ assetId: string;
973
+ specHash: string;
974
+ status?: "pending" | "processing" | "review" | "selected" | "downloaded" | "download-failed" | "failed" | undefined;
975
+ tileFeature?: string | null | undefined;
976
+ jobId?: string | null | undefined;
977
+ reviewObjectId?: string | null | undefined;
978
+ objectId?: string | null | undefined;
979
+ candidateIndex?: number | null | undefined;
980
+ error?: string | null | undefined;
981
+ sourceUrl?: string | null | undefined;
982
+ sourceUrls?: {
983
+ url: string;
984
+ role?: string | undefined;
985
+ }[] | undefined;
986
+ outputs?: {
987
+ path: string;
988
+ sha256: string;
989
+ role?: string | undefined;
990
+ }[] | undefined;
991
+ providerMetadata?: Record<string, Record<string, unknown>> | undefined;
992
+ submittedAt?: string | null | undefined;
993
+ downloadedAt?: string | null | undefined;
994
+ cost?: number | undefined;
995
+ costUnit?: "generations" | "usd" | "free" | undefined;
996
+ provider?: string | undefined;
997
+ }>;
998
+ declare const LockSchema: z.ZodObject<{
999
+ version: z.ZodLiteral<2>;
1000
+ entries: z.ZodRecord<z.ZodString, z.ZodObject<{
1001
+ styleId: z.ZodString;
1002
+ assetId: z.ZodString;
1003
+ /** sha256 of the resolved spec. Changing a prompt/size/style invalidates it. */
1004
+ specHash: z.ZodString;
1005
+ generator: z.ZodEnum<["1dir", "map", "pixflux", "tiles"]>;
1006
+ /** Connectable tiles must remain multi-output even when polling is resumed later. */
1007
+ tileFeature: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1008
+ /** The resolved prompt actually sent, kept for auditing and for adopt matching. */
1009
+ prompt: z.ZodString;
1010
+ width: z.ZodNumber;
1011
+ height: z.ZodNumber;
1012
+ /** Set at submit time, before the request is awaited, so a crash is recoverable. */
1013
+ jobId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1014
+ /** For `1dir`: the multi-candidate parent object awaiting selection. */
1015
+ reviewObjectId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1016
+ /** The chosen candidate's own object id, once selected. */
1017
+ objectId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1018
+ candidateIndex: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
1019
+ status: z.ZodDefault<z.ZodEnum<["pending", "processing", "review", "selected", "downloaded", "download-failed", "failed"]>>;
1020
+ error: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1021
+ sourceUrl: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1022
+ /** Every source in a structural multi-output result, in provider order. */
1023
+ sourceUrls: z.ZodDefault<z.ZodArray<z.ZodObject<{
1024
+ url: z.ZodString;
1025
+ role: z.ZodOptional<z.ZodString>;
1026
+ }, "strip", z.ZodTypeAny, {
1027
+ url: string;
1028
+ role?: string | undefined;
1029
+ }, {
1030
+ url: string;
1031
+ role?: string | undefined;
1032
+ }>, "many">>;
1033
+ /**
1034
+ * Files this entry produced. A plain object generates one; asset kinds that
1035
+ * expand into many — an animated character is ~35 spritesheets plus an engine
1036
+ * resource — need the list, which is why v1's single `file` became this.
1037
+ *
1038
+ * `role` labels non-primary artifacts (e.g. "portrait", "spriteframes") so a
1039
+ * consumer can find the one it wants without pattern-matching on paths.
1040
+ */
1041
+ outputs: z.ZodDefault<z.ZodArray<z.ZodObject<{
1042
+ path: z.ZodString;
1043
+ sha256: z.ZodString;
1044
+ role: z.ZodOptional<z.ZodString>;
1045
+ }, "strip", z.ZodTypeAny, {
1046
+ path: string;
1047
+ sha256: string;
1048
+ role?: string | undefined;
1049
+ }, {
1050
+ path: string;
1051
+ sha256: string;
1052
+ role?: string | undefined;
1053
+ }>, "many">>;
1054
+ /**
1055
+ * Provider-owned data retained for downstream consumers, namespaced by
1056
+ * provider id. PixelLab connectable sets keep their exact `tileRules` here
1057
+ * so exporters can map images to adjacency rules.
1058
+ */
1059
+ providerMetadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1060
+ submittedAt: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1061
+ downloadedAt: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1062
+ /** Successful-submission estimate in `costUnit`; may be fractional USD. */
1063
+ cost: z.ZodDefault<z.ZodNumber>;
1064
+ /** Unit for `cost`. Defaults preserve pre-unit PixelLab lockfiles. */
1065
+ costUnit: z.ZodDefault<z.ZodEnum<["generations", "usd", "free"]>>;
1066
+ /** Which provider produced this. Absent on entries written before providers. */
1067
+ provider: z.ZodDefault<z.ZodString>;
1068
+ }, "strip", z.ZodTypeAny, {
1069
+ status: "pending" | "processing" | "review" | "selected" | "downloaded" | "download-failed" | "failed";
1070
+ generator: "1dir" | "map" | "pixflux" | "tiles";
1071
+ tileFeature: string | null;
1072
+ prompt: string;
1073
+ width: number;
1074
+ height: number;
1075
+ styleId: string;
1076
+ assetId: string;
1077
+ specHash: string;
1078
+ jobId: string | null;
1079
+ reviewObjectId: string | null;
1080
+ objectId: string | null;
1081
+ candidateIndex: number | null;
1082
+ error: string | null;
1083
+ sourceUrl: string | null;
1084
+ sourceUrls: {
1085
+ url: string;
1086
+ role?: string | undefined;
1087
+ }[];
1088
+ outputs: {
1089
+ path: string;
1090
+ sha256: string;
1091
+ role?: string | undefined;
1092
+ }[];
1093
+ providerMetadata: Record<string, Record<string, unknown>>;
1094
+ submittedAt: string | null;
1095
+ downloadedAt: string | null;
1096
+ cost: number;
1097
+ costUnit: "generations" | "usd" | "free";
1098
+ provider: string;
1099
+ }, {
1100
+ generator: "1dir" | "map" | "pixflux" | "tiles";
1101
+ prompt: string;
1102
+ width: number;
1103
+ height: number;
1104
+ styleId: string;
1105
+ assetId: string;
1106
+ specHash: string;
1107
+ status?: "pending" | "processing" | "review" | "selected" | "downloaded" | "download-failed" | "failed" | undefined;
1108
+ tileFeature?: string | null | undefined;
1109
+ jobId?: string | null | undefined;
1110
+ reviewObjectId?: string | null | undefined;
1111
+ objectId?: string | null | undefined;
1112
+ candidateIndex?: number | null | undefined;
1113
+ error?: string | null | undefined;
1114
+ sourceUrl?: string | null | undefined;
1115
+ sourceUrls?: {
1116
+ url: string;
1117
+ role?: string | undefined;
1118
+ }[] | undefined;
1119
+ outputs?: {
1120
+ path: string;
1121
+ sha256: string;
1122
+ role?: string | undefined;
1123
+ }[] | undefined;
1124
+ providerMetadata?: Record<string, Record<string, unknown>> | undefined;
1125
+ submittedAt?: string | null | undefined;
1126
+ downloadedAt?: string | null | undefined;
1127
+ cost?: number | undefined;
1128
+ costUnit?: "generations" | "usd" | "free" | undefined;
1129
+ provider?: string | undefined;
1130
+ }>>;
1131
+ }, "strip", z.ZodTypeAny, {
1132
+ entries: Record<string, {
1133
+ status: "pending" | "processing" | "review" | "selected" | "downloaded" | "download-failed" | "failed";
1134
+ generator: "1dir" | "map" | "pixflux" | "tiles";
1135
+ tileFeature: string | null;
1136
+ prompt: string;
1137
+ width: number;
1138
+ height: number;
1139
+ styleId: string;
1140
+ assetId: string;
1141
+ specHash: string;
1142
+ jobId: string | null;
1143
+ reviewObjectId: string | null;
1144
+ objectId: string | null;
1145
+ candidateIndex: number | null;
1146
+ error: string | null;
1147
+ sourceUrl: string | null;
1148
+ sourceUrls: {
1149
+ url: string;
1150
+ role?: string | undefined;
1151
+ }[];
1152
+ outputs: {
1153
+ path: string;
1154
+ sha256: string;
1155
+ role?: string | undefined;
1156
+ }[];
1157
+ providerMetadata: Record<string, Record<string, unknown>>;
1158
+ submittedAt: string | null;
1159
+ downloadedAt: string | null;
1160
+ cost: number;
1161
+ costUnit: "generations" | "usd" | "free";
1162
+ provider: string;
1163
+ }>;
1164
+ version: 2;
1165
+ }, {
1166
+ entries: Record<string, {
1167
+ generator: "1dir" | "map" | "pixflux" | "tiles";
1168
+ prompt: string;
1169
+ width: number;
1170
+ height: number;
1171
+ styleId: string;
1172
+ assetId: string;
1173
+ specHash: string;
1174
+ status?: "pending" | "processing" | "review" | "selected" | "downloaded" | "download-failed" | "failed" | undefined;
1175
+ tileFeature?: string | null | undefined;
1176
+ jobId?: string | null | undefined;
1177
+ reviewObjectId?: string | null | undefined;
1178
+ objectId?: string | null | undefined;
1179
+ candidateIndex?: number | null | undefined;
1180
+ error?: string | null | undefined;
1181
+ sourceUrl?: string | null | undefined;
1182
+ sourceUrls?: {
1183
+ url: string;
1184
+ role?: string | undefined;
1185
+ }[] | undefined;
1186
+ outputs?: {
1187
+ path: string;
1188
+ sha256: string;
1189
+ role?: string | undefined;
1190
+ }[] | undefined;
1191
+ providerMetadata?: Record<string, Record<string, unknown>> | undefined;
1192
+ submittedAt?: string | null | undefined;
1193
+ downloadedAt?: string | null | undefined;
1194
+ cost?: number | undefined;
1195
+ costUnit?: "generations" | "usd" | "free" | undefined;
1196
+ provider?: string | undefined;
1197
+ }>;
1198
+ version: 2;
1199
+ }>;
1200
+ type LockEntry = z.infer<typeof LockEntrySchema>;
1201
+ type Lock = z.infer<typeof LockSchema>;
1202
+ type LockOutput = LockEntry["outputs"][number];
1203
+ /**
1204
+ * Parses a lockfile, rejecting anything that is not v2.
1205
+ *
1206
+ * There is deliberately no migration path. Both consuming projects were
1207
+ * onboarded after v2 landed, so a v1 file would be a corruption or a
1208
+ * hand-edit rather than a legacy artifact — better to fail loudly than to
1209
+ * quietly reinterpret it.
1210
+ */
1211
+ declare function parseLock(raw: unknown): Lock;
1212
+ /** The file a consumer means when it says "the asset" — the first output. */
1213
+ declare function primaryOutput(entry: LockEntry): LockOutput | null;
1214
+ /** Lock entries are keyed `<styleId>/<assetId>`, which makes styles a namespace. */
1215
+ declare function lockKey(styleId: string, assetId: string): string;
1216
+ /** A manifest entry resolved against its style — everything needed to generate. */
1217
+ interface ResolvedSpec {
1218
+ /** Absolute directory containing the manifest; excluded from the spec hash. */
1219
+ root: string;
1220
+ styleId: string;
1221
+ assetId: string;
1222
+ generator: Generator;
1223
+ prompt: string;
1224
+ width: number;
1225
+ height: number;
1226
+ view: string;
1227
+ size: number;
1228
+ styleImagePaths: string[];
1229
+ outFile: string;
1230
+ /**
1231
+ * Manifest-relative path of committed art that stands in for generated
1232
+ * output; excluded from the spec hash. Set only when the asset declares
1233
+ * `source`, which also means it needs no lock entry.
1234
+ */
1235
+ source?: string;
1236
+ tags: string[];
1237
+ specHash: string;
1238
+ cost: number;
1239
+ costUnit: "generations" | "usd" | "free";
1240
+ candidates: number;
1241
+ outline?: string;
1242
+ shading?: string;
1243
+ detail?: string;
1244
+ seed?: number;
1245
+ /** Forced palette hex values; empty unless the style sets one. */
1246
+ palette: string[];
1247
+ /** `pixflux` only — strip the generated background. Defaults to true. */
1248
+ noBackground: boolean;
1249
+ /** `tiles` generator only — see StyleSchema for what each one means. */
1250
+ tileSize?: number;
1251
+ tileType?: string;
1252
+ tileView?: string;
1253
+ tileFeature?: string;
1254
+ outlineMode?: string;
1255
+ }
1256
+
1257
+ declare const MAX_RETRIES = 4;
1258
+ declare const MAX_DOWNLOAD_BYTES: number;
1259
+ declare function shouldRetry(status: number): boolean;
1260
+ /** Exponential backoff with jitter, so parallel workers don't retry in lockstep. */
1261
+ declare function backoffMs(attempt: number): number;
1262
+ /** Supports both Retry-After forms: seconds and an HTTP date. */
1263
+ declare function retryAfterMs(value: string | null, now?: number): number | null;
1264
+ interface Balance {
1265
+ usd: number;
1266
+ generations: number;
1267
+ total: number;
1268
+ plan: string;
1269
+ }
1270
+ interface PixelLabObject {
1271
+ id: string;
1272
+ name: string | null;
1273
+ prompt: string;
1274
+ size: {
1275
+ width: number;
1276
+ height: number;
1277
+ };
1278
+ directions: number;
1279
+ created_at: string;
1280
+ view: string | null;
1281
+ preview_url?: string | null;
1282
+ rotation_urls?: Record<string, string | null> | null;
1283
+ /** Populated only while status === "review". Index order is what select-frames expects. */
1284
+ frame_urls?: string[] | null;
1285
+ tags: string[];
1286
+ status: string | null;
1287
+ progress_percent?: number | null;
1288
+ eta_seconds?: number | null;
1289
+ }
1290
+ /**
1291
+ * A tiles-pro job. Unlike an object or a map object there is no `status`
1292
+ * field: GET /tiles-pro/{id} answers 423 while the set is still drawing and
1293
+ * 200 with `storage_urls` once it is done, so the HTTP code IS the status.
1294
+ */
1295
+ interface TilesPro {
1296
+ /** Keyed `tile_0`, `tile_1`, ... Index order is load-bearing for a
1297
+ * connectable set (`tile_feature`), where a consumer slices by index. */
1298
+ storage_urls: Record<string, string>;
1299
+ kind: string | null;
1300
+ tile_rules?: Record<string, unknown> | null;
1301
+ }
1302
+ interface MapObject {
1303
+ object_id: string;
1304
+ status: string;
1305
+ description: string | null;
1306
+ width: number | null;
1307
+ height: number | null;
1308
+ download_url: string | null;
1309
+ }
1310
+ declare class PixelLabError extends Error {
1311
+ readonly status: number;
1312
+ readonly body: string;
1313
+ constructor(message: string, status: number, body: string);
1314
+ }
1315
+ declare class PixelLabClient {
1316
+ private readonly apiKey;
1317
+ private readonly timeoutMs;
1318
+ constructor(apiKey: string, timeoutMs?: number);
1319
+ /**
1320
+ * Retries only what is safe to retry: transport failures, 429, and 5xx.
1321
+ * A 4xx other than 429 is a bad request and retrying it just wastes time.
1322
+ *
1323
+ * POSTs that create objects are included, which is a deliberate trade: the
1324
+ * failure mode of not retrying (a dropped asset in a 65-item run) is more
1325
+ * common than the failure mode of retrying (a duplicate object), and a
1326
+ * duplicate is visible and free to delete whereas a silent gap is neither.
1327
+ */
1328
+ private request;
1329
+ balance(): Promise<Balance>;
1330
+ /**
1331
+ * Square objects that persist indefinitely.
1332
+ *
1333
+ * `size` and `styleImages` are mutually exclusive at the API level: when style
1334
+ * images are supplied the largest one dictates the output size. So style
1335
+ * references must already be at the target resolution — a 128px reference
1336
+ * silently produces 128px output and a different candidate count.
1337
+ */
1338
+ create1Direction(args: {
1339
+ description: string;
1340
+ size?: number;
1341
+ view?: string;
1342
+ styleImages?: ResolvedStyleImage[];
1343
+ itemDescriptions?: string[];
1344
+ }): Promise<{
1345
+ object_id: string;
1346
+ status: string;
1347
+ n_frames: number;
1348
+ }>;
1349
+ /**
1350
+ * Arbitrary width x height. Returns a single result — no selection step.
1351
+ *
1352
+ * These AUTO-DELETE AFTER 8 HOURS, so `fetch` must run in the same session as
1353
+ * `submit`. The pipeline warns when a map-object entry is older than that.
1354
+ */
1355
+ createMapObject(args: {
1356
+ description: string;
1357
+ width: number;
1358
+ height: number;
1359
+ view?: string;
1360
+ outline?: string;
1361
+ shading?: string;
1362
+ detail?: string;
1363
+ seed?: number;
1364
+ }): Promise<{
1365
+ object_id: string;
1366
+ status: string;
1367
+ }>;
1368
+ /**
1369
+ * Draws a whole tile set in one call — many variations, or a connectable
1370
+ * set when `tileFeature` is given.
1371
+ *
1372
+ * `styleImages` here is NOT the shape `create-1-direction-object` uses.
1373
+ * TilesProStyleImage is flat — `{base64, width, height}`, all three
1374
+ * required — where 1dir wants `{type, base64, format}`. Confirmed against
1375
+ * the OpenAPI schema; sending 1dir's shape is rejected as an extra field.
1376
+ *
1377
+ * Passing style images also makes the API ignore `tileType` and `tileView`
1378
+ * and copy the reference's tile geometry instead.
1379
+ */
1380
+ createTilesPro(args: {
1381
+ description: string;
1382
+ tileSize?: number;
1383
+ tileType?: string;
1384
+ tileView?: string;
1385
+ tileFeature?: string;
1386
+ outlineMode?: string;
1387
+ seed?: number;
1388
+ styleImages?: {
1389
+ base64: string;
1390
+ width: number;
1391
+ height: number;
1392
+ }[];
1393
+ }): Promise<{
1394
+ tile_id: string;
1395
+ background_job_id: string;
1396
+ status: string;
1397
+ }>;
1398
+ /** Throws PixelLabError(423) while the set is still drawing — see TilesPro. */
1399
+ getTilesPro(tileId: string): Promise<TilesPro>;
1400
+ /**
1401
+ * Synchronous single-image generation. Returns the PNG inline rather than a
1402
+ * job id, and is the only endpoint that honours a forced palette —
1403
+ * `color_image` on /map-objects returns a 500 whatever the payload shape.
1404
+ */
1405
+ createImagePixflux(args: {
1406
+ description: string;
1407
+ width: number;
1408
+ height: number;
1409
+ noBackground?: boolean;
1410
+ paletteSwatchBase64?: string;
1411
+ seed?: number;
1412
+ }): Promise<{
1413
+ png: Buffer;
1414
+ usage: unknown;
1415
+ }>;
1416
+ getObject(objectId: string): Promise<PixelLabObject>;
1417
+ getMapObject(objectId: string): Promise<MapObject>;
1418
+ listObjects(limit?: number, offset?: number): Promise<{
1419
+ objects: PixelLabObject[];
1420
+ total: number;
1421
+ }>;
1422
+ /** Walks the whole account. Used by `adopt` to reconcile orphaned objects. */
1423
+ iterateObjects(pageSize?: number): AsyncGenerator<PixelLabObject>;
1424
+ /**
1425
+ * Promotes chosen candidates to standalone objects, each with its own id.
1426
+ * The review parent survives until nothing is left in it, so the returned
1427
+ * `created_object_ids` — not the parent id — is what should be recorded.
1428
+ */
1429
+ selectFrames(objectId: string, indices: number[], commonTag?: string): Promise<{
1430
+ created_object_ids?: string[];
1431
+ }>;
1432
+ /** Irreversible. Only reached via `purge`, behind an explicit confirmation. */
1433
+ deleteObject(objectId: string): Promise<unknown>;
1434
+ dismissReview(objectId: string): Promise<unknown>;
1435
+ /** Free and synchronous. Replaces the full tag set — include tags you want to keep. */
1436
+ setTags(objectId: string, tags: string[]): Promise<unknown>;
1437
+ /** Storage URLs are public; no auth header, and sending one can break the CDN request. */
1438
+ download(url: string): Promise<Buffer>;
1439
+ }
1440
+ declare function clientFromEnv(): PixelLabClient;
1441
+
1442
+ /**
1443
+ * What a provider charges in. Cost is not universally "generations": PixelLab
1444
+ * bills a subscription quota, OpenAI bills dollars per image, a local model
1445
+ * bills nothing. `plan` prints the unit alongside the number so the figure is
1446
+ * never silently misread, and `--budget` is interpreted in the active unit.
1447
+ */
1448
+ type CostUnit = "generations" | "usd" | "free";
1449
+ interface CostEstimate {
1450
+ unit: CostUnit;
1451
+ amount: number;
1452
+ /**
1453
+ * How many candidates one call returns. This is a provider property, not a
1454
+ * universal truth — PixelLab's `1dir` returns up to 64 for a single fixed
1455
+ * price, whereas a per-image provider returns one and charges N times for N.
1456
+ * The "generate small, pick from many" strategy only pays off where this is
1457
+ * greater than 1 at no extra cost.
1458
+ */
1459
+ candidates: number;
1460
+ }
1461
+ /** Runtime guard for third-party adapters before estimates influence a budget. */
1462
+ declare function validateCostEstimate(providerId: string, value: unknown): CostEstimate;
1463
+ interface OutputSource {
1464
+ url: string;
1465
+ /** Stable semantic/index role used in filenames and lockfile outputs. */
1466
+ role?: string;
1467
+ }
1468
+ interface PollContext {
1469
+ /** Distinguishes structural tile sets from independent tile candidates. */
1470
+ tileFeature?: string;
1471
+ }
1472
+ /** Provider-owned, JSON-serializable details needed by downstream exporters. */
1473
+ type ProviderMetadata = Record<string, unknown>;
1474
+ /** Terminal and non-terminal states a queued job can be observed in. */
1475
+ type JobState = {
1476
+ status: "processing";
1477
+ progressPercent?: number | null;
1478
+ etaSeconds?: number | null;
1479
+ } | {
1480
+ status: "review";
1481
+ candidateUrls: string[];
1482
+ } | {
1483
+ status: "ready";
1484
+ objectId: string;
1485
+ /** Kept for compatibility with single-output provider implementations. */
1486
+ sourceUrl: string | null;
1487
+ /** Present for structural multi-output results. */
1488
+ sources?: OutputSource[];
1489
+ /** Preserved under the provider's namespace in the lockfile. */
1490
+ metadata?: ProviderMetadata;
1491
+ } | {
1492
+ status: "failed";
1493
+ error: string;
1494
+ };
1495
+ /** A previously generated asset as the provider reports it. */
1496
+ interface RemoteAsset {
1497
+ id: string;
1498
+ prompt: string;
1499
+ width: number;
1500
+ height: number;
1501
+ createdAt: string;
1502
+ previewUrl: string | null;
1503
+ tags: string[];
1504
+ status: string;
1505
+ }
1506
+ interface BalanceInfo {
1507
+ unit: CostUnit;
1508
+ remaining: number;
1509
+ total?: number;
1510
+ plan?: string;
1511
+ }
1512
+ interface BalanceChange {
1513
+ unit: CostUnit;
1514
+ before: number;
1515
+ after: number;
1516
+ /** Provider-reported quota/currency consumed between the two readings. */
1517
+ spent: number;
1518
+ /** Quota/currency added between readings, e.g. a refill during the run. */
1519
+ credited: number;
1520
+ }
1521
+ /** Compare two provider readings without ever combining incompatible units. */
1522
+ declare function measureBalanceChange(before: BalanceInfo, after: BalanceInfo): BalanceChange | null;
1523
+ /**
1524
+ * Submission constraints a backend enforces upstream, in its own units —
1525
+ * `submit` has no business knowing these numbers itself.
1526
+ */
1527
+ interface RateLimit {
1528
+ /** Minimum time between successive submissions, global across the account. */
1529
+ spacingMs: number;
1530
+ /** Background jobs allowed in flight at once. */
1531
+ maxInFlight: number;
1532
+ }
1533
+ /**
1534
+ * Used when a provider doesn't declare `rateLimit()` — conservative enough
1535
+ * not to be a real constraint for a provider that has none of its own, and
1536
+ * overridable per run via `submit`'s own options regardless.
1537
+ */
1538
+ declare const DEFAULT_RATE_LIMIT: RateLimit;
1539
+ /**
1540
+ * A backend that turns a resolved spec into image bytes.
1541
+ *
1542
+ * Everything above this interface — the manifest, the lockfile, plan diffing,
1543
+ * salvage, the contact sheets — is provider-agnostic. Everything that knows a
1544
+ * URL shape or an auth header lives below it.
1545
+ *
1546
+ * The optional members are genuinely optional capabilities rather than
1547
+ * convenience: a provider with no queryable asset list cannot support `adopt`
1548
+ * or `salvage`, and the CLI reports that rather than failing obscurely.
1549
+ */
1550
+ interface Provider {
1551
+ readonly id: string;
1552
+ /** False for a generator this backend cannot express (e.g. non-square). */
1553
+ supports(generator: Generator): boolean;
1554
+ /** Never performs I/O — `plan` must stay free and offline. */
1555
+ estimate(spec: ResolvedSpec): CostEstimate;
1556
+ /** This backend's own submission constraints. Falls back to
1557
+ * `DEFAULT_RATE_LIMIT` when absent — see that constant's doc. */
1558
+ rateLimit?(): RateLimit;
1559
+ submit(spec: ResolvedSpec, styleImages: ResolvedStyleImage[]): Promise<{
1560
+ jobId: string;
1561
+ }>;
1562
+ poll(jobId: string, generator: Generator, context?: PollContext): Promise<JobState>;
1563
+ /**
1564
+ * Promote one candidate from a review-status job to a standalone asset.
1565
+ * Only meaningful where `estimate().candidates > 1`.
1566
+ *
1567
+ * `generator` is passed because "promote" is not universal: PixelLab's
1568
+ * frame-based generators create a new account object, while a tiles
1569
+ * variation is already a finished image and there is nothing to promote.
1570
+ * A provider that treats every candidate alike can ignore it.
1571
+ */
1572
+ selectCandidate(jobId: string, index: number, commonTag?: string, generator?: Generator): Promise<{
1573
+ objectId: string;
1574
+ sourceUrl: string | null;
1575
+ }>;
1576
+ /** Storage URLs are usually public; implementations should not send auth. */
1577
+ download(url: string): Promise<Buffer>;
1578
+ balance(): Promise<BalanceInfo>;
1579
+ /** Free-form labels on the remote asset. Absent if unsupported. */
1580
+ setTags?(objectId: string, tags: string[]): Promise<void>;
1581
+ /** Walk every asset on the account. Required by `adopt` and `salvage`. */
1582
+ list?(): AsyncGenerator<RemoteAsset>;
1583
+ /** Irreversible. Only reached via `purge`, behind explicit confirmation. */
1584
+ delete?(assetId: string): Promise<void>;
1585
+ }
1586
+ declare class UnsupportedCapabilityError extends Error {
1587
+ constructor(providerId: string, capability: string);
1588
+ }
1589
+ /** Narrowing helpers so call sites fail with a clear message, not `undefined is not a function`. */
1590
+ declare function requireList(provider: Provider): NonNullable<Provider["list"]>;
1591
+ declare function requireDelete(provider: Provider): NonNullable<Provider["delete"]>;
1592
+ declare function formatCost(unit: CostUnit, amount: number): string;
1593
+
1594
+ /**
1595
+ * PixelLab, the reference implementation.
1596
+ *
1597
+ * All PixelLab-specific knowledge lives here: the three generators and their
1598
+ * very different pricing, the review/candidate flow, the fact that a map
1599
+ * object's job record expires while its image does not, and that pixflux
1600
+ * returns bytes inline instead of a job id.
1601
+ */
1602
+ declare class PixelLabProvider implements Provider {
1603
+ private readonly client;
1604
+ readonly id = "pixellab";
1605
+ constructor(client: PixelLabClient);
1606
+ static fromEnv(): PixelLabProvider;
1607
+ /** Public storage URLs and the local content cache do not require API auth. */
1608
+ static forDownloads(): PixelLabProvider;
1609
+ /** Capability and cost estimation only; makes no network request itself. */
1610
+ static forOffline(): PixelLabProvider;
1611
+ supports(generator: Generator): boolean;
1612
+ /** PixelLab's own constraints: submissions must be >2s apart, and
1613
+ * background jobs in flight are capped by subscription tier (Tier 1=8,
1614
+ * Tier 2=10, Tier 3=20) — 8 is the safe floor across every tier. */
1615
+ rateLimit(): RateLimit;
1616
+ /**
1617
+ * Where synchronous pixflux results are parked between `submit` and `fetch`.
1618
+ *
1619
+ * pixflux returns the PNG inline rather than a job id, but the pipeline is
1620
+ * built around submit → poll → fetch running as separate commands. Writing
1621
+ * the bytes to a known path keeps that model intact: the "job id" is the
1622
+ * filename, polling is an existence check, and downloading is a file read.
1623
+ */
1624
+ private static cacheDir;
1625
+ estimate(spec: ResolvedSpec): CostEstimate;
1626
+ submit(spec: ResolvedSpec, styleImages: ResolvedStyleImage[]): Promise<{
1627
+ jobId: string;
1628
+ }>;
1629
+ poll(jobId: string, generator: Generator, context?: PollContext): Promise<JobState>;
1630
+ /**
1631
+ * Map objects need their own path because the `/map-objects/{id}` record is
1632
+ * deleted upstream roughly 8 hours after creation while the image survives in
1633
+ * the objects collection. Verified: a March 2026 sprite still resolves from
1634
+ * `/objects` four months on, with `/map-objects` returning 404 for the same
1635
+ * id. So a 404 here is not evidence the work is lost.
1636
+ */
1637
+ private pollMap;
1638
+ /**
1639
+ * Tiles report progress through the HTTP status: 423 while drawing, 200 with
1640
+ * `storage_urls` once finished. There is no `status` field to read and no
1641
+ * progress percentage on offer, so "processing" here carries no ETA.
1642
+ */
1643
+ private pollTiles;
1644
+ selectCandidate(jobId: string, index: number, commonTag?: string, generator?: Generator): Promise<{
1645
+ objectId: string;
1646
+ sourceUrl: string | null;
1647
+ }>;
1648
+ download(url: string): Promise<Buffer>;
1649
+ balance(): Promise<BalanceInfo>;
1650
+ setTags(objectId: string, tags: string[]): Promise<void>;
1651
+ list(): AsyncGenerator<RemoteAsset>;
1652
+ delete(assetId: string): Promise<void>;
1653
+ }
1654
+
1655
+ /**
1656
+ * An in-memory Provider for tests.
1657
+ *
1658
+ * This exists because the stages that spend money — submit, poll, fetch — were
1659
+ * the only ones without coverage, and four of the five real bugs found so far
1660
+ * lived in them. A fake at this seam exercises the actual state machine,
1661
+ * including the review/candidate path, without a network or an API key.
1662
+ *
1663
+ * Deliberately configurable in the ways that broke things for real: how many
1664
+ * candidates come back, whether a job fails, and whether the queue is observed
1665
+ * as still processing before it settles.
1666
+ */
1667
+ interface FakeOptions {
1668
+ /** Candidates per call. >1 routes through review, as PixelLab's 1dir does. */
1669
+ candidates?: number;
1670
+ /** Polls to report `processing` before settling. Exercises the poll loop. */
1671
+ processingPolls?: number;
1672
+ /** Asset ids that should fail instead of completing. */
1673
+ failAssets?: Set<string>;
1674
+ /** Simulate a provider with no listing capability (adopt/salvage unavailable). */
1675
+ supportsList?: boolean;
1676
+ costUnit?: BalanceInfo["unit"];
1677
+ startingBalance?: number;
1678
+ }
1679
+ interface FakeJob {
1680
+ jobId: string;
1681
+ spec: ResolvedSpec;
1682
+ pollsRemaining: number;
1683
+ candidates: number;
1684
+ failed: boolean;
1685
+ selectedIndex: number | null;
1686
+ objectId: string | null;
1687
+ }
1688
+ /** A valid 1x1 PNG, so `fetch`'s signature check passes on fake output. */
1689
+ declare const FAKE_PNG: Buffer<ArrayBuffer>;
1690
+ declare class FakeProvider implements Provider {
1691
+ private readonly opts;
1692
+ readonly id = "fake";
1693
+ readonly jobs: Map<string, FakeJob>;
1694
+ readonly assets: Map<string, RemoteAsset>;
1695
+ readonly tags: Map<string, string[]>;
1696
+ readonly deleted: string[];
1697
+ /** Every submit, in order — lets a test assert on call count and spacing. */
1698
+ readonly submissions: {
1699
+ jobId: string;
1700
+ assetId: string;
1701
+ at: number;
1702
+ }[];
1703
+ private counter;
1704
+ private balanceRemaining;
1705
+ constructor(opts?: FakeOptions);
1706
+ supports(generator: Generator): boolean;
1707
+ estimate(spec: ResolvedSpec): CostEstimate;
1708
+ submit(spec: ResolvedSpec): Promise<{
1709
+ jobId: string;
1710
+ }>;
1711
+ poll(jobId: string): Promise<JobState>;
1712
+ selectCandidate(jobId: string, index: number, commonTag?: string): Promise<{
1713
+ objectId: string;
1714
+ sourceUrl: string | null;
1715
+ }>;
1716
+ download(url: string): Promise<Buffer>;
1717
+ balance(): Promise<BalanceInfo>;
1718
+ setTags(objectId: string, tags: string[]): Promise<void>;
1719
+ /**
1720
+ * Assigned in the constructor rather than declared as a method, so a test can
1721
+ * model a provider that genuinely lacks listing (making `adopt` and `salvage`
1722
+ * unavailable) instead of one that throws.
1723
+ */
1724
+ list?: () => AsyncGenerator<RemoteAsset>;
1725
+ private listImpl;
1726
+ delete(assetId: string): Promise<void>;
1727
+ /** Seed a pre-existing remote asset, for adopt / salvage tests. */
1728
+ seed(asset: Partial<RemoteAsset> & {
1729
+ id: string;
1730
+ }): RemoteAsset;
1731
+ private register;
1732
+ }
1733
+
1734
+ interface LoadedManifest {
1735
+ manifest: Manifest;
1736
+ /** Directory the manifest lives in. All relative paths resolve against it. */
1737
+ root: string;
1738
+ path: string;
1739
+ }
1740
+ declare function loadManifest(manifestPath: string): Promise<LoadedManifest>;
1741
+ /**
1742
+ * Expands the manifest into one concrete spec per (style, asset) pair. This is
1743
+ * where a style becomes a namespace: adding a style re-derives the entire asset
1744
+ * set under a separate output root and separate lock keys, which is what makes
1745
+ * a whole-collection restyle a one-flag operation.
1746
+ */
1747
+ declare function resolveSpecs(loaded: LoadedManifest, filter?: {
1748
+ styles?: string[];
1749
+ assets?: string[];
1750
+ /** Optional provider makes offline plan cost/candidate estimates adapter-owned. */
1751
+ provider?: Pick<Provider, "supports" | "estimate" | "id">;
1752
+ }): Promise<ResolvedSpec[]>;
1753
+ /** Reference image bytes and measured dimensions, in manifest order. */
1754
+ declare function resolveStyleImages(loaded: LoadedManifest, styleId: string): Promise<ResolvedStyleImage[]>;
1755
+ /** Retained for callers that only need the encoded bytes. */
1756
+ declare function styleImagesBase64(loaded: LoadedManifest, styleId: string): Promise<string[]>;
1757
+ /** Reads dimensions without decoding pixel data. */
1758
+ declare function imageMetadata(buf: Buffer): Pick<ResolvedStyleImage, "width" | "height" | "format"> | null;
1759
+
1760
+ /**
1761
+ * The lockfile is the record that maps a spec to the PixelLab object that
1762
+ * satisfies it and the file on disk that came from it. It is written after
1763
+ * every state transition — including immediately after submitting, before the
1764
+ * job is awaited — so an interrupted run never loses track of paid-for work.
1765
+ */
1766
+ declare function loadLock(lockPath: string): Promise<Lock>;
1767
+ /** Atomic write — a crash mid-save must not leave a truncated lockfile. */
1768
+ declare function saveLock(lockPath: string, lock: Lock): Promise<void>;
1769
+ declare function upsert(lock: Lock, key: string, patch: Partial<LockEntry>): LockEntry;
1770
+ /**
1771
+ * Drop a lock entry, and record the removal so the next save honours it.
1772
+ *
1773
+ * Deleting from `lock.entries` alone does nothing: `writeLockWhileHeld` merges
1774
+ * onto whatever is on disk, so the entry is read back and restored. Callers
1775
+ * that need an entry gone must come through here. Returns false when the key
1776
+ * was not present, so a caller can tell a no-op from a removal.
1777
+ */
1778
+ declare function remove(lock: Lock, key: string): boolean;
1779
+ /** Recorded successful-submission estimates, kept separate by provider unit. */
1780
+ declare function spendByUnit(lock: Lock): Record<CostUnit, number>;
1781
+ /** @deprecated Prefer spendByUnit; summing unlike provider units is unsafe. */
1782
+ declare function totalSpend(lock: Lock, unit?: CostUnit): number;
1783
+
1784
+ declare function sha256(data: Buffer | string): string;
1785
+ declare function sha256File(path: string): Promise<string>;
1786
+ /**
1787
+ * Identity of a spec: everything that would change the generated image.
1788
+ * Project root, `outFile`, `source`, and `tags` are deliberately excluded —
1789
+ * moving a checkout, renaming the destination, swapping the committed art a
1790
+ * `mount` places, or retagging should not regenerate art.
1791
+ *
1792
+ * Generator-specific parameters are hashed only where they apply, and are
1793
+ * left `undefined` otherwise so `JSON.stringify` drops the key entirely.
1794
+ * That matters: adding a field unconditionally rewrites the hash of every
1795
+ * spec in every existing lockfile, and each one then reports as `stale` and
1796
+ * invites a full regeneration of art that never changed. Adding `palette`
1797
+ * unconditionally did exactly that once already.
1798
+ */
1799
+ declare function specHash(spec: Omit<ResolvedSpec, "specHash" | "root" | "outFile" | "source" | "tags">, styleImageHashes: string[]): string;
1800
+
1801
+ /** One lockfile output with the identity consumers should expose publicly. */
1802
+ interface ResolvedOutput extends LockOutput {
1803
+ assetId: string;
1804
+ /** Stable within a style's atlas or report. Single-output assets keep their old id. */
1805
+ id: string;
1806
+ index: number;
1807
+ absolutePath: string;
1808
+ }
1809
+ /** Store generated paths relative to the manifest so committed locks survive a clone/move. */
1810
+ declare function portableOutputPath(file: string, manifestDir: string): string;
1811
+ /** Resolve a portable lock path against the manifest, never the process cwd. */
1812
+ declare function resolveOutputPath(recordedPath: string, manifestDir: string): string;
1813
+ /** Deterministic current destination for one provider output. */
1814
+ declare function expectedOutputPath(spec: ResolvedSpec, role: string | undefined, index: number, total: number): string;
1815
+ /** Resolve one recorded output from the current manifest-owned destination. */
1816
+ declare function currentOutputPath(output: Pick<LockOutput, "path" | "role">, spec: ResolvedSpec, index: number, total: number): string;
1817
+ /** Resolve an entry member using the complete source-set order when available. */
1818
+ declare function currentEntryOutputPath(entry: LockEntry, spec: ResolvedSpec, index: number): string;
1819
+ /**
1820
+ * Rebase legacy absolute output paths onto this checkout and canonicalize all
1821
+ * selected entries in memory. Persistence remains the caller's decision.
1822
+ */
1823
+ declare function normalizeLockOutputPaths(lock: Lock, specs: ResolvedSpec[]): number;
1824
+ /** The fallback role used when a provider returns several unnamed outputs. */
1825
+ declare function fallbackOutputRole(index: number): string;
1826
+ /**
1827
+ * Gives every output a stable, collision-resistant consumer id.
1828
+ *
1829
+ * A conventional one-file asset remains `grass`, preserving existing atlas
1830
+ * lookups. A structural set becomes `grass/tile-00`, `grass/tile-01`, … so no
1831
+ * downstream command has to silently pretend its first file is the whole set.
1832
+ */
1833
+ declare function outputId(assetId: string, output: Pick<LockOutput, "role">, index: number, total: number): string;
1834
+ /** Resolve every output recorded for one manifest asset, preserving lock order. */
1835
+ declare function resolveEntryOutputs(entry: LockEntry, assetId: string, manifestDir: string): ResolvedOutput[];
1836
+ /** Resolve an entry using its current spec, including cross-checkout v2 migration. */
1837
+ declare function resolveSpecEntryOutputs(entry: LockEntry, spec: ResolvedSpec): ResolvedOutput[];
1838
+ /** Resolve all outputs for a style in deterministic asset-id order. */
1839
+ declare function resolveStyleOutputs(lock: Lock, styleId: string, manifestDir: string): ResolvedOutput[];
1840
+ type OutputSelection = {
1841
+ ok: true;
1842
+ output: LockOutput;
1843
+ } | {
1844
+ ok: false;
1845
+ reason: string;
1846
+ };
1847
+ /**
1848
+ * Select the one output needed by a single-cell consumer such as `mount`.
1849
+ * Multi-output sets are deliberately ambiguous unless the manifest names a
1850
+ * role; choosing index zero is silent data loss disguised as convenience.
1851
+ */
1852
+ declare function selectEntryOutput(entry: LockEntry, role?: string): OutputSelection;
1853
+ /** Lock outputs for a resolved spec, or its conventional path before adoption. */
1854
+ declare function resolveSpecOutputs(spec: ResolvedSpec, lock: Lock | undefined, manifestDir: string): ResolvedOutput[];
1855
+
1856
+ interface ArtifactFile {
1857
+ path: string;
1858
+ data: string | Uint8Array;
1859
+ }
1860
+ interface ArtifactBundleResult {
1861
+ changed: string[];
1862
+ unchanged: string[];
1863
+ }
1864
+ interface ArtifactBundleOptions {
1865
+ /** @internal Failure-injection hook used to verify staging cleanup. */
1866
+ beforeStage?: (destination: string, index: number) => void | Promise<void>;
1867
+ /** @internal Failure-injection hook used to verify rollback behavior. */
1868
+ beforePromote?: (destination: string, index: number) => void | Promise<void>;
1869
+ /** @internal Durable transaction journal used by the managed writer. */
1870
+ recoveryFile?: string;
1871
+ /** @internal Failure-injection hook used to simulate a crash after commit. */
1872
+ afterCommit?: () => void | Promise<void>;
1873
+ }
1874
+ interface ManagedArtifactBundleOptions extends ArtifactBundleOptions {
1875
+ /** Explicitly replace unowned or manually modified destinations. */
1876
+ force?: boolean;
1877
+ }
1878
+ interface ArtifactSource {
1879
+ id: string;
1880
+ path: string;
1881
+ sha256: string | null;
1882
+ included: boolean;
1883
+ }
1884
+ interface ArtifactProvenance {
1885
+ kind: "pack" | "mount" | "tileset";
1886
+ sources: ArtifactSource[];
1887
+ /** Every non-source input that can change the derived bytes. */
1888
+ options: unknown;
1889
+ }
1890
+ interface ArtifactBundleManifest {
1891
+ format: "pixelkiln-artifact-bundle";
1892
+ version: 1;
1893
+ kind: ArtifactProvenance["kind"];
1894
+ fingerprint: string;
1895
+ sources: ArtifactSource[];
1896
+ options: unknown;
1897
+ outputs: Array<{
1898
+ path: string;
1899
+ sha256: string;
1900
+ }>;
1901
+ }
1902
+ interface ArtifactVerification {
1903
+ current: boolean;
1904
+ fingerprintValid: boolean;
1905
+ changedSources: string[];
1906
+ changedOutputs: string[];
1907
+ }
1908
+ /** Build the deterministic companion metadata written beside a derived bundle. */
1909
+ declare function createArtifactBundleManifest(manifestPath: string, outputs: ArtifactFile[], provenance: ArtifactProvenance): ArtifactBundleManifest;
1910
+ /** Add deterministic provenance metadata to a related set of generated files. */
1911
+ declare function withArtifactManifest(manifestPath: string, outputs: ArtifactFile[], provenance: ArtifactProvenance): ArtifactFile[];
1912
+ /**
1913
+ * Persist a generated bundle while protecting files not demonstrably owned by
1914
+ * its existing companion manifest. Byte-identical legacy files are adopted
1915
+ * without rewriting; `force` is required to take over anything else.
1916
+ */
1917
+ declare function writeManagedArtifactBundle(manifestPath: string, outputs: ArtifactFile[], provenance: ArtifactProvenance, options?: ManagedArtifactBundleOptions): Promise<ArtifactBundleResult>;
1918
+ /** Verify stored provenance and source/output hashes without rebuilding output. */
1919
+ declare function verifyArtifactBundle(manifestPath: string): Promise<ArtifactVerification>;
1920
+ /**
1921
+ * Write a related set of generated files without leaving a partial bundle
1922
+ * after an ordinary write failure.
1923
+ *
1924
+ * All changed members are staged before any destination is replaced. If an
1925
+ * ordinary filesystem error interrupts promotion, prior files are restored.
1926
+ * Byte-identical members are not rewritten.
1927
+ */
1928
+ declare function writeArtifactBundle(files: ArtifactFile[], options?: ArtifactBundleOptions): Promise<ArtifactBundleResult>;
1929
+
1930
+ type PlanState = "ok" | "missing" | "untracked" | "stale" | "orphaned" | "in-flight" | "recoverable" | "failed";
1931
+ interface PlanItem {
1932
+ spec: ResolvedSpec;
1933
+ key: string;
1934
+ state: PlanState;
1935
+ reason: string;
1936
+ }
1937
+ interface Plan {
1938
+ items: PlanItem[];
1939
+ /** Items that would incur provider cost if the plan were executed. */
1940
+ actionable: PlanItem[];
1941
+ cost: number;
1942
+ costUnit: CostUnit;
1943
+ candidates: number;
1944
+ }
1945
+ /**
1946
+ * Diffs the manifest against the lockfile and the files on disk. Nothing here
1947
+ * touches the network, so it is safe to run constantly — it is the cheap
1948
+ * "what would this cost me?" question that should precede every real run.
1949
+ */
1950
+ declare function buildPlan(specs: ResolvedSpec[], lock: Lock, opts?: {
1951
+ force?: boolean;
1952
+ }): Promise<Plan>;
1953
+ declare function summarize(plan: Plan): Record<PlanState, number>;
1954
+
1955
+ interface PaletteEntry {
1956
+ r: number;
1957
+ g: number;
1958
+ b: number;
1959
+ /** Share of opaque pixels using this colour, 0-1. */
1960
+ weight: number;
1961
+ }
1962
+
1963
+ /**
1964
+ * Perceptual-ish distance between two colours, 0-255ish.
1965
+ *
1966
+ * "Redmean" — a cheap weighting that tracks human perception far better than
1967
+ * raw RGB euclidean and needs no colour-space conversion or dependency. Good
1968
+ * enough to rank outliers, which is all this is for; it is not colourimetry.
1969
+ */
1970
+ declare function colorDistance(a: {
1971
+ r: number;
1972
+ g: number;
1973
+ b: number;
1974
+ }, b: {
1975
+ r: number;
1976
+ g: number;
1977
+ b: number;
1978
+ }): number;
1979
+ /**
1980
+ * How far an asset's palette sits from a reference palette.
1981
+ *
1982
+ * For each colour in the asset, weighted by how much of the image it covers,
1983
+ * take the distance to its nearest reference colour. An asset built entirely
1984
+ * from on-style colours scores ~0; one introducing a foreign hue scores high
1985
+ * in proportion to how much of the image that hue occupies.
1986
+ */
1987
+ declare function paletteDistance(asset: PaletteEntry[], reference: PaletteEntry[]): number;
1988
+ /** Merge many palettes into one, summing weights for repeated colours. */
1989
+ declare function mergePalettes(palettes: PaletteEntry[][], topN?: number): PaletteEntry[];
1990
+ interface AssetAudit {
1991
+ assetId: string;
1992
+ /** Present when this is one member of a structural multi-output asset. */
1993
+ outputRole?: string;
1994
+ /** Stable report/atlas identity: assetId for one output, assetId/role for many. */
1995
+ id: string;
1996
+ file: string;
1997
+ width: number;
1998
+ height: number;
1999
+ /** Distance from the style's reference palette. Higher is more off-style. */
2000
+ paletteDistance: number;
2001
+ /** Share of the canvas that is transparent. Very low suggests a baked background. */
2002
+ transparency: number;
2003
+ /** Distinct opaque colours. Very high suggests photo-like rendering, not pixel art. */
2004
+ colorCount: number;
2005
+ palette: PaletteEntry[];
2006
+ }
2007
+ interface StyleAudit {
2008
+ styleId: string;
2009
+ /** True when the reference came from the style's own styleImages. */
2010
+ referenceFromStyleImages: boolean;
2011
+ reference: PaletteEntry[];
2012
+ assets: AssetAudit[];
2013
+ missing: string[];
2014
+ unreadable: string[];
2015
+ }
2016
+ interface AuditThresholds {
2017
+ /** Absolute palette-distance ceiling. Omit to rely on relative outliers. */
2018
+ maxDistance?: number;
2019
+ /** Minimum transparent share of the canvas, from 0 to 1. */
2020
+ minTransparency?: number;
2021
+ /** Maximum number of distinct opaque colors. */
2022
+ maxColors?: number;
2023
+ /** Relative outlier cutoff. Defaults to 1.5 standard deviations. */
2024
+ sigma?: number;
2025
+ }
2026
+ interface AuditViolation {
2027
+ id: string;
2028
+ reasons: string[];
2029
+ }
2030
+ interface AuditEvaluation {
2031
+ safe: boolean;
2032
+ thresholds: Required<Pick<AuditThresholds, "sigma">> & Omit<AuditThresholds, "sigma">;
2033
+ outliers: string[];
2034
+ violations: AuditViolation[];
2035
+ missing: string[];
2036
+ unreadable: string[];
2037
+ }
2038
+ /**
2039
+ * Measures how consistently a style's assets actually hold together.
2040
+ *
2041
+ * This exists to answer "is this variant working?" with a number instead of an
2042
+ * eyeball. Measured on a real neon trial, prose alone carried the style on
2043
+ * subjects with no strong inherent colour but was ignored on ones that had
2044
+ * some — a chocolate bar stayed brown, a camera stayed grey. Those are exactly
2045
+ * the assets this ranks to the top, before another 57 are generated to match.
2046
+ *
2047
+ * Reference palette comes from the style's `styleImages` when set, since those
2048
+ * are the declared intent. Otherwise the assets are compared against their own
2049
+ * collective palette, which still surfaces outliers but cannot tell you the
2050
+ * whole set has drifted together.
2051
+ */
2052
+ declare function auditStyle(loaded: LoadedManifest, specs: ResolvedSpec[], styleId: string, lock?: Lock): Promise<StyleAudit>;
2053
+ /** Assets more than `sigma` standard deviations off the mean distance. */
2054
+ declare function outliers(audit: StyleAudit, sigma?: number): AssetAudit[];
2055
+ /** Turn an audit into a stable CI decision without hiding the underlying measurements. */
2056
+ declare function evaluateAudit(audit: StyleAudit, thresholds?: AuditThresholds): AuditEvaluation;
2057
+ declare function hex(c: {
2058
+ r: number;
2059
+ g: number;
2060
+ b: number;
2061
+ }): string;
2062
+
2063
+ interface ContentCacheIssue {
2064
+ name: string;
2065
+ reason: string;
2066
+ }
2067
+ interface ContentCacheHealth {
2068
+ path: string;
2069
+ exists: boolean;
2070
+ files: number;
2071
+ bytes: number;
2072
+ valid: number;
2073
+ referenced: number;
2074
+ unreferenced: string[];
2075
+ missingReferenced: string[];
2076
+ invalid: ContentCacheIssue[];
2077
+ }
2078
+ interface RemoteHashCacheHealth {
2079
+ path: string;
2080
+ exists: boolean;
2081
+ entries: number;
2082
+ valid: number;
2083
+ invalidIds: string[];
2084
+ error: string | null;
2085
+ }
2086
+ interface CacheHealthReport {
2087
+ safe: boolean;
2088
+ content: ContentCacheHealth;
2089
+ remoteHashes: RemoteHashCacheHealth;
2090
+ removed: {
2091
+ contentFiles: number;
2092
+ remoteHashEntries: number;
2093
+ resetRemoteHashCache: boolean;
2094
+ };
2095
+ }
2096
+ interface CacheHealthOptions {
2097
+ /** Remove invalid and unreferenced content, plus invalid remote hash entries. */
2098
+ prune?: boolean;
2099
+ }
2100
+ /**
2101
+ * Inspect both local caches without contacting the provider.
2102
+ *
2103
+ * Content PNGs are project-local and keyed by lockfile SHA, so orphaned bytes
2104
+ * can be removed safely. The object-hash cache is account-wide: this function
2105
+ * validates its schema and hashes but never removes an object merely because
2106
+ * the current project does not reference it. `adopt` owns that live-ID prune,
2107
+ * because only it has walked the complete provider account.
2108
+ */
2109
+ declare function inspectCaches(lock: Lock, lockPath: string, options?: CacheHealthOptions): Promise<CacheHealthReport>;
2110
+
2111
+ interface PackedSource {
2112
+ id: string;
2113
+ /** Absolute source path. Artifact manifests make it relative to themselves. */
2114
+ path: string;
2115
+ /** Null when the source could not be read. */
2116
+ sha256: string | null;
2117
+ /** False when the source was present but could not be decoded or selected. */
2118
+ included: boolean;
2119
+ }
2120
+ interface PackedFrame {
2121
+ /** Asset id, so a consumer can look a sprite up by name rather than index. */
2122
+ id: string;
2123
+ x: number;
2124
+ y: number;
2125
+ width: number;
2126
+ height: number;
2127
+ }
2128
+ interface PackedSheet {
2129
+ png: Buffer;
2130
+ atlas: {
2131
+ style: string;
2132
+ sheet: {
2133
+ width: number;
2134
+ height: number;
2135
+ };
2136
+ /** Uniform cell the grid was laid out on. Frames may be smaller. */
2137
+ cell: {
2138
+ width: number;
2139
+ height: number;
2140
+ };
2141
+ columns: number;
2142
+ frames: PackedFrame[];
2143
+ };
2144
+ /** Assets skipped because their file was missing or unreadable. */
2145
+ skipped: {
2146
+ id: string;
2147
+ reason: string;
2148
+ }[];
2149
+ /** Inputs and their exact bytes, for deterministic derived-artifact provenance. */
2150
+ sources: PackedSource[];
2151
+ }
2152
+ /**
2153
+ * Composites one style's sprites into a single sheet plus a frame atlas.
2154
+ *
2155
+ * A grid rather than a bin-packer. Every sprite in a style shares a generator
2156
+ * and a size, so rectangles are near-uniform and the gain from tight packing
2157
+ * is a few percent of area — not worth the loss of a stable, predictable
2158
+ * layout. A grid also means a frame's position is derivable from its index,
2159
+ * which matters when someone is reading the sheet by eye to debug it.
2160
+ *
2161
+ * The cell is the largest sprite in the set. Assets can override width/height
2162
+ * individually, so assuming uniformity would silently clip the odd one out.
2163
+ * Smaller sprites are placed at the cell's top-left and their real dimensions
2164
+ * recorded, rather than being centred: centring would bake half-pixel offsets
2165
+ * into odd-sized differences, and a consumer that ignores the atlas and slices
2166
+ * on the cell grid still gets a correct — if padded — sprite.
2167
+ */
2168
+ interface SpriteInput {
2169
+ /** The name this sprite is looked up by in the atlas. */
2170
+ id: string;
2171
+ /** Absolute path to the PNG. */
2172
+ path: string;
2173
+ }
2174
+ /**
2175
+ * Validates and resolves the JSON contract behind `pack --inputs`.
2176
+ *
2177
+ * A plain function rather than inline in the CLI so it is testable without
2178
+ * spawning the binary: `main()` in cli.ts runs at module load and is not
2179
+ * exported, so anything left inline there has no seam a test can reach. This
2180
+ * is also the only place that needs to know the file is JSON at all — once it
2181
+ * returns, everything downstream just deals in `SpriteInput[]`.
2182
+ *
2183
+ * `entry.path` resolves relative to `inputsFilePath`'s directory, not the
2184
+ * process's cwd, so a caller can write the JSON into a scratch directory
2185
+ * without rewriting every entry to be absolute. An already-absolute
2186
+ * `entry.path` passes through unchanged, since `path.resolve` discards
2187
+ * earlier segments once it hits one.
2188
+ */
2189
+ declare function resolvePackInputs(raw: unknown, inputsFilePath: string): SpriteInput[];
2190
+ /**
2191
+ * The packing primitive: an explicit list of sprites in, one sheet out.
2192
+ *
2193
+ * Separate from `packStyle` because the lockfile is not the only way to decide
2194
+ * what belongs on a sheet. A consumer may draw from several manifests, or key
2195
+ * frames by its own vocabulary rather than by pixelkiln asset ids — heybud's
2196
+ * review-form icons do both. Keeping the pixel work here and the "which files,
2197
+ * called what" decision at the call site avoids teaching this module about
2198
+ * anyone else's naming.
2199
+ */
2200
+ declare function packSprites(inputs: SpriteInput[], options?: {
2201
+ columns?: number;
2202
+ order?: "id" | "input";
2203
+ }): PackedSheet;
2204
+ declare function packStyle(lock: Lock, styleId: string, manifestDir: string, options?: {
2205
+ columns?: number;
2206
+ outputRoles?: string[];
2207
+ primaryOnly?: boolean;
2208
+ }): PackedSheet;
2209
+ interface MountPlacement extends SpriteInput {
2210
+ /** Grid cell this sprite owns, as [column, row]. */
2211
+ cell: [number, number];
2212
+ }
2213
+ interface MountedSheet {
2214
+ png: Buffer;
2215
+ atlas: {
2216
+ style: string;
2217
+ sheet: {
2218
+ width: number;
2219
+ height: number;
2220
+ };
2221
+ cell: {
2222
+ width: number;
2223
+ height: number;
2224
+ };
2225
+ frames: PackedFrame[];
2226
+ };
2227
+ skipped: {
2228
+ id: string;
2229
+ reason: string;
2230
+ }[];
2231
+ /** Inputs and their exact bytes, for deterministic derived-artifact provenance. */
2232
+ sources: PackedSource[];
2233
+ /** True when an existing sheet was composited into rather than replaced. */
2234
+ overBase: boolean;
2235
+ }
2236
+ /**
2237
+ * Places sprites at *declared* grid cells, optionally into an existing sheet.
2238
+ *
2239
+ * The sibling of `packSprites`, for the case its layout rules cannot serve.
2240
+ * `packSprites` derives position from index and sorts by asset id so the sheet
2241
+ * is byte-stable — excellent when pixelkiln owns the whole sheet, and fatal
2242
+ * when it does not. A consumer whose atlas coordinates are already load-bearing
2243
+ * (a tile engine naming tiles by cell, a scene file storing cell indices in
2244
+ * saved data) cannot accept a layout that moves when an asset is added or
2245
+ * renamed: every existing reference would silently point at different art.
2246
+ *
2247
+ * So here the caller declares the cell and pixelkiln honours it. Two
2248
+ * consequences worth stating:
2249
+ *
2250
+ * - **Only declared cells are touched.** With a `base` sheet, every other
2251
+ * pixel survives byte-for-byte, so a hand-authored sheet can be part
2252
+ * generated and part drawn without the generated half claiming the file.
2253
+ * - **A cell is replaced, not blended.** The sprite owns its cell, so the
2254
+ * cell is cleared first. Compositing instead would make a regenerated
2255
+ * tile show through to whatever it replaced, and the residue would be
2256
+ * invisible until it shipped.
2257
+ *
2258
+ * Sprites larger than the cell are a declaration error, not something to crop
2259
+ * silently — cropping would produce a sheet that looks right in isolation and
2260
+ * is wrong at every seam.
2261
+ */
2262
+ declare function mountSprites(placements: MountPlacement[], options: {
2263
+ cellWidth: number;
2264
+ cellHeight: number;
2265
+ basePng?: Buffer;
2266
+ }): MountedSheet;
2267
+ /**
2268
+ * `mountSprites` driven by the lockfile and the manifest's `mount` block.
2269
+ *
2270
+ * The manifest is the source of cells rather than the lockfile: a cell is a
2271
+ * declaration about where art belongs, not a record of what was generated, and
2272
+ * it has to be readable and editable before anything has been generated at all.
2273
+ */
2274
+ declare function mountStyle(lock: Lock, styleId: string, manifestDir: string, mount: {
2275
+ base?: string;
2276
+ cellWidth: number;
2277
+ cellHeight: number;
2278
+ }, cells: Record<string, [number, number]>, sources?: Record<string, string>, outputRoles?: Record<string, string>): MountedSheet;
2279
+
2280
+ type TilesetFormat = "generic" | "tiled" | "godot";
2281
+ interface NormalizedTileRules {
2282
+ ruleType: "edge" | "corner" | "outline";
2283
+ arity: 4 | 6;
2284
+ connectivity?: "same" | "other";
2285
+ terrains: string[];
2286
+ /** Provider tile index to adjacency mask. Tiles absent here are stamp-only. */
2287
+ masks: Record<number, number>;
2288
+ raw: Record<string, unknown>;
2289
+ }
2290
+ interface GenericTileset {
2291
+ format: "pixelkiln-tileset";
2292
+ version: 1;
2293
+ name: string;
2294
+ style: string;
2295
+ asset: string;
2296
+ image: string;
2297
+ tile: {
2298
+ width: number;
2299
+ height: number;
2300
+ };
2301
+ sheet: {
2302
+ width: number;
2303
+ height: number;
2304
+ columns: number;
2305
+ };
2306
+ tiles: Array<{
2307
+ id: number;
2308
+ /** Original provider index; can differ from id when stamp-only keys leave gaps. */
2309
+ sourceIndex: number;
2310
+ role: string;
2311
+ x: number;
2312
+ y: number;
2313
+ width: number;
2314
+ height: number;
2315
+ bitmask?: number;
2316
+ stampOnly: boolean;
2317
+ }>;
2318
+ /** Complete provider object, even when its rule family is not normalized yet. */
2319
+ providerRules: Record<string, unknown> | null;
2320
+ rules: NormalizedTileRules | null;
2321
+ }
2322
+ interface TilesetExport {
2323
+ png: Buffer;
2324
+ extension: ".json" | ".tsj" | ".tres";
2325
+ document: string;
2326
+ generic: GenericTileset;
2327
+ sources: PackedSource[];
2328
+ }
2329
+ interface TilesetExportOptions {
2330
+ format: TilesetFormat;
2331
+ manifestDir: string;
2332
+ imageName: string;
2333
+ columns?: number;
2334
+ }
2335
+ /** Parse the deliberately open provider schema into the stable export subset. */
2336
+ declare function normalizeTileRules(raw: unknown): NormalizedTileRules | null;
2337
+ /** Build an atlas plus generic, Tiled TSJ, or Godot 4 TileSet metadata. */
2338
+ declare function exportTileset(entry: LockEntry, spec: ResolvedSpec, options: TilesetExportOptions): TilesetExport;
2339
+
2340
+ interface SubmitOptions {
2341
+ maxInFlight?: number;
2342
+ spacingMs?: number;
2343
+ /** How often to re-check occupied background-job slots. */
2344
+ slotPollMs?: number;
2345
+ /** Stop waiting for an unreadable/stuck slot instead of hanging forever. */
2346
+ slotTimeoutMs?: number;
2347
+ /** Refuse to submit if the run would exceed this much, in the provider's unit. */
2348
+ budget?: number;
2349
+ onProgress?: (msg: string) => void;
2350
+ }
2351
+ interface SubmitResult {
2352
+ submitted: number;
2353
+ failed: number;
2354
+ /** Successful-submission estimates in `unit`. */
2355
+ spent: number;
2356
+ unit: CostUnit;
2357
+ }
2358
+ declare function submit(provider: Provider, loaded: LoadedManifest, items: PlanItem[], lock: Lock, lockPath: string, opts?: SubmitOptions): Promise<SubmitResult>;
2359
+
2360
+ interface PollOptions {
2361
+ intervalMs?: number;
2362
+ timeoutMs?: number;
2363
+ onProgress?: (msg: string) => void;
2364
+ specs?: ResolvedSpec[];
2365
+ }
2366
+ interface PollResult {
2367
+ review: number;
2368
+ completed: number;
2369
+ failed: number;
2370
+ stillRunning: number;
2371
+ }
2372
+ /**
2373
+ * Advances every unfinished lock entry to its settled state.
2374
+ *
2375
+ * Safe to run repeatedly and safe to interrupt — all state lives in the
2376
+ * lockfile, so a re-run picks up exactly where the last one stopped. Provider
2377
+ * quirks (a job record that expires before its image does, review-status
2378
+ * candidate lists) are the provider's problem, not this loop's.
2379
+ */
2380
+ declare function poll(provider: Provider, lock: Lock, lockPath: string, opts?: PollOptions): Promise<PollResult>;
2381
+
2382
+ interface FetchResult {
2383
+ downloaded: number;
2384
+ skipped: number;
2385
+ failed: number;
2386
+ }
2387
+ /**
2388
+ * Downloads every selected object to its manifest-defined path and records the
2389
+ * file hash. The hash is what lets `plan` tell "untouched" from "edited by
2390
+ * hand" on later runs, so a manual retouch is never silently clobbered. The
2391
+ * same hash keys a local content cache, allowing `restore` to outlive a
2392
+ * provider's temporary storage URL.
2393
+ */
2394
+ declare function fetchAssets(provider: Provider, specs: ResolvedSpec[], lock: Lock, lockPath: string, opts?: {
2395
+ onProgress?: (msg: string) => void;
2396
+ concurrency?: number;
2397
+ repair?: boolean;
2398
+ /** Content-addressed PNG cache. Defaults beside the lockfile; false disables it. */
2399
+ cacheDir?: string | false;
2400
+ }): Promise<FetchResult>;
2401
+ /**
2402
+ * Applies the manifest's tags to each generated object upstream. Tagging is
2403
+ * free and synchronous, and it makes the account itself queryable — the thing
2404
+ * that was missing when 350 objects accumulated with no way to tell which 65
2405
+ * were the keepers.
2406
+ */
2407
+ declare function pushTags(provider: Provider, specs: ResolvedSpec[], lock: Lock, opts?: {
2408
+ onProgress?: (msg: string) => void;
2409
+ }): Promise<number>;
2410
+
2411
+ type DoctorLevel = "ok" | "warning" | "error";
2412
+ interface DoctorCheck {
2413
+ id: string;
2414
+ level: DoctorLevel;
2415
+ message: string;
2416
+ }
2417
+ interface DoctorReport {
2418
+ ok: boolean;
2419
+ checks: DoctorCheck[];
2420
+ }
2421
+ interface DoctorOptions {
2422
+ provider?: Provider;
2423
+ /** Skip provider connectivity while retaining every local check. */
2424
+ offline?: boolean;
2425
+ apiKeyPresent?: boolean;
2426
+ }
2427
+ /**
2428
+ * Checks the whole project without changing it. Loading/resolving the manifest
2429
+ * and loading the lock happen before this function, so schema, style-image,
2430
+ * filter, and output-collision validation have already passed if it runs.
2431
+ */
2432
+ declare function doctor(loaded: LoadedManifest, specs: ResolvedSpec[], lock: Lock, lockPath: string, opts?: DoctorOptions): Promise<DoctorReport>;
2433
+
2434
+ interface AdoptResult {
2435
+ scanned: number;
2436
+ matched: number;
2437
+ unmatchedLocal: string[];
2438
+ /** Objects on the account that correspond to no local file — safe to delete. */
2439
+ unmatchedRemote: RemoteAsset[];
2440
+ ambiguous: string[];
2441
+ }
2442
+ /**
2443
+ * Reconciles an account full of previously generated objects against the files
2444
+ * already committed in the repo, and writes the mapping into the lockfile.
2445
+ *
2446
+ * Matching is by exact SHA-256 of the image bytes. That was verified to hold:
2447
+ * a file downloaded from PixelLab storage is byte-identical to the copy sitting
2448
+ * in the repo, so a hash match is proof of provenance rather than a guess. No
2449
+ * fuzzy prompt matching is involved and nothing is regenerated.
2450
+ */
2451
+ declare function adopt(provider: Provider, specs: ResolvedSpec[], lock: Lock, lockPath: string, opts?: {
2452
+ onProgress?: (msg: string) => void;
2453
+ concurrency?: number;
2454
+ noCache?: boolean;
2455
+ }): Promise<AdoptResult>;
2456
+ /** Applies manifest tags to every adopted object so the account becomes filterable. */
2457
+ declare function tagAdopted(provider: Provider, specs: ResolvedSpec[], lock: Lock, opts?: {
2458
+ onProgress?: (msg: string) => void;
2459
+ }): Promise<number>;
2460
+
2461
+ interface PickResult {
2462
+ selected: number;
2463
+ skipped: number;
2464
+ }
2465
+ /**
2466
+ * Serves the contact sheet on localhost, waits for the selections to be
2467
+ * applied, then shuts down.
2468
+ *
2469
+ * Selections are committed to PixelLab (`select-frames` promotes the chosen
2470
+ * candidate to its own object and drops the rest) and written to the lockfile
2471
+ * before the browser gets its response, so a closed tab never loses a choice.
2472
+ */
2473
+ declare function runPicker(provider: Provider, lock: Lock, lockPath: string, opts?: {
2474
+ port?: number;
2475
+ open?: boolean;
2476
+ onProgress?: (msg: string) => void;
2477
+ }): Promise<PickResult>;
2478
+
2479
+ interface SheetGroup {
2480
+ key: string;
2481
+ assetId: string;
2482
+ styleId: string;
2483
+ prompt: string;
2484
+ reviewObjectId: string;
2485
+ frameUrls: string[];
2486
+ size: number;
2487
+ }
2488
+ /**
2489
+ * A contact sheet for choosing among generated candidates.
2490
+ *
2491
+ * This is the only step that needs human judgement, so it is optimised for one
2492
+ * thing: deciding fast. Candidates render at 4x with a transparency
2493
+ * checkerboard and a true-size swatch beside them, because a pixel icon that
2494
+ * looks good enlarged can still be unreadable at its real size. Keyboard-first,
2495
+ * one row per asset, and the page posts back and closes itself.
2496
+ */
2497
+ declare function renderSheet(groups: SheetGroup[]): string;
2498
+
2499
+ /** Minimal PNG header read — avoids pulling in an image library just for dimensions. */
2500
+ declare function pngSize(buf: Buffer): {
2501
+ width: number;
2502
+ height: number;
2503
+ } | null;
2504
+ /** Path segment → a stable, readable asset id. */
2505
+ declare function slugify(value: string): string;
2506
+ interface ScannedAsset {
2507
+ id: string;
2508
+ category: string;
2509
+ file: string;
2510
+ width: number;
2511
+ height: number;
2512
+ }
2513
+ /**
2514
+ * Scans an existing asset tree and produces manifest entries for it.
2515
+ *
2516
+ * Prompts are left empty on purpose. For a project whose art already exists,
2517
+ * the accurate prompts are the ones actually used upstream — `adopt
2518
+ * --write-prompts` recovers those from the matched objects rather than having
2519
+ * anyone invent plausible-looking replacements.
2520
+ */
2521
+ declare function scanAssets(root: string, opts?: {
2522
+ exclude?: string[];
2523
+ }): Promise<{
2524
+ assets: ScannedAsset[];
2525
+ skipped: string[];
2526
+ }>;
2527
+ declare function buildManifest(name: string, styleId: string, generator: Generator, outDir: string, scanned: ScannedAsset[]): Manifest;
2528
+
2529
+ /**
2530
+ * Objects on the account that no known lockfile claims.
2531
+ *
2532
+ * The point of this is recovery, not cleanup. An account accumulates work that
2533
+ * was generated, paid for, and never landed in a repo — alternate takes,
2534
+ * abandoned experiments, whole categories that were explored and forgotten.
2535
+ * Measured on one account: 190 of 361 objects were unclaimed, and a visual
2536
+ * sample showed usable character portraits, tree variants, terrain tiles, and
2537
+ * UI icons rather than rejects.
2538
+ *
2539
+ * Correctness here depends entirely on being given EVERY lockfile. One account
2540
+ * is shared across projects, so an incomplete claim set makes another project's
2541
+ * shipped art look like an orphan.
2542
+ */
2543
+ declare function loadClaims(lockPaths: string[]): Promise<Set<string>>;
2544
+ interface Orphan {
2545
+ id: string;
2546
+ prompt: string;
2547
+ width: number;
2548
+ height: number;
2549
+ createdAt: string;
2550
+ previewUrl: string;
2551
+ tags: string[];
2552
+ }
2553
+ declare function findOrphans(provider: Provider, claimed: Set<string>, opts?: {
2554
+ onProgress?: (msg: string) => void;
2555
+ }): Promise<{
2556
+ orphans: Orphan[];
2557
+ total: number;
2558
+ }>;
2559
+ /**
2560
+ * Which manifest style, if any, an orphan's prompt was generated from.
2561
+ *
2562
+ * A manifest with only one style skips the pattern check entirely — there is
2563
+ * nothing to disambiguate, and a style with an empty prefix/suffix (common
2564
+ * for single-style projects that don't template their prompts, like a
2565
+ * `map`-generator asset pack) would otherwise match nothing at all. This is
2566
+ * a routing decision ("where would salvage import this"), not a claim that
2567
+ * the content is genuinely this project's — see `groupOrphansByStyle`'s
2568
+ * `siblings` param for that distinction, which single-style manifests need
2569
+ * and multi-style ones get from the plain pattern check already.
2570
+ */
2571
+ declare function matchOrphanStyle(prompt: string, manifest: Manifest): string | null;
2572
+ /** A sibling project's manifest, consulted only to recognise its own style
2573
+ * patterns — never as an import target. */
2574
+ interface SiblingManifest {
2575
+ /** How to refer to this sibling in output — a project directory name, typically. */
2576
+ label: string;
2577
+ manifest: Manifest;
2578
+ }
2579
+ interface OrphanGroups {
2580
+ /** styleId → its matched orphans, in manifest style order. */
2581
+ matched: Map<string, Orphan[]>;
2582
+ /** Orphans that matched no style anywhere known — this manifest or any sibling. */
2583
+ unmatched: Orphan[];
2584
+ /** "<sibling label>: <styleId>" → orphans that confidently matched a
2585
+ * SIBLING's own pattern instead of this manifest's — excluded from
2586
+ * `matched` even where this manifest would otherwise have swallowed them
2587
+ * by default (a single-style manifest with nothing of its own to check
2588
+ * against). Empty unless `siblings` was passed to `groupOrphansByStyle`. */
2589
+ elsewhere: Map<string, Orphan[]>;
2590
+ }
2591
+ /**
2592
+ * Splits orphans by the style that most likely produced them.
2593
+ *
2594
+ * `salvage` used to hand a single `--style` to a whole session regardless of
2595
+ * how many the orphan pool actually spanned, so importing anything wrong
2596
+ * silently mislabeled it under the first style in the manifest. Grouping
2597
+ * first lets the caller run one correctly-scoped session per style instead.
2598
+ *
2599
+ * A single-style manifest has no pattern of its own to filter by, so by
2600
+ * default everything routes to that one style — correct for a genuinely
2601
+ * single-project account, wrong for a shared one where the orphan pool is
2602
+ * mostly a sibling's art. Passing that sibling's manifest via `siblings` (its
2603
+ * lockfile sits beside it, by the same convention `--lock` defaults from
2604
+ * `--manifest`) lets a confident match against ITS pattern win instead,
2605
+ * pulling those orphans out of `matched` and into `elsewhere` even though
2606
+ * this manifest would otherwise have claimed them.
2607
+ */
2608
+ declare function groupOrphansByStyle(orphans: Orphan[], manifest: Manifest, siblings?: SiblingManifest[]): OrphanGroups;
2609
+ declare function idFromPrompt(prompt: string, taken: Set<string>): string;
2610
+ type SalvageAction = "import" | "keep" | "discard";
2611
+ interface SalvageDecision {
2612
+ id: string;
2613
+ action: SalvageAction;
2614
+ }
2615
+ /**
2616
+ * Tags are the durable record of a decision. They are free and synchronous, and
2617
+ * unlike a local file they survive on the account itself — so a later salvage
2618
+ * run from a different machine sees what was already triaged.
2619
+ *
2620
+ * `discard` deliberately only tags. Deleting is a separate, explicit command.
2621
+ */
2622
+ declare function applyTags(provider: Provider, decisions: SalvageDecision[], existing: Map<string, string[]>, opts?: {
2623
+ onProgress?: (msg: string) => void;
2624
+ }): Promise<{
2625
+ tagged: number;
2626
+ failed: number;
2627
+ }>;
2628
+
2629
+ interface SalvageResult {
2630
+ imported: number;
2631
+ kept: number;
2632
+ discarded: number;
2633
+ failed: number;
2634
+ }
2635
+ /**
2636
+ * Serves the triage sheet and applies decisions.
2637
+ *
2638
+ * `import` is the only action that touches local state: it downloads the image,
2639
+ * writes a manifest asset and a lock entry, so a recovered sprite becomes a
2640
+ * first-class tracked asset rather than a loose file. `keep` and `discard` only
2641
+ * write tags upstream — no object is ever deleted here.
2642
+ */
2643
+ declare function runSalvage(provider: Provider, orphans: Orphan[], ctx: {
2644
+ manifestPath: string;
2645
+ manifest: Manifest;
2646
+ styleId: string;
2647
+ importDir: string;
2648
+ lock: Lock;
2649
+ lockPath: string;
2650
+ }, opts?: {
2651
+ port?: number;
2652
+ open?: boolean;
2653
+ onProgress?: (msg: string) => void;
2654
+ }): Promise<SalvageResult>;
2655
+
2656
+ interface SalvageSheetContext {
2657
+ /** The style every "import" on this page will be tagged and routed under. */
2658
+ styleId: string;
2659
+ /** Where an import lands, shown so the page answers "where does this go"
2660
+ * without anyone needing to go check the manifest. */
2661
+ importDir: string;
2662
+ }
2663
+ /**
2664
+ * Triage sheet for unclaimed account objects.
2665
+ *
2666
+ * Optimised for volume: a couple of hundred items, most of which get a
2667
+ * one-keystroke verdict. Everything defaults to no decision, so closing the tab
2668
+ * changes nothing, and `discard` only ever writes a tag — deletion is a
2669
+ * separate command that has to be asked for by name.
2670
+ *
2671
+ * `salvage` opens one tab per matched style in sequence (grouping — see
2672
+ * pipeline/salvage.ts), and every one of them used to render the exact same
2673
+ * generic title. With several left open across styles there was no way to
2674
+ * tell them apart short of squinting at which images loaded. The tab title
2675
+ * and header now name the style the page is actually scoped to.
2676
+ */
2677
+ declare function renderSalvageSheet(orphans: Orphan[], ctx?: SalvageSheetContext): string;
2678
+
2679
+ export { type AdoptResult, type ArtifactBundleManifest, type ArtifactBundleOptions, type ArtifactBundleResult, type ArtifactFile, type ArtifactProvenance, type ArtifactSource, type ArtifactVerification, type Asset, type AssetAudit, AssetSchema, type AuditEvaluation, type AuditThresholds, type AuditViolation, type Balance, type BalanceChange, type BalanceInfo, type CacheHealthOptions, type CacheHealthReport, type ContentCacheHealth, type ContentCacheIssue, type CostEstimate, type CostUnit, DEFAULT_RATE_LIMIT, type DoctorCheck, type DoctorLevel, type DoctorOptions, type DoctorReport, FAKE_PNG, type FakeOptions, FakeProvider, type FetchResult, type Generator, GeneratorSchema, type GenericTileset, type JobState, type LoadedManifest, type Lock, type LockEntry, LockEntrySchema, type LockOutput, LockSchema, MAX_DOWNLOAD_BYTES, MAX_RETRIES, type ManagedArtifactBundleOptions, type Manifest, ManifestSchema, type MapObject, type MountPlacement, type MountedSheet, type NormalizedTileRules, type Orphan, type OrphanGroups, type OutputSelection, type OutputSource, type PackedFrame, type PackedSheet, type PackedSource, type PickResult, PixelLabClient, PixelLabError, type PixelLabObject, PixelLabProvider, type Plan, type PlanItem, type PlanState, type PollContext, type PollOptions, type PollResult, type Provider, type ProviderMetadata, type RateLimit, type RemoteAsset, type RemoteHashCacheHealth, type ResolvedOutput, type ResolvedSpec, type ResolvedStyleImage, type SalvageAction, type SalvageResult, type ScannedAsset, type SheetGroup, type SiblingManifest, type SpriteInput, type Style, type StyleAudit, StyleSchema, type SubmitOptions, type SubmitResult, type TilesPro, type TilesetExport, type TilesetExportOptions, type TilesetFormat, UnsupportedCapabilityError, adopt, applyTags, auditStyle, backoffMs, buildManifest, buildPlan, candidateCount, clientFromEnv, colorDistance, countNumberedDescriptions, createArtifactBundleManifest, currentEntryOutputPath, currentOutputPath, doctor, evaluateAudit, expectedOutputPath, exportTileset, fallbackOutputRole, fetchAssets, findOrphans, formatCost, generationCost, groupOrphansByStyle, hex, idFromPrompt, imageMetadata, inspectCaches, loadClaims, loadLock, loadManifest, lockKey, matchOrphanStyle, measureBalanceChange, mergePalettes, mountSprites, mountStyle, normalizeLockOutputPaths, normalizeTileRules, outliers, outputId, packSprites, packStyle, paletteDistance, parseLock, pngSize, poll, portableOutputPath, primaryOutput, pushTags, remove, renderSalvageSheet, renderSheet, requireDelete, requireList, resolveEntryOutputs, resolveOutputPath, resolvePackInputs, resolveSpecEntryOutputs, resolveSpecOutputs, resolveSpecs, resolveStyleImages, resolveStyleOutputs, retryAfterMs, runPicker, runSalvage, saveLock, scanAssets, selectEntryOutput, sha256, sha256File, shouldRetry, slugify, specHash, spendByUnit, styleImagesBase64, submit, summarize, tagAdopted, tileFeatureOutputCount, tileVariationCount, tilesCost, totalSpend, upsert, validateCostEstimate, verifyArtifactBundle, withArtifactManifest, writeArtifactBundle, writeManagedArtifactBundle };