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.
- package/CONTRIBUTING.md +92 -0
- package/LICENSE +21 -0
- package/NAMING.md +87 -0
- package/PROVIDERS.md +96 -0
- package/README.md +333 -0
- package/SECURITY.md +38 -0
- package/bin/pixelkiln.js +2 -0
- package/dist/cli.d.ts +47 -0
- package/dist/cli.js +5889 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +5129 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2679 -0
- package/dist/index.d.ts +2679 -0
- package/dist/index.js +4990 -0
- package/dist/index.js.map +1 -0
- package/docs/AGENTS.md +64 -0
- package/docs/ARCHITECTURE.md +120 -0
- package/docs/ARTIFACTS.md +162 -0
- package/docs/CLI.md +248 -0
- package/docs/ENDPOINTS.md +344 -0
- package/docs/GENERATORS.md +170 -0
- package/docs/GETTING_STARTED.md +170 -0
- package/docs/LIBRARY.md +157 -0
- package/docs/MANIFEST.md +162 -0
- package/docs/QUALITY.md +107 -0
- package/docs/README.md +44 -0
- package/docs/RECOVERY.md +124 -0
- package/docs/TILES.md +118 -0
- package/examples/minimal/README.md +26 -0
- package/examples/minimal/pixelkiln.manifest.json +21 -0
- package/package.json +100 -0
- package/schema/manifest.schema.json +255 -0
- package/skills/pixelkiln/SKILL.md +51 -0
- package/skills/pixelkiln/agents/openai.yaml +5 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|