pixelkiln 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +124 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +123 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -1
- package/dist/index.d.ts +152 -1
- package/dist/index.js +123 -18
- package/dist/index.js.map +1 -1
- package/docs/MANIFEST.md +25 -4
- package/package.json +1 -1
- package/schema/manifest.schema.json +16 -0
- package/schema/recipe.schema.json +4 -0
- package/skills/pixelkiln/references/pixellab.md +4 -1
package/dist/cli.js
CHANGED
|
@@ -676,7 +676,9 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
676
676
|
textGuidanceScale: spec.character.textGuidanceScale,
|
|
677
677
|
isometric: spec.character.isometric,
|
|
678
678
|
enhancePrompt: spec.character.enhancePrompt,
|
|
679
|
-
reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0
|
|
679
|
+
reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0,
|
|
680
|
+
concept: spec.character.concept?.sha256,
|
|
681
|
+
styleAnchor: spec.character.styleAnchor ? { of: spec.character.styleAnchor.assetId, sha256: spec.character.styleAnchor.sha256 } : void 0
|
|
680
682
|
} : void 0,
|
|
681
683
|
// A mirror's bytes come from its source's recorded outputs; the plan
|
|
682
684
|
// compares those directly, so only the choice of source is identity.
|
|
@@ -1009,6 +1011,12 @@ var StyleObjectSchema = z.object({
|
|
|
1009
1011
|
isometric: z.boolean().optional(),
|
|
1010
1012
|
/** `character` only, `v3` bases: let PixelLab expand the prompt into a fuller one before drawing. */
|
|
1011
1013
|
enhancePrompt: z.boolean().optional(),
|
|
1014
|
+
/**
|
|
1015
|
+
* `character` only, `pro` bases: the asset id of a generated 8-direction
|
|
1016
|
+
* character in this style whose look every base drawn from text or a
|
|
1017
|
+
* concept follows. An asset may override it; the anchor itself ignores it.
|
|
1018
|
+
*/
|
|
1019
|
+
styleCharacter: z.string().min(1).optional(),
|
|
1012
1020
|
/** Fixed seed for reproducibility where the endpoint supports it. */
|
|
1013
1021
|
seed: z.number().int().optional(),
|
|
1014
1022
|
/**
|
|
@@ -1181,6 +1189,15 @@ var AssetSchema = z.object({
|
|
|
1181
1189
|
* still guides the result.
|
|
1182
1190
|
*/
|
|
1183
1191
|
reference: z.union([z.string().min(1), z.record(CharacterDirectionSchema, z.string().min(1))]).optional(),
|
|
1192
|
+
/**
|
|
1193
|
+
* `character` styles, `pro` bases only: a manifest-relative concept
|
|
1194
|
+
* image (a painting, a photo, a sketch; up to 1024px) that seeds the
|
|
1195
|
+
* design instead of the prompt alone. A style image or `styleCharacter`
|
|
1196
|
+
* may still anchor the look.
|
|
1197
|
+
*/
|
|
1198
|
+
concept: z.string().min(1).optional(),
|
|
1199
|
+
/** `character` styles, `pro` bases: this base's style anchor, over the style's. */
|
|
1200
|
+
styleCharacter: z.string().min(1).optional(),
|
|
1184
1201
|
/**
|
|
1185
1202
|
* Another asset of the same style flipped left to right, made locally
|
|
1186
1203
|
* from that asset's downloaded files at no generation cost.
|
|
@@ -1262,6 +1279,20 @@ var AssetSchema = z.object({
|
|
|
1262
1279
|
path: ["reference"]
|
|
1263
1280
|
});
|
|
1264
1281
|
}
|
|
1282
|
+
if (asset.concept && (asset.state || asset.animation || asset.mirror)) {
|
|
1283
|
+
context.addIssue({
|
|
1284
|
+
code: z.ZodIssueCode.custom,
|
|
1285
|
+
message: "a concept image belongs on a base; a state, animation, or mirror takes its look from its parent",
|
|
1286
|
+
path: ["concept"]
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
if (asset.concept && asset.reference) {
|
|
1290
|
+
context.addIssue({
|
|
1291
|
+
code: z.ZodIssueCode.custom,
|
|
1292
|
+
message: "concept and reference are mutually exclusive: a concept seeds a new design, a reference is the sprite to rotate",
|
|
1293
|
+
path: ["concept"]
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1265
1296
|
if (typeof asset.reference === "object" && !Object.keys(asset.reference).length) {
|
|
1266
1297
|
context.addIssue({ code: z.ZodIssueCode.custom, message: "a reference needs at least a south image", path: ["reference"] });
|
|
1267
1298
|
}
|
|
@@ -3629,9 +3660,24 @@ import path8 from "path";
|
|
|
3629
3660
|
async function inspectRevisionReadiness(spec, lock) {
|
|
3630
3661
|
if (spec.mirror) return inspectMirror(spec, lock, /* @__PURE__ */ new Set());
|
|
3631
3662
|
if (spec.character?.parentSpec) return inspectCharacterParent(spec, lock, /* @__PURE__ */ new Set());
|
|
3663
|
+
if (spec.character?.styleAnchor) return inspectStyleAnchor(spec, lock, /* @__PURE__ */ new Set());
|
|
3632
3664
|
if (!spec.revision) return null;
|
|
3633
3665
|
return inspectRevision(spec, lock, /* @__PURE__ */ new Set());
|
|
3634
3666
|
}
|
|
3667
|
+
async function inspectStyleAnchor(spec, lock, seen) {
|
|
3668
|
+
const anchor = spec.character.styleAnchor;
|
|
3669
|
+
const label = `style anchor ${spec.styleId}/${anchor.assetId}`;
|
|
3670
|
+
const dependency = await inspectParent(anchor.spec, lock, seen);
|
|
3671
|
+
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
3672
|
+
if (!anchor.sha256 || !existsSync4(anchor.file)) return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
3673
|
+
if (await sha256File(anchor.file) !== anchor.sha256) {
|
|
3674
|
+
return { ready: false, reason: `${label} changed after the manifest was resolved` };
|
|
3675
|
+
}
|
|
3676
|
+
if (!lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)]?.objectId) {
|
|
3677
|
+
return { ready: false, reason: `${label} has no PixelLab character id recorded` };
|
|
3678
|
+
}
|
|
3679
|
+
return { ready: true, reason: `${label} is current` };
|
|
3680
|
+
}
|
|
3635
3681
|
async function inspectMirror(spec, lock, seen) {
|
|
3636
3682
|
const source = spec.mirror.sourceSpec;
|
|
3637
3683
|
const label = `mirror source ${spec.styleId}/${spec.mirror.sourceAssetId}`;
|
|
@@ -3703,6 +3749,10 @@ async function inspectParent(spec, lock, seen) {
|
|
|
3703
3749
|
const inputs = await inspectCharacterParent(spec, lock, nextSeen);
|
|
3704
3750
|
if (!inputs.ready) return inputs;
|
|
3705
3751
|
}
|
|
3752
|
+
if (spec.character?.styleAnchor) {
|
|
3753
|
+
const inputs = await inspectStyleAnchor(spec, lock, nextSeen);
|
|
3754
|
+
if (!inputs.ready) return inputs;
|
|
3755
|
+
}
|
|
3706
3756
|
if (spec.quality) {
|
|
3707
3757
|
const quality = await inspectQualityProfile(spec, lock);
|
|
3708
3758
|
return quality?.state === "approved" ? { ready: true, reason: "approved quality output is current" } : {
|
|
@@ -3742,6 +3792,14 @@ async function anyOutputModified(entry, spec) {
|
|
|
3742
3792
|
}
|
|
3743
3793
|
return false;
|
|
3744
3794
|
}
|
|
3795
|
+
function dependencies(spec) {
|
|
3796
|
+
return [
|
|
3797
|
+
spec.mirror?.sourceSpec,
|
|
3798
|
+
spec.revision?.sourceSpec,
|
|
3799
|
+
spec.character?.parentSpec,
|
|
3800
|
+
spec.character?.styleAnchor?.spec
|
|
3801
|
+
].filter((parent) => Boolean(parent));
|
|
3802
|
+
}
|
|
3745
3803
|
function resumeCommandForStatus(status) {
|
|
3746
3804
|
if (status === "pending" || status === "processing") return "poll";
|
|
3747
3805
|
if (status === "review") return "pick";
|
|
@@ -3850,16 +3908,19 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
3850
3908
|
const stateOf = new Map(items.map((i) => [i.key, i.state]));
|
|
3851
3909
|
for (const item of items) {
|
|
3852
3910
|
if (!isActionable(item)) continue;
|
|
3853
|
-
const parent
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
item.
|
|
3862
|
-
|
|
3911
|
+
for (const parent of dependencies(item.spec)) {
|
|
3912
|
+
const parentKey = lockKey(parent.styleId, parent.assetId);
|
|
3913
|
+
const parentState = stateOf.get(parentKey);
|
|
3914
|
+
if (parentState && isActionable({ state: parentState })) {
|
|
3915
|
+
item.state = "blocked";
|
|
3916
|
+
item.reason = `parent ${parentKey} is being generated in this run; this follows in the next wave`;
|
|
3917
|
+
break;
|
|
3918
|
+
}
|
|
3919
|
+
if (item.spec.mirror && (parentState === "recoverable" || parentState === "in-flight")) {
|
|
3920
|
+
item.state = "blocked";
|
|
3921
|
+
item.reason = `mirror source ${parentKey} is ${parentState}; this follows in the next wave`;
|
|
3922
|
+
break;
|
|
3923
|
+
}
|
|
3863
3924
|
}
|
|
3864
3925
|
}
|
|
3865
3926
|
const actionable = items.filter(isActionable);
|
|
@@ -5388,10 +5449,12 @@ var PixelLabClient = class {
|
|
|
5388
5449
|
image_size: imageSize,
|
|
5389
5450
|
template_id: args.template,
|
|
5390
5451
|
no_background: args.noBackground ?? true,
|
|
5391
|
-
method: south ? "rotate_character" : "create_with_style",
|
|
5452
|
+
method: south ? "rotate_character" : args.concept ? "create_from_concept" : "create_with_style",
|
|
5392
5453
|
...args.view ? { view: args.view } : {},
|
|
5393
5454
|
...args.seed != null ? { seed: args.seed } : {},
|
|
5394
|
-
...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {}
|
|
5455
|
+
...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {},
|
|
5456
|
+
...!south && args.concept ? { concept_image: encode(args.concept) } : {},
|
|
5457
|
+
...!south && args.styleCharacterId ? { style_character_id: args.styleCharacterId } : {}
|
|
5395
5458
|
};
|
|
5396
5459
|
} else {
|
|
5397
5460
|
path45 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
|
|
@@ -5702,6 +5765,19 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
5702
5765
|
}
|
|
5703
5766
|
}
|
|
5704
5767
|
}
|
|
5768
|
+
if (character.concept) {
|
|
5769
|
+
if (character.mode !== "pro") throw new Error(`${label}: a concept image is a pro input; the ${character.mode} engine draws from text or a reference`);
|
|
5770
|
+
if (character.concept.width > 1024 || character.concept.height > 1024) {
|
|
5771
|
+
throw new Error(`${label}: concept image is ${character.concept.width}x${character.concept.height}; PixelLab pro takes up to 1024px`);
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
if (character.styleAnchor) {
|
|
5775
|
+
if (character.mode !== "pro") throw new Error(`${label}: styleCharacter is a pro input; the ${character.mode} engine has no style anchor`);
|
|
5776
|
+
if (character.reference) throw new Error(`${label}: a pro base rotating its own reference takes no styleCharacter`);
|
|
5777
|
+
if (character.styleAnchor.spec.character?.directions !== 8) {
|
|
5778
|
+
throw new Error(`${label}: styleCharacter ${character.styleAnchor.assetId} has ${character.styleAnchor.spec.character?.directions} directions; PixelLab wants an 8-direction anchor`);
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5705
5781
|
if (character.reference) {
|
|
5706
5782
|
const given = Object.keys(character.reference);
|
|
5707
5783
|
if (!character.reference.south) throw new Error(`${label}: a reference needs a south-facing sprite`);
|
|
@@ -5803,7 +5879,9 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
5803
5879
|
isometric: character.isometric,
|
|
5804
5880
|
reference: character.reference ? readReference(spec, character.reference) : void 0,
|
|
5805
5881
|
styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
|
|
5806
|
-
enhancePrompt: character.enhancePrompt
|
|
5882
|
+
enhancePrompt: character.enhancePrompt,
|
|
5883
|
+
concept: character.concept ? readPose(spec, "concept image", character.concept) : void 0,
|
|
5884
|
+
styleCharacterId: character.styleAnchor ? requireStyleObjectId(spec, context) : void 0
|
|
5807
5885
|
});
|
|
5808
5886
|
return { jobId: res2.character_id, metadata: { character: { kind: "base", characterId: res2.character_id, mode: character.mode, directions: character.directions, backgroundJobId: res2.background_job_id } } };
|
|
5809
5887
|
}
|
|
@@ -6223,6 +6301,14 @@ function readReference(spec, reference) {
|
|
|
6223
6301
|
}
|
|
6224
6302
|
return images;
|
|
6225
6303
|
}
|
|
6304
|
+
function requireStyleObjectId(spec, context) {
|
|
6305
|
+
if (!context?.styleObjectId) {
|
|
6306
|
+
throw new Error(
|
|
6307
|
+
`${spec.styleId}/${spec.assetId} anchors its style on ${spec.character.styleAnchor.assetId}, which has no PixelLab character id in the lockfile yet; generate the anchor first`
|
|
6308
|
+
);
|
|
6309
|
+
}
|
|
6310
|
+
return context.styleObjectId;
|
|
6311
|
+
}
|
|
6226
6312
|
function readPose(spec, what, image) {
|
|
6227
6313
|
const bytes = readFileSync2(image.path);
|
|
6228
6314
|
if (sha256(bytes) !== image.sha256) {
|
|
@@ -7544,6 +7630,18 @@ async function resolveSpecs(loaded, filter) {
|
|
|
7544
7630
|
finalized.add(assetId);
|
|
7545
7631
|
return resolved;
|
|
7546
7632
|
}
|
|
7633
|
+
const anchorId = asset.styleCharacter ?? style.styleCharacter;
|
|
7634
|
+
if (resolved.character?.kind === "base" && anchorId && anchorId !== assetId) {
|
|
7635
|
+
const anchorSpec = await finalize(anchorId);
|
|
7636
|
+
if (!anchorSpec.character || anchorSpec.character.kind === "animation") {
|
|
7637
|
+
throw new Error(`assets.${assetId}: styleCharacter ${anchorId} is not a character base or state`);
|
|
7638
|
+
}
|
|
7639
|
+
const file = memberPath(anchorSpec.outFile, "south", 0, anchorSpec.character.directions, MediaType.PNG);
|
|
7640
|
+
resolved.character = {
|
|
7641
|
+
...resolved.character,
|
|
7642
|
+
styleAnchor: { assetId: anchorId, spec: anchorSpec, file, sha256: existsSync8(file) ? await sha256File(file) : null }
|
|
7643
|
+
};
|
|
7644
|
+
}
|
|
7547
7645
|
const characterParent = asset.state?.of ?? asset.animation?.of;
|
|
7548
7646
|
if (resolved.character && characterParent) {
|
|
7549
7647
|
const parentSpec = await finalize(characterParent);
|
|
@@ -7693,6 +7791,10 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
7693
7791
|
...style.isometric !== void 0 ? { isometric: style.isometric } : {},
|
|
7694
7792
|
...style.enhancePrompt !== void 0 ? { enhancePrompt: style.enhancePrompt } : {}
|
|
7695
7793
|
};
|
|
7794
|
+
if (asset.concept) {
|
|
7795
|
+
const image = await files.load(asset.concept, "Concept image");
|
|
7796
|
+
shape.concept = { path: path13.resolve(files.root, asset.concept), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
7797
|
+
}
|
|
7696
7798
|
if (asset.reference) {
|
|
7697
7799
|
const byDirection = typeof asset.reference === "string" ? { south: asset.reference } : asset.reference;
|
|
7698
7800
|
shape.reference = {};
|
|
@@ -11827,10 +11929,13 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
11827
11929
|
try {
|
|
11828
11930
|
const refs = styleImages.get(spec.styleId) ?? [];
|
|
11829
11931
|
const parentEntry = spec.character?.parentSpec ? lock.entries[lockKey(spec.character.parentSpec.styleId, spec.character.parentSpec.assetId)] : void 0;
|
|
11932
|
+
const anchor = spec.character?.styleAnchor;
|
|
11933
|
+
const anchorEntry = anchor ? lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)] : void 0;
|
|
11830
11934
|
const replacedMetadata = previousEntry?.providerMetadata?.[provider.id];
|
|
11831
11935
|
const { jobId, metadata } = await provider.submit(spec, refs, {
|
|
11832
11936
|
...previousJobId ? { previousJobId } : {},
|
|
11833
11937
|
...parentEntry?.objectId ? { parentObjectId: parentEntry.objectId } : {},
|
|
11938
|
+
...anchorEntry?.objectId ? { styleObjectId: anchorEntry.objectId } : {},
|
|
11834
11939
|
...replacedMetadata ? { replacedMetadata } : {},
|
|
11835
11940
|
...previousMetadata ? { previousMetadata } : {},
|
|
11836
11941
|
checkpoint: async (checkpoint) => {
|
|
@@ -12910,7 +13015,7 @@ var pin_default = {
|
|
|
12910
13015
|
godot: "4.7.2",
|
|
12911
13016
|
extensionsApi: 9,
|
|
12912
13017
|
protocol: 4,
|
|
12913
|
-
release: "editor-pixelorama-v1.2.2-pk.
|
|
13018
|
+
release: "editor-pixelorama-v1.2.2-pk.26",
|
|
12914
13019
|
files: {
|
|
12915
13020
|
"index.apple-touch-icon.png": {
|
|
12916
13021
|
sha256: "ee5434873e0fed91e9a888c3ea5a8b79beac25999308be7a6eb41bf4ce640d27",
|
|
@@ -12925,7 +13030,7 @@ var pin_default = {
|
|
|
12925
13030
|
bytes: 7298
|
|
12926
13031
|
},
|
|
12927
13032
|
"index.html": {
|
|
12928
|
-
sha256: "
|
|
13033
|
+
sha256: "326101ab02bb4a29a7964214c9307c7dff26e9d66b8ebaaf6e7a3987e84a05b2",
|
|
12929
13034
|
bytes: 5442
|
|
12930
13035
|
},
|
|
12931
13036
|
"index.icon.png": {
|
|
@@ -12937,8 +13042,8 @@ var pin_default = {
|
|
|
12937
13042
|
bytes: 279815
|
|
12938
13043
|
},
|
|
12939
13044
|
"index.pck": {
|
|
12940
|
-
sha256: "
|
|
12941
|
-
bytes:
|
|
13045
|
+
sha256: "e26292650bb996965acd1ba70e74d2b4020b063d14f1276701312c1d34cb0a83",
|
|
13046
|
+
bytes: 6431932
|
|
12942
13047
|
},
|
|
12943
13048
|
"index.png": {
|
|
12944
13049
|
sha256: "6e3ca3eb4a99e4c07bdd89ebc873b440ec0ffd66ce1955160da9e7ed80d538d7",
|
|
@@ -13557,7 +13662,7 @@ async function runLifecycle(args, wave, spent) {
|
|
|
13557
13662
|
log(` Packaging stays blocked until each refined PNG has a current human approval.`);
|
|
13558
13663
|
}
|
|
13559
13664
|
}
|
|
13560
|
-
return args.command === "gen" && submitted > 0 && total.downloaded > 0 && !total.failed && specs.some((spec) => spec.character?.parentSpec || spec.revision || spec.mirror);
|
|
13665
|
+
return args.command === "gen" && submitted > 0 && total.downloaded > 0 && !total.failed && specs.some((spec) => spec.character?.parentSpec || spec.character?.styleAnchor || spec.revision || spec.mirror);
|
|
13561
13666
|
}
|
|
13562
13667
|
return false;
|
|
13563
13668
|
}
|