pixelkiln 0.84.0 → 0.86.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/PROVIDERS.md +3 -3
- package/dist/cli.js +528 -353
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +615 -440
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -4
- package/dist/index.d.ts +107 -4
- package/dist/index.js +510 -335
- package/dist/index.js.map +1 -1
- package/docs/MANIFEST.md +2 -1
- package/docs/REVISIONS.md +43 -11
- package/docs/SCENARIO.md +203 -7
- package/package.json +1 -1
- package/schema/manifest.schema.json +4 -0
- package/skills/pixelkiln/references/revisions.md +4 -2
- package/skills/pixelkiln/references/scenario.md +8 -4
package/dist/index.cjs
CHANGED
|
@@ -638,8 +638,16 @@ var RevisionDitheringSchema = import_zod.z.enum(["none", "2x2", "4x4", "8x8"]);
|
|
|
638
638
|
var RevisionSchema = import_zod.z.object({
|
|
639
639
|
/** What kind of controlled change the provider workflow performs. */
|
|
640
640
|
mode: RevisionModeSchema,
|
|
641
|
-
/** Parent asset id in
|
|
641
|
+
/** Parent asset id. It lives in this asset's own style unless `fromStyle` names another. */
|
|
642
642
|
from: import_zod.z.string().min(1),
|
|
643
|
+
/**
|
|
644
|
+
* The style the parent lives in, when it is not this asset's own. The
|
|
645
|
+
* parent is generated by that style's provider and model, and this
|
|
646
|
+
* revision starts from its output file, so a chain (generate with one
|
|
647
|
+
* model, clean up with another) is declared instead of restated by hand.
|
|
648
|
+
* Styles may depend on one another but not in a cycle.
|
|
649
|
+
*/
|
|
650
|
+
fromStyle: import_zod.z.string().min(1).optional(),
|
|
643
651
|
/** Manifest-relative black/white PNG. Required only for masked inpainting. */
|
|
644
652
|
mask: import_zod.z.string().min(1).optional(),
|
|
645
653
|
/** Provider-neutral edit strength. The active adapter must bind it explicitly. */
|
|
@@ -1500,6 +1508,7 @@ var LockEntrySchema = import_zod.z.object({
|
|
|
1500
1508
|
revision: import_zod.z.object({
|
|
1501
1509
|
mode: RevisionModeSchema,
|
|
1502
1510
|
sourceAssetId: import_zod.z.string().min(1),
|
|
1511
|
+
sourceStyleId: import_zod.z.string().min(1).optional(),
|
|
1503
1512
|
sourceSha256: import_zod.z.string().regex(/^[0-9a-f]{64}$/),
|
|
1504
1513
|
maskSha256: import_zod.z.string().regex(/^[0-9a-f]{64}$/).optional(),
|
|
1505
1514
|
strength: import_zod.z.number().min(0).max(1).optional(),
|
|
@@ -2141,9 +2150,9 @@ var PixelLabClient = class {
|
|
|
2141
2150
|
apiKey;
|
|
2142
2151
|
http;
|
|
2143
2152
|
/** Retries transport failures, 429, and 5xx; see http.ts for the policy. */
|
|
2144
|
-
async request(
|
|
2153
|
+
async request(path38, init) {
|
|
2145
2154
|
const auth = this.apiKey.startsWith("Bearer ") ? this.apiKey : `Bearer ${this.apiKey}`;
|
|
2146
|
-
const res = await this.http(`${BASE}${
|
|
2155
|
+
const res = await this.http(`${BASE}${path38}`, {
|
|
2147
2156
|
...init,
|
|
2148
2157
|
headers: {
|
|
2149
2158
|
Authorization: auth,
|
|
@@ -2153,13 +2162,13 @@ var PixelLabClient = class {
|
|
|
2153
2162
|
});
|
|
2154
2163
|
const text = await res.text();
|
|
2155
2164
|
if (!res.ok) {
|
|
2156
|
-
throw new PixelLabError(`${init?.method ?? "GET"} ${
|
|
2165
|
+
throw new PixelLabError(`${init?.method ?? "GET"} ${path38} \u2192 ${res.status}`, res.status, text);
|
|
2157
2166
|
}
|
|
2158
2167
|
if (!text) return {};
|
|
2159
2168
|
try {
|
|
2160
2169
|
return JSON.parse(text);
|
|
2161
2170
|
} catch {
|
|
2162
|
-
throw new Error(`${init?.method ?? "GET"} ${
|
|
2171
|
+
throw new Error(`${init?.method ?? "GET"} ${path38} returned invalid JSON`);
|
|
2163
2172
|
}
|
|
2164
2173
|
}
|
|
2165
2174
|
async balance() {
|
|
@@ -2564,10 +2573,10 @@ var PixelLabClient = class {
|
|
|
2564
2573
|
const palette = args.paletteSwatchBase64 ? { color_image: { type: "base64", base64: args.paletteSwatchBase64, format: "png" }, force_colors: true } : {};
|
|
2565
2574
|
const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
|
|
2566
2575
|
const south = args.reference?.south;
|
|
2567
|
-
let
|
|
2576
|
+
let path38;
|
|
2568
2577
|
let body;
|
|
2569
2578
|
if (args.mode === "v3") {
|
|
2570
|
-
|
|
2579
|
+
path38 = "/create-character-v3";
|
|
2571
2580
|
body = {
|
|
2572
2581
|
description: args.description,
|
|
2573
2582
|
image_size: imageSize,
|
|
@@ -2581,7 +2590,7 @@ var PixelLabClient = class {
|
|
|
2581
2590
|
...args.enhancePrompt ? { enhance_prompt: true } : {}
|
|
2582
2591
|
};
|
|
2583
2592
|
} else if (args.mode === "pro-flash") {
|
|
2584
|
-
|
|
2593
|
+
path38 = "/create-character-pro-flash";
|
|
2585
2594
|
const traits = args.styleTraits;
|
|
2586
2595
|
body = {
|
|
2587
2596
|
description: args.description,
|
|
@@ -2605,7 +2614,7 @@ var PixelLabClient = class {
|
|
|
2605
2614
|
} : {}
|
|
2606
2615
|
};
|
|
2607
2616
|
} else if (args.mode === "pro") {
|
|
2608
|
-
|
|
2617
|
+
path38 = "/create-character-pro";
|
|
2609
2618
|
body = {
|
|
2610
2619
|
description: args.description,
|
|
2611
2620
|
image_size: imageSize,
|
|
@@ -2619,7 +2628,7 @@ var PixelLabClient = class {
|
|
|
2619
2628
|
...!south && args.styleCharacterId ? { style_character_id: args.styleCharacterId } : {}
|
|
2620
2629
|
};
|
|
2621
2630
|
} else {
|
|
2622
|
-
|
|
2631
|
+
path38 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
|
|
2623
2632
|
body = {
|
|
2624
2633
|
description: args.description,
|
|
2625
2634
|
image_size: imageSize,
|
|
@@ -2636,8 +2645,8 @@ var PixelLabClient = class {
|
|
|
2636
2645
|
...palette
|
|
2637
2646
|
};
|
|
2638
2647
|
}
|
|
2639
|
-
const raw = await this.request(
|
|
2640
|
-
return validateResponse(CharacterSubmitSchema, raw,
|
|
2648
|
+
const raw = await this.request(path38, { method: "POST", body: JSON.stringify(body) });
|
|
2649
|
+
return validateResponse(CharacterSubmitSchema, raw, path38.slice(1));
|
|
2641
2650
|
}
|
|
2642
2651
|
/** A text edit of an existing character, applied to every direction and stored as a sibling. */
|
|
2643
2652
|
async createCharacterState(args) {
|
|
@@ -3850,10 +3859,10 @@ var import_node_fs = require("fs");
|
|
|
3850
3859
|
function sha256(data) {
|
|
3851
3860
|
return (0, import_node_crypto.createHash)("sha256").update(data).digest("hex");
|
|
3852
3861
|
}
|
|
3853
|
-
async function sha256File(
|
|
3862
|
+
async function sha256File(path38) {
|
|
3854
3863
|
return new Promise((resolve, reject) => {
|
|
3855
3864
|
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
3856
|
-
const stream = (0, import_node_fs.createReadStream)(
|
|
3865
|
+
const stream = (0, import_node_fs.createReadStream)(path38);
|
|
3857
3866
|
stream.on("data", (chunk2) => hash.update(chunk2));
|
|
3858
3867
|
stream.on("error", reject);
|
|
3859
3868
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
@@ -3962,6 +3971,7 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
3962
3971
|
revision: spec.revision ? {
|
|
3963
3972
|
mode: spec.revision.mode,
|
|
3964
3973
|
from: spec.revision.sourceAssetId,
|
|
3974
|
+
fromStyle: spec.revision.sourceStyleId,
|
|
3965
3975
|
sourceSha256: spec.revision.sourceSha256,
|
|
3966
3976
|
maskSha256: spec.revision.maskSha256,
|
|
3967
3977
|
strength: spec.revision.strength,
|
|
@@ -6257,9 +6267,9 @@ var RetroDiffusionClient = class {
|
|
|
6257
6267
|
if (!response.ok) throw new ProviderError("retrodiffusion", `Retro Diffusion download failed (${response.status})`, { status: response.status });
|
|
6258
6268
|
return Buffer.from(await response.arrayBuffer());
|
|
6259
6269
|
}
|
|
6260
|
-
async call(
|
|
6270
|
+
async call(path38, init = {}) {
|
|
6261
6271
|
if (!this.token) throw new Error("RD_API_KEY is not set");
|
|
6262
|
-
const response = await this.request(`${this.baseUrl}${
|
|
6272
|
+
const response = await this.request(`${this.baseUrl}${path38}`, {
|
|
6263
6273
|
...init,
|
|
6264
6274
|
headers: {
|
|
6265
6275
|
"Content-Type": "application/json",
|
|
@@ -6568,8 +6578,13 @@ function retroError(value) {
|
|
|
6568
6578
|
}
|
|
6569
6579
|
|
|
6570
6580
|
// src/providers/scenario.ts
|
|
6581
|
+
var import_node_crypto3 = require("crypto");
|
|
6582
|
+
var import_node_fs3 = require("fs");
|
|
6583
|
+
var import_node_os2 = __toESM(require("os"), 1);
|
|
6584
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
6571
6585
|
var DEFAULT_BASE_URL2 = "https://api.cloud.scenario.com/v1";
|
|
6572
6586
|
var SOURCE_PROTOCOL2 = "scenario:";
|
|
6587
|
+
var SUBMIT_RETRIES = 8;
|
|
6573
6588
|
var PROTECTED_PARAMETERS = /* @__PURE__ */ new Set([
|
|
6574
6589
|
"dryRun",
|
|
6575
6590
|
"height",
|
|
@@ -6616,7 +6631,11 @@ var ScenarioClient = class {
|
|
|
6616
6631
|
async submit(modelId, body, projectId) {
|
|
6617
6632
|
const response = await this.call(
|
|
6618
6633
|
`/generate/custom/${encodeURIComponent(modelId)}`,
|
|
6619
|
-
|
|
6634
|
+
// A 429 here means this account's concurrent-job limit refused the
|
|
6635
|
+
// request, so nothing was accepted or billed; wait it out instead of
|
|
6636
|
+
// failing the asset. The default four retries give up after about half
|
|
6637
|
+
// a minute, before a running job has finished.
|
|
6638
|
+
{ method: "POST", body: JSON.stringify(body), retries: SUBMIT_RETRIES },
|
|
6620
6639
|
{ projectId }
|
|
6621
6640
|
);
|
|
6622
6641
|
const job = response?.job;
|
|
@@ -6624,6 +6643,29 @@ var ScenarioClient = class {
|
|
|
6624
6643
|
if (!jobId) throw new Error("Scenario did not return an async job id");
|
|
6625
6644
|
return jobId;
|
|
6626
6645
|
}
|
|
6646
|
+
/**
|
|
6647
|
+
* Uploads one image (POST /assets) and returns its asset id, which a model's
|
|
6648
|
+
* reference input takes. Free, and idempotent per image: the id is kept by
|
|
6649
|
+
* content hash so a rerun does not upload the same bytes again.
|
|
6650
|
+
*/
|
|
6651
|
+
async uploadImage(image, projectId) {
|
|
6652
|
+
const bytes = Buffer.from(image.base64, "base64");
|
|
6653
|
+
const digest2 = sha256(bytes);
|
|
6654
|
+
const cached = readUploadCache()[uploadCacheKey(this.baseUrl, projectId, digest2)];
|
|
6655
|
+
if (cached) return cached;
|
|
6656
|
+
const response = await this.call(
|
|
6657
|
+
"/assets",
|
|
6658
|
+
{
|
|
6659
|
+
method: "POST",
|
|
6660
|
+
body: JSON.stringify({ image: image.base64, name: `pixelkiln-${digest2.slice(0, 12)}.${image.format === "png" ? "png" : "jpg"}` })
|
|
6661
|
+
},
|
|
6662
|
+
{ projectId }
|
|
6663
|
+
);
|
|
6664
|
+
const id = response?.asset?.id;
|
|
6665
|
+
if (typeof id !== "string" || !id) throw new Error("Scenario did not return an uploaded asset id");
|
|
6666
|
+
writeUploadCache(uploadCacheKey(this.baseUrl, projectId, digest2), id);
|
|
6667
|
+
return id;
|
|
6668
|
+
}
|
|
6627
6669
|
async job(id, projectId) {
|
|
6628
6670
|
const response = await this.call(`/jobs/${encodeURIComponent(id)}`, {}, { projectId });
|
|
6629
6671
|
const raw = response?.job;
|
|
@@ -6750,6 +6792,14 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6750
6792
|
supports(generator) {
|
|
6751
6793
|
return generator === "map";
|
|
6752
6794
|
}
|
|
6795
|
+
/**
|
|
6796
|
+
* An edit is a generation with the parent as a reference image, which the
|
|
6797
|
+
* editing models (GPT Image, Gemini, FLUX) take. Masked inpainting and the
|
|
6798
|
+
* PixelLab-specific modes have no common Scenario input.
|
|
6799
|
+
*/
|
|
6800
|
+
supportsRevision(mode2) {
|
|
6801
|
+
return mode2 === "image-to-image";
|
|
6802
|
+
}
|
|
6753
6803
|
estimate(spec) {
|
|
6754
6804
|
const options = scenarioOptions(spec);
|
|
6755
6805
|
return {
|
|
@@ -6770,11 +6820,31 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6770
6820
|
if (!Number.isInteger(count) || count < 1 || count > 4) {
|
|
6771
6821
|
throw new Error("Scenario numOutputs must be a whole number from 1 to 4");
|
|
6772
6822
|
}
|
|
6773
|
-
if (spec.width
|
|
6774
|
-
throw new Error("Scenario width and height must be
|
|
6823
|
+
if (!Number.isInteger(spec.width) || spec.width < 16 || spec.width > 4096 || !Number.isInteger(spec.height) || spec.height < 16 || spec.height > 4096) {
|
|
6824
|
+
throw new Error("Scenario width and height must be whole numbers from 16 to 4096");
|
|
6775
6825
|
}
|
|
6776
|
-
|
|
6777
|
-
|
|
6826
|
+
const revision = spec.revision;
|
|
6827
|
+
if (revision) {
|
|
6828
|
+
if (revision.sourceMembers?.length) {
|
|
6829
|
+
throw new Error(
|
|
6830
|
+
`Scenario edits one image; ${revision.sourceAssetId} is a ${revision.sourceMembers.length}-member set`
|
|
6831
|
+
);
|
|
6832
|
+
}
|
|
6833
|
+
if (revision.strength != null) {
|
|
6834
|
+
throw new Error("Scenario image-to-image takes no strength; use the model's own parameters");
|
|
6835
|
+
}
|
|
6836
|
+
}
|
|
6837
|
+
const parameter = referenceParameter(options);
|
|
6838
|
+
if ((styleImages.length || revision) && Object.hasOwn(options.parameters ?? {}, parameter)) {
|
|
6839
|
+
throw new Error(
|
|
6840
|
+
`Scenario parameters.${parameter} is the reference-image input and is filled from styleImages and revisions`
|
|
6841
|
+
);
|
|
6842
|
+
}
|
|
6843
|
+
if (styleImages.length + (revision ? 1 : 0) > 1 && options.referenceArray === false) {
|
|
6844
|
+
throw new Error(`Scenario ${parameter} takes one image; use one style image or a revision, not both`);
|
|
6845
|
+
}
|
|
6846
|
+
if (options.referenceParameter != null && (typeof options.referenceParameter !== "string" || !options.referenceParameter.trim())) {
|
|
6847
|
+
throw new Error("Scenario referenceParameter must be a non-empty string");
|
|
6778
6848
|
}
|
|
6779
6849
|
if (options.projectId != null && (typeof options.projectId !== "string" || !options.projectId.trim())) {
|
|
6780
6850
|
throw new Error("Scenario projectId must be a non-empty string");
|
|
@@ -6787,7 +6857,12 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6787
6857
|
async submit(spec, styleImages) {
|
|
6788
6858
|
this.validate(spec, styleImages);
|
|
6789
6859
|
const options = scenarioOptions(spec);
|
|
6790
|
-
const
|
|
6860
|
+
const references = [];
|
|
6861
|
+
if (spec.revision) references.push(readRevisionSource(spec));
|
|
6862
|
+
for (const image of styleImages) references.push({ base64: image.base64, format: image.format });
|
|
6863
|
+
const referenceAssetIds = [];
|
|
6864
|
+
for (const image of references) referenceAssetIds.push(await this.client.uploadImage(image, options.projectId));
|
|
6865
|
+
const body = requestBody(spec, options, referenceAssetIds);
|
|
6791
6866
|
const quote = await this.client.quote(options.modelId, body, options.projectId);
|
|
6792
6867
|
if (quote.total > options.maxComputeUnits + Number.EPSILON) {
|
|
6793
6868
|
throw new Error(
|
|
@@ -6804,7 +6879,8 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6804
6879
|
quotedComputeUnits: quote.total,
|
|
6805
6880
|
creativeUnitsCost: quote.creativeUnitsCost,
|
|
6806
6881
|
costDetails: quote.costDetails,
|
|
6807
|
-
ipDetectionCost: quote.ipDetectionCost
|
|
6882
|
+
ipDetectionCost: quote.ipDetectionCost,
|
|
6883
|
+
...referenceAssetIds.length ? { referenceAssetIds } : {}
|
|
6808
6884
|
}
|
|
6809
6885
|
};
|
|
6810
6886
|
}
|
|
@@ -6826,7 +6902,13 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6826
6902
|
}
|
|
6827
6903
|
const assetIds = scenarioAssetIds(job);
|
|
6828
6904
|
if (!assetIds.length) return { status: "failed", error: "Scenario job returned no asset IDs" };
|
|
6829
|
-
|
|
6905
|
+
let assets;
|
|
6906
|
+
try {
|
|
6907
|
+
assets = await this.assets(assetIds, reference.projectId);
|
|
6908
|
+
} catch (error) {
|
|
6909
|
+
if (error instanceof UnsupportedScenarioOutput) return { status: "failed", error: error.message };
|
|
6910
|
+
throw error;
|
|
6911
|
+
}
|
|
6830
6912
|
if (assets.length > 1) {
|
|
6831
6913
|
return {
|
|
6832
6914
|
status: "review",
|
|
@@ -6881,6 +6963,32 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6881
6963
|
return assets.sort((left, right) => left.outputIndex - right.outputIndex || (jobOrder.get(left.id) ?? Number.MAX_SAFE_INTEGER) - (jobOrder.get(right.id) ?? Number.MAX_SAFE_INTEGER) || left.id.localeCompare(right.id));
|
|
6882
6964
|
}
|
|
6883
6965
|
};
|
|
6966
|
+
function uploadCachePath() {
|
|
6967
|
+
return process.env.SCENARIO_UPLOAD_CACHE ?? import_node_path2.default.join(import_node_os2.default.tmpdir(), "pixelkiln-scenario", "uploads.json");
|
|
6968
|
+
}
|
|
6969
|
+
function uploadCacheKey(baseUrl, projectId, digest2) {
|
|
6970
|
+
return (0, import_node_crypto3.createHash)("sha256").update(`${baseUrl}
|
|
6971
|
+
${projectId ?? ""}
|
|
6972
|
+
${digest2}`).digest("hex");
|
|
6973
|
+
}
|
|
6974
|
+
function readUploadCache() {
|
|
6975
|
+
try {
|
|
6976
|
+
const value = JSON.parse((0, import_node_fs3.readFileSync)(uploadCachePath(), "utf8"));
|
|
6977
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
6978
|
+
} catch {
|
|
6979
|
+
return {};
|
|
6980
|
+
}
|
|
6981
|
+
}
|
|
6982
|
+
function writeUploadCache(key, assetId) {
|
|
6983
|
+
try {
|
|
6984
|
+
const file = uploadCachePath();
|
|
6985
|
+
(0, import_node_fs3.mkdirSync)(import_node_path2.default.dirname(file), { recursive: true });
|
|
6986
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
6987
|
+
(0, import_node_fs3.writeFileSync)(tmp, JSON.stringify({ ...readUploadCache(), [key]: assetId }));
|
|
6988
|
+
(0, import_node_fs3.renameSync)(tmp, file);
|
|
6989
|
+
} catch {
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6884
6992
|
function scenarioOptions(spec) {
|
|
6885
6993
|
return spec.providerOptions;
|
|
6886
6994
|
}
|
|
@@ -6896,9 +7004,25 @@ function scenarioBaseUrl(value) {
|
|
|
6896
7004
|
}
|
|
6897
7005
|
return url.href.replace(/\/$/, "");
|
|
6898
7006
|
}
|
|
6899
|
-
function
|
|
7007
|
+
function referenceParameter(options) {
|
|
7008
|
+
return options.referenceParameter?.trim() || "referenceImages";
|
|
7009
|
+
}
|
|
7010
|
+
function readRevisionSource(spec) {
|
|
7011
|
+
const revision = spec.revision;
|
|
7012
|
+
if (!revision.sourceFile || !revision.sourceSha256 || !revision.sourceFormat) {
|
|
7013
|
+
throw new Error(`${spec.styleId}/${spec.assetId}: revision source is not ready`);
|
|
7014
|
+
}
|
|
7015
|
+
const bytes = (0, import_node_fs3.readFileSync)(revision.sourceFile);
|
|
7016
|
+
if (sha256(bytes) !== revision.sourceSha256) {
|
|
7017
|
+
throw new Error(`${spec.styleId}/${spec.assetId}: revision source changed after the manifest was resolved`);
|
|
7018
|
+
}
|
|
7019
|
+
return { base64: bytes.toString("base64"), format: revision.sourceFormat };
|
|
7020
|
+
}
|
|
7021
|
+
function requestBody(spec, options, referenceAssetIds = []) {
|
|
7022
|
+
const parameter = referenceParameter(options);
|
|
6900
7023
|
return {
|
|
6901
7024
|
...options.parameters ?? {},
|
|
7025
|
+
...referenceAssetIds.length ? { [parameter]: options.referenceArray === false ? referenceAssetIds[0] : referenceAssetIds } : {},
|
|
6902
7026
|
prompt: spec.prompt,
|
|
6903
7027
|
width: spec.width,
|
|
6904
7028
|
height: spec.height,
|
|
@@ -6988,9 +7112,11 @@ function scenarioAssetIds(job) {
|
|
|
6988
7112
|
function assetDownloadUrl(asset) {
|
|
6989
7113
|
return asset.originalFileUrl || asset.url;
|
|
6990
7114
|
}
|
|
7115
|
+
var UnsupportedScenarioOutput = class extends Error {
|
|
7116
|
+
};
|
|
6991
7117
|
function assertPngAsset(asset) {
|
|
6992
7118
|
if (asset.originalMimeType && asset.originalMimeType !== MediaType.PNG) {
|
|
6993
|
-
throw new
|
|
7119
|
+
throw new UnsupportedScenarioOutput(
|
|
6994
7120
|
`Scenario asset ${asset.id} is ${asset.originalMimeType}; PixelKiln's Scenario MVP requires PNG output`
|
|
6995
7121
|
);
|
|
6996
7122
|
}
|
|
@@ -7055,9 +7181,9 @@ function redactScenarioError(value, secrets) {
|
|
|
7055
7181
|
}
|
|
7056
7182
|
|
|
7057
7183
|
// src/providers/comfyui.ts
|
|
7058
|
-
var
|
|
7184
|
+
var import_node_crypto4 = require("crypto");
|
|
7059
7185
|
var import_promises = require("fs/promises");
|
|
7060
|
-
var
|
|
7186
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
7061
7187
|
var DEFAULT_BASE_URL3 = "http://127.0.0.1:8188";
|
|
7062
7188
|
var BUILTIN_BINDINGS = /* @__PURE__ */ new Set([
|
|
7063
7189
|
"prompt",
|
|
@@ -7084,7 +7210,7 @@ var ComfyUIClient = class {
|
|
|
7084
7210
|
async submit(workflow) {
|
|
7085
7211
|
const value = await this.json("prompt", {
|
|
7086
7212
|
method: "POST",
|
|
7087
|
-
body: JSON.stringify({ prompt: workflow, client_id: (0,
|
|
7213
|
+
body: JSON.stringify({ prompt: workflow, client_id: (0, import_node_crypto4.randomUUID)() })
|
|
7088
7214
|
});
|
|
7089
7215
|
if (!isObject(value)) throw new Error("ComfyUI returned an invalid queue response");
|
|
7090
7216
|
const promptId = value.prompt_id;
|
|
@@ -7194,7 +7320,7 @@ var ComfyUIProvider = class _ComfyUIProvider {
|
|
|
7194
7320
|
}
|
|
7195
7321
|
async resolveOptions(value, context) {
|
|
7196
7322
|
const options = parseOptions(value);
|
|
7197
|
-
const workflowPath =
|
|
7323
|
+
const workflowPath = import_node_path3.default.resolve(context.root, options.workflowFile);
|
|
7198
7324
|
let raw;
|
|
7199
7325
|
try {
|
|
7200
7326
|
raw = await (0, import_promises.readFile)(workflowPath, "utf8");
|
|
@@ -7249,7 +7375,7 @@ var ComfyUIProvider = class _ComfyUIProvider {
|
|
|
7249
7375
|
if (typeof item !== "string") {
|
|
7250
7376
|
throw new Error(`ComfyUI ${label} must be a manifest-relative PNG or JPEG path`);
|
|
7251
7377
|
}
|
|
7252
|
-
const file =
|
|
7378
|
+
const file = import_node_path3.default.resolve(context.root, item);
|
|
7253
7379
|
let bytes;
|
|
7254
7380
|
try {
|
|
7255
7381
|
bytes = await (0, import_promises.readFile)(file);
|
|
@@ -7961,7 +8087,7 @@ function canonical(value) {
|
|
|
7961
8087
|
}
|
|
7962
8088
|
|
|
7963
8089
|
// src/providers/fake.ts
|
|
7964
|
-
var
|
|
8090
|
+
var import_node_crypto5 = require("crypto");
|
|
7965
8091
|
var FAKE_PNG = Buffer.from(
|
|
7966
8092
|
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
|
|
7967
8093
|
"base64"
|
|
@@ -8063,7 +8189,7 @@ var FakeProvider = class {
|
|
|
8063
8189
|
}
|
|
8064
8190
|
async download(url) {
|
|
8065
8191
|
if (!url.startsWith("fake://")) throw new Error(`fake provider cannot download ${url}`);
|
|
8066
|
-
const salt = (0,
|
|
8192
|
+
const salt = (0, import_node_crypto5.createHash)("sha256").update(url).digest();
|
|
8067
8193
|
return Buffer.concat([FAKE_PNG, salt.subarray(0, 4)]);
|
|
8068
8194
|
}
|
|
8069
8195
|
async balance() {
|
|
@@ -8211,8 +8337,8 @@ registerProvider({
|
|
|
8211
8337
|
|
|
8212
8338
|
// src/manifest.ts
|
|
8213
8339
|
var import_promises3 = require("fs/promises");
|
|
8214
|
-
var
|
|
8215
|
-
var
|
|
8340
|
+
var import_node_fs6 = require("fs");
|
|
8341
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
8216
8342
|
|
|
8217
8343
|
// src/loop-directions.ts
|
|
8218
8344
|
var SINGLE_OUTPUT_FIELDS = ["file", "cell", "remoteId", "source", "sourceByStyle", "outputRole"];
|
|
@@ -8300,15 +8426,15 @@ function isObject2(value) {
|
|
|
8300
8426
|
}
|
|
8301
8427
|
|
|
8302
8428
|
// src/outputs.ts
|
|
8303
|
-
var
|
|
8304
|
-
var
|
|
8429
|
+
var import_node_fs5 = require("fs");
|
|
8430
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
8305
8431
|
|
|
8306
8432
|
// src/lock.ts
|
|
8307
8433
|
var import_promises2 = require("fs/promises");
|
|
8308
|
-
var
|
|
8309
|
-
var
|
|
8434
|
+
var import_node_fs4 = require("fs");
|
|
8435
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
8310
8436
|
async function loadLock(lockPath) {
|
|
8311
|
-
if (!(0,
|
|
8437
|
+
if (!(0, import_node_fs4.existsSync)(lockPath)) return { version: 2, entries: {} };
|
|
8312
8438
|
try {
|
|
8313
8439
|
return parseLock(JSON.parse(await (0, import_promises2.readFile)(lockPath, "utf8")));
|
|
8314
8440
|
} catch (err) {
|
|
@@ -8323,7 +8449,7 @@ var saveQueues = /* @__PURE__ */ new Map();
|
|
|
8323
8449
|
var dirtyPatches = /* @__PURE__ */ new WeakMap();
|
|
8324
8450
|
var dirtyDeletes = /* @__PURE__ */ new WeakMap();
|
|
8325
8451
|
function saveLock(lockPath, lock) {
|
|
8326
|
-
const queueKey =
|
|
8452
|
+
const queueKey = import_node_path4.default.resolve(lockPath);
|
|
8327
8453
|
const previous = saveQueues.get(queueKey) ?? Promise.resolve();
|
|
8328
8454
|
const next = previous.catch(() => {
|
|
8329
8455
|
}).then(() => writeLockNow(lockPath, lock));
|
|
@@ -8335,7 +8461,7 @@ function saveLock(lockPath, lock) {
|
|
|
8335
8461
|
return next;
|
|
8336
8462
|
}
|
|
8337
8463
|
async function writeLockNow(lockPath, lock) {
|
|
8338
|
-
await (0, import_promises2.mkdir)(
|
|
8464
|
+
await (0, import_promises2.mkdir)(import_node_path4.default.dirname(import_node_path4.default.resolve(lockPath)), { recursive: true });
|
|
8339
8465
|
const release = await acquireFileLock(lockPath);
|
|
8340
8466
|
try {
|
|
8341
8467
|
await writeLockWhileHeld(lockPath, lock);
|
|
@@ -8345,7 +8471,7 @@ async function writeLockNow(lockPath, lock) {
|
|
|
8345
8471
|
}
|
|
8346
8472
|
async function writeLockWhileHeld(file, lock) {
|
|
8347
8473
|
let disk = { version: 2, entries: {} };
|
|
8348
|
-
if ((0,
|
|
8474
|
+
if ((0, import_node_fs4.existsSync)(file)) {
|
|
8349
8475
|
try {
|
|
8350
8476
|
disk = parseLock(JSON.parse(await (0, import_promises2.readFile)(file, "utf8")));
|
|
8351
8477
|
} catch (err) {
|
|
@@ -8480,22 +8606,22 @@ function totalSpend(lock, unit = "generations") {
|
|
|
8480
8606
|
|
|
8481
8607
|
// src/outputs.ts
|
|
8482
8608
|
function portableOutputPath(file, manifestDir) {
|
|
8483
|
-
const root =
|
|
8484
|
-
const absolute =
|
|
8485
|
-
const relative =
|
|
8486
|
-
const value =
|
|
8487
|
-
return value.split(
|
|
8609
|
+
const root = import_node_path5.default.resolve(manifestDir);
|
|
8610
|
+
const absolute = import_node_path5.default.isAbsolute(file) ? import_node_path5.default.normalize(file) : import_node_path5.default.resolve(root, file);
|
|
8611
|
+
const relative = import_node_path5.default.relative(root, absolute);
|
|
8612
|
+
const value = import_node_path5.default.isAbsolute(relative) ? absolute : relative;
|
|
8613
|
+
return value.split(import_node_path5.default.sep).join("/");
|
|
8488
8614
|
}
|
|
8489
8615
|
function resolveOutputPath(recordedPath, manifestDir) {
|
|
8490
|
-
if (
|
|
8491
|
-
if (
|
|
8492
|
-
return
|
|
8616
|
+
if (import_node_path5.default.isAbsolute(recordedPath)) return import_node_path5.default.normalize(recordedPath);
|
|
8617
|
+
if (import_node_path5.default.win32.isAbsolute(recordedPath)) return recordedPath;
|
|
8618
|
+
return import_node_path5.default.resolve(manifestDir, recordedPath.split(/[\\/]/).join(import_node_path5.default.sep));
|
|
8493
8619
|
}
|
|
8494
8620
|
function expectedOutputPath(spec, role, index, total, mediaType) {
|
|
8495
8621
|
return memberPath(spec.outFile, role, index, total, mediaType);
|
|
8496
8622
|
}
|
|
8497
8623
|
function memberPath(file, role, index, total, mediaType) {
|
|
8498
|
-
const originalExt =
|
|
8624
|
+
const originalExt = import_node_path5.default.extname(file);
|
|
8499
8625
|
const ext = mediaType ? mediaExtension(mediaType) : originalExt || ".png";
|
|
8500
8626
|
const stem = originalExt ? file.slice(0, -originalExt.length) : file;
|
|
8501
8627
|
if (total === 1) return `${stem}${ext}`;
|
|
@@ -8509,10 +8635,10 @@ function isFrameSetEntry(entry) {
|
|
|
8509
8635
|
return entry.generator === "frames" && isMemberSetEntry(entry);
|
|
8510
8636
|
}
|
|
8511
8637
|
function sourceIsStem(source, entry, manifestDir) {
|
|
8512
|
-
return isMemberSetEntry(entry) && !(0,
|
|
8638
|
+
return isMemberSetEntry(entry) && !(0, import_node_fs5.existsSync)(import_node_path5.default.resolve(manifestDir, source));
|
|
8513
8639
|
}
|
|
8514
8640
|
function sourceOutputPath(source, entry, index, manifestDir) {
|
|
8515
|
-
const absolute =
|
|
8641
|
+
const absolute = import_node_path5.default.resolve(manifestDir, source);
|
|
8516
8642
|
if (!sourceIsStem(source, entry, manifestDir)) return absolute;
|
|
8517
8643
|
const output = entry.outputs[index];
|
|
8518
8644
|
return memberPath(absolute, output.role, index, entry.outputs.length, output.mediaType);
|
|
@@ -8599,7 +8725,7 @@ function resolveSpecOutputs(spec, lock, manifestDir) {
|
|
|
8599
8725
|
id: spec.assetId,
|
|
8600
8726
|
index: 0,
|
|
8601
8727
|
path: spec.outFile,
|
|
8602
|
-
absolutePath:
|
|
8728
|
+
absolutePath: import_node_path5.default.resolve(spec.outFile),
|
|
8603
8729
|
sha256: ""
|
|
8604
8730
|
}];
|
|
8605
8731
|
}
|
|
@@ -8615,8 +8741,8 @@ function frameSetFps(entry) {
|
|
|
8615
8741
|
|
|
8616
8742
|
// src/manifest.ts
|
|
8617
8743
|
async function loadManifest(manifestPath) {
|
|
8618
|
-
const abs =
|
|
8619
|
-
if (!(0,
|
|
8744
|
+
const abs = import_node_path6.default.resolve(manifestPath);
|
|
8745
|
+
if (!(0, import_node_fs6.existsSync)(abs)) throw new ProjectError(`No manifest at ${abs}`);
|
|
8620
8746
|
const expansion = expandLoopDirections(JSON.parse(await (0, import_promises3.readFile)(abs, "utf8")));
|
|
8621
8747
|
if (expansion.issues.length) {
|
|
8622
8748
|
throw new ProjectError(`Manifest at ${abs} is invalid:
|
|
@@ -8660,13 +8786,24 @@ ${issues}`);
|
|
|
8660
8786
|
}
|
|
8661
8787
|
}
|
|
8662
8788
|
if (asset.revision) {
|
|
8663
|
-
|
|
8789
|
+
const parent = parsed.data.assets[asset.revision.from];
|
|
8790
|
+
if (!parent) {
|
|
8664
8791
|
unknownReferences.push(
|
|
8665
8792
|
`assets.${assetId}.revision.from: unknown asset "${asset.revision.from}"`
|
|
8666
8793
|
);
|
|
8667
8794
|
} else if (asset.revision.from === assetId) {
|
|
8668
8795
|
unknownReferences.push(`assets.${assetId}.revision.from: an asset cannot revise itself`);
|
|
8669
8796
|
}
|
|
8797
|
+
const fromStyle = asset.revision.fromStyle;
|
|
8798
|
+
if (fromStyle) {
|
|
8799
|
+
if (!styleIds.has(fromStyle)) {
|
|
8800
|
+
unknownReferences.push(`assets.${assetId}.revision.fromStyle: unknown style "${fromStyle}"`);
|
|
8801
|
+
} else if (parent && parent.styles.length && !parent.styles.includes(fromStyle)) {
|
|
8802
|
+
unknownReferences.push(
|
|
8803
|
+
`assets.${assetId}.revision.fromStyle: "${asset.revision.from}" is not in style "${fromStyle}"`
|
|
8804
|
+
);
|
|
8805
|
+
}
|
|
8806
|
+
}
|
|
8670
8807
|
}
|
|
8671
8808
|
for (const shape of ["state", "animation"]) {
|
|
8672
8809
|
const of = asset[shape]?.of;
|
|
@@ -8708,7 +8845,7 @@ ${issues}`);
|
|
|
8708
8845
|
${unknownReferences.map((i) => ` ${i}`).join("\n")}`);
|
|
8709
8846
|
}
|
|
8710
8847
|
const loopFamilies = Object.keys(expansion.families).length ? { loopFamilies: expansion.families } : {};
|
|
8711
|
-
return { manifest: parsed.data, root:
|
|
8848
|
+
return { manifest: parsed.data, root: import_node_path6.default.dirname(abs), path: abs, ...loopFamilies };
|
|
8712
8849
|
}
|
|
8713
8850
|
function parentAssetId(asset) {
|
|
8714
8851
|
return asset?.revision?.from ?? asset?.state?.of ?? asset?.animation?.of ?? asset?.portrait?.of ?? asset?.outfit?.of;
|
|
@@ -8782,7 +8919,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8782
8919
|
return provider;
|
|
8783
8920
|
};
|
|
8784
8921
|
const specs = [];
|
|
8785
|
-
const
|
|
8922
|
+
const requestedStyleIds = Object.keys(manifest.styles).filter(
|
|
8786
8923
|
(id) => !filter?.styles?.length || filter.styles.includes(id)
|
|
8787
8924
|
);
|
|
8788
8925
|
for (const unknownStyle of filter?.styles ?? []) {
|
|
@@ -8807,12 +8944,45 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8807
8944
|
current = parentAssetId(manifest.assets[current]);
|
|
8808
8945
|
}
|
|
8809
8946
|
}
|
|
8947
|
+
const styleDependencies = /* @__PURE__ */ new Map();
|
|
8948
|
+
for (const styleId of Object.keys(manifest.styles)) {
|
|
8949
|
+
const needed = /* @__PURE__ */ new Set();
|
|
8950
|
+
for (const assetId of resolutionAssetIds) {
|
|
8951
|
+
const asset = manifest.assets[assetId];
|
|
8952
|
+
if (asset?.styles.length && !asset.styles.includes(styleId)) continue;
|
|
8953
|
+
const fromStyle = asset?.revision?.fromStyle;
|
|
8954
|
+
if (fromStyle && fromStyle !== styleId) needed.add(fromStyle);
|
|
8955
|
+
}
|
|
8956
|
+
styleDependencies.set(styleId, needed);
|
|
8957
|
+
}
|
|
8958
|
+
const styleIds = [];
|
|
8959
|
+
const emitStyles = new Set(requestedStyleIds);
|
|
8960
|
+
{
|
|
8961
|
+
const placed = /* @__PURE__ */ new Set();
|
|
8962
|
+
const visiting = [];
|
|
8963
|
+
const place = (styleId) => {
|
|
8964
|
+
if (placed.has(styleId)) return;
|
|
8965
|
+
const cycleStart = visiting.indexOf(styleId);
|
|
8966
|
+
if (cycleStart >= 0) {
|
|
8967
|
+
throw new Error(
|
|
8968
|
+
`Styles revise each other in a cycle: ${[...visiting.slice(cycleStart), styleId].join(" -> ")}. revision.fromStyle must form a chain, not a loop.`
|
|
8969
|
+
);
|
|
8970
|
+
}
|
|
8971
|
+
visiting.push(styleId);
|
|
8972
|
+
for (const dependency of styleDependencies.get(styleId) ?? []) place(dependency);
|
|
8973
|
+
visiting.pop();
|
|
8974
|
+
placed.add(styleId);
|
|
8975
|
+
styleIds.push(styleId);
|
|
8976
|
+
};
|
|
8977
|
+
for (const styleId of requestedStyleIds) place(styleId);
|
|
8978
|
+
}
|
|
8979
|
+
const styleFinalizers = /* @__PURE__ */ new Map();
|
|
8810
8980
|
const styleImageCache = /* @__PURE__ */ new Map();
|
|
8811
8981
|
async function loadStyleImage(rel, what = "Style image") {
|
|
8812
|
-
const abs =
|
|
8982
|
+
const abs = import_node_path6.default.resolve(root, rel);
|
|
8813
8983
|
let hit = styleImageCache.get(abs);
|
|
8814
8984
|
if (!hit) {
|
|
8815
|
-
if (!(0,
|
|
8985
|
+
if (!(0, import_node_fs6.existsSync)(abs)) throw new Error(`${what} not found: ${abs}`);
|
|
8816
8986
|
const buf = await (0, import_promises3.readFile)(abs);
|
|
8817
8987
|
const metadata = imageMetadata(buf);
|
|
8818
8988
|
if (!metadata) throw new Error(`${what} is not a readable PNG or JPEG: ${abs}`);
|
|
@@ -8839,7 +9009,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8839
9009
|
styleImageDimensions.push(loadedImage);
|
|
8840
9010
|
}
|
|
8841
9011
|
const resolvedImages = style.styleImages.map((image) => {
|
|
8842
|
-
const hit = styleImageCache.get(
|
|
9012
|
+
const hit = styleImageCache.get(import_node_path6.default.resolve(root, image.path));
|
|
8843
9013
|
return { base64: hit.base64, width: hit.width, height: hit.height, format: hit.format };
|
|
8844
9014
|
});
|
|
8845
9015
|
const styleSpecs = /* @__PURE__ */ new Map();
|
|
@@ -8940,9 +9110,9 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8940
9110
|
} else {
|
|
8941
9111
|
prompt = [style.promptPrefix, subject, style.promptSuffix].map((p) => p.trim()).filter(Boolean).join(", ");
|
|
8942
9112
|
}
|
|
8943
|
-
const relFile = asset.file ??
|
|
8944
|
-
const outFile =
|
|
8945
|
-
const qualityOutFile = style.quality ?
|
|
9113
|
+
const relFile = asset.file ?? import_node_path6.default.join(asset.category ?? "", `${assetId}.png`);
|
|
9114
|
+
const outFile = import_node_path6.default.resolve(root, style.outDir, relFile);
|
|
9115
|
+
const qualityOutFile = style.quality ? import_node_path6.default.resolve(root, style.quality.outDir, pngPath(relFile)) : void 0;
|
|
8946
9116
|
const tileSize = generator === "tiles" ? size : style.tileSize ?? 32;
|
|
8947
9117
|
const tileVariations = tileFeatureOutputCount(generator === "tiles" ? style.tileFeature : void 0) ?? tileVariationCount(countNumberedDescriptions(prompt));
|
|
8948
9118
|
const terrainTiles = generator === "terrain" ? terrainTileCount(style.terrainTransitionSize) : 0;
|
|
@@ -9037,7 +9207,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9037
9207
|
minGridConfidence: style.quality.minGridConfidence,
|
|
9038
9208
|
...style.quality.minTransparency == null ? {} : { minTransparency: style.quality.minTransparency },
|
|
9039
9209
|
...style.quality.fixerRevision ? { fixerRevision: style.quality.fixerRevision } : {},
|
|
9040
|
-
...style.quality.fixerPython ? { fixerPython:
|
|
9210
|
+
...style.quality.fixerPython ? { fixerPython: import_node_path6.default.resolve(root, style.quality.fixerPython) } : {},
|
|
9041
9211
|
...style.quality.fps == null ? {} : { fps: style.quality.fps }
|
|
9042
9212
|
}
|
|
9043
9213
|
} : {},
|
|
@@ -9120,7 +9290,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9120
9290
|
const file = memberPath(anchorSpec.outFile, "south", 0, anchorSpec.character.directions, MediaType.PNG);
|
|
9121
9291
|
resolved.character = {
|
|
9122
9292
|
...resolved.character,
|
|
9123
|
-
styleAnchor: { assetId: anchorId, spec: anchorSpec, file, sha256: (0,
|
|
9293
|
+
styleAnchor: { assetId: anchorId, spec: anchorSpec, file, sha256: (0, import_node_fs6.existsSync)(file) ? await sha256File(file) : null }
|
|
9124
9294
|
};
|
|
9125
9295
|
}
|
|
9126
9296
|
const characterParent = asset.state?.of ?? asset.animation?.of ?? asset.portrait?.of;
|
|
@@ -9133,7 +9303,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9133
9303
|
parentAssetId: characterParent,
|
|
9134
9304
|
parentSpec,
|
|
9135
9305
|
parentFile,
|
|
9136
|
-
parentSha256: (0,
|
|
9306
|
+
parentSha256: (0, import_node_fs6.existsSync)(parentFile) ? await sha256File(parentFile) : null,
|
|
9137
9307
|
// A state and its animations keep the engine and rotations of the base.
|
|
9138
9308
|
mode: resolved.character.kind === "base" ? resolved.character.mode : parentSpec.character?.mode ?? resolved.character.mode,
|
|
9139
9309
|
directions: resolved.character.kind === "base" ? resolved.character.directions : parentSpec.character?.directions ?? resolved.character.directions
|
|
@@ -9151,7 +9321,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9151
9321
|
parentAssetId: characterParent,
|
|
9152
9322
|
parentSpec,
|
|
9153
9323
|
parentFile,
|
|
9154
|
-
parentSha256: (0,
|
|
9324
|
+
parentSha256: (0, import_node_fs6.existsSync)(parentFile) ? await sha256File(parentFile) : null,
|
|
9155
9325
|
// A state and its animations keep the base's own rotation count.
|
|
9156
9326
|
directions: resolved.objectPro.kind === "base" ? resolved.objectPro.directions : parentSpec.objectPro.directions
|
|
9157
9327
|
};
|
|
@@ -9173,7 +9343,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9173
9343
|
animation: sourceSpec.character.animation,
|
|
9174
9344
|
outfit: {
|
|
9175
9345
|
reference: {
|
|
9176
|
-
path:
|
|
9346
|
+
path: import_node_path6.default.resolve(root, asset.outfit.reference),
|
|
9177
9347
|
sha256: referenceImage.hash,
|
|
9178
9348
|
width: referenceImage.width,
|
|
9179
9349
|
height: referenceImage.height,
|
|
@@ -9194,28 +9364,30 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9194
9364
|
`Provider "${activeProvider.id}" does not support ${asset.revision.mode} revisions`
|
|
9195
9365
|
);
|
|
9196
9366
|
}
|
|
9197
|
-
const
|
|
9198
|
-
const
|
|
9367
|
+
const sourceStyleId = asset.revision.fromStyle && asset.revision.fromStyle !== styleId ? asset.revision.fromStyle : void 0;
|
|
9368
|
+
const sourceSpec = sourceStyleId ? await styleFinalizers.get(sourceStyleId)(asset.revision.from) : await finalize(asset.revision.from);
|
|
9369
|
+
const sourceStem = sourceSpec.quality?.outFile ?? (sourceSpec.source ? import_node_path6.default.resolve(root, sourceSpec.source) : sourceSpec.outFile);
|
|
9199
9370
|
const sourceMembers = await revisionSourceMembers(sourceStem, `revision source ${asset.revision.from}`);
|
|
9200
9371
|
const sourceFile = sourceMembers?.[0]?.file ?? sourceStem;
|
|
9201
9372
|
const sourceImage = sourceMembers ? { ...sourceMembers[0], hash: sha256(JSON.stringify(sourceMembers.map((member) => member.sha256))) } : await optionalRevisionImage(sourceFile, "revision source");
|
|
9202
9373
|
const sourceFps = sourceSpec.character?.animation?.fps ?? sourceSpec.objectPro?.animation?.fps ?? sourceSpec.revision?.fps ?? sourceSpec.revision?.sourceFps;
|
|
9203
|
-
const maskFile = asset.revision.mask ?
|
|
9374
|
+
const maskFile = asset.revision.mask ? import_node_path6.default.resolve(root, asset.revision.mask) : void 0;
|
|
9204
9375
|
const maskImage = maskFile ? await optionalRevisionImage(maskFile, "revision mask", "png") : null;
|
|
9205
9376
|
if (sourceImage && maskImage && (sourceImage.width !== maskImage.width || sourceImage.height !== maskImage.height)) {
|
|
9206
9377
|
throw new Error(
|
|
9207
9378
|
`Revision mask for ${styleId}/${assetId} is ${maskImage.width}x${maskImage.height}; source ${asset.revision.from} is ${sourceImage.width}x${sourceImage.height}`
|
|
9208
9379
|
);
|
|
9209
9380
|
}
|
|
9210
|
-
const paletteImageFile = asset.revision.paletteImage ?
|
|
9381
|
+
const paletteImageFile = asset.revision.paletteImage ? import_node_path6.default.resolve(root, asset.revision.paletteImage) : void 0;
|
|
9211
9382
|
const paletteImage = paletteImageFile ? await optionalRevisionImage(paletteImageFile, "revision palette image", "png") : null;
|
|
9212
|
-
const lastFrameFile = asset.revision.lastFrame ?
|
|
9383
|
+
const lastFrameFile = asset.revision.lastFrame ? import_node_path6.default.resolve(root, asset.revision.lastFrame) : void 0;
|
|
9213
9384
|
const lastFrame = lastFrameFile ? await optionalRevisionImage(lastFrameFile, "revision last frame") : null;
|
|
9214
|
-
const keypointsFile = asset.revision.keypointsFile ?
|
|
9385
|
+
const keypointsFile = asset.revision.keypointsFile ? import_node_path6.default.resolve(root, asset.revision.keypointsFile) : void 0;
|
|
9215
9386
|
const keypoints = keypointsFile ? await optionalSkeletonSet(keypointsFile) : null;
|
|
9216
9387
|
resolved.revision = {
|
|
9217
9388
|
mode: asset.revision.mode,
|
|
9218
9389
|
sourceAssetId: asset.revision.from,
|
|
9390
|
+
...sourceStyleId ? { sourceStyleId } : {},
|
|
9219
9391
|
sourceFile,
|
|
9220
9392
|
sourceSha256: sourceImage?.hash ?? null,
|
|
9221
9393
|
sourceWidth: sourceImage?.width ?? null,
|
|
@@ -9337,6 +9509,8 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9337
9509
|
finalized.add(assetId);
|
|
9338
9510
|
return resolved;
|
|
9339
9511
|
};
|
|
9512
|
+
styleFinalizers.set(styleId, finalize);
|
|
9513
|
+
if (!emitStyles.has(styleId)) continue;
|
|
9340
9514
|
for (const assetId of Object.keys(manifest.assets)) {
|
|
9341
9515
|
if (!requestedAssetIds.has(assetId) || !styleSpecs.has(assetId)) continue;
|
|
9342
9516
|
specs.push(await finalize(assetId));
|
|
@@ -9378,7 +9552,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9378
9552
|
return specs;
|
|
9379
9553
|
}
|
|
9380
9554
|
function pngPath(file) {
|
|
9381
|
-
const extension =
|
|
9555
|
+
const extension = import_node_path6.default.extname(file);
|
|
9382
9556
|
return extension ? `${file.slice(0, -extension.length)}.png` : `${file}.png`;
|
|
9383
9557
|
}
|
|
9384
9558
|
async function resolveMapScene(root, styleId, assetId, generator, scene, provider) {
|
|
@@ -9388,12 +9562,12 @@ async function resolveMapScene(root, styleId, assetId, generator, scene, provide
|
|
|
9388
9562
|
if (!provider.supportsMapScene) {
|
|
9389
9563
|
throw new Error(`assets.${assetId}.scene: provider "${provider.id}" cannot draw into a scene; PixelLab's /map-objects can`);
|
|
9390
9564
|
}
|
|
9391
|
-
const file =
|
|
9565
|
+
const file = import_node_path6.default.resolve(root, scene.image);
|
|
9392
9566
|
const image = await optionalRevisionImage(file, "scene image", "png");
|
|
9393
9567
|
const base = { file, sha256: image?.hash ?? null, width: image?.width ?? null, height: image?.height ?? null };
|
|
9394
9568
|
const placement = scene.placement;
|
|
9395
9569
|
if ("mask" in placement) {
|
|
9396
|
-
const maskFile =
|
|
9570
|
+
const maskFile = import_node_path6.default.resolve(root, placement.mask);
|
|
9397
9571
|
const mask = await optionalRevisionImage(maskFile, "scene mask", "png");
|
|
9398
9572
|
if (image && mask && (image.width !== mask.width || image.height !== mask.height)) {
|
|
9399
9573
|
throw new Error(
|
|
@@ -9408,7 +9582,7 @@ async function resolveMapScene(root, styleId, assetId, generator, scene, provide
|
|
|
9408
9582
|
return "oval" in placement ? { ...base, placement: { type: "oval", fraction: placement.oval } } : { ...base, placement: { type: "rectangle", fraction: placement.rectangle } };
|
|
9409
9583
|
}
|
|
9410
9584
|
async function optionalRevisionImage(file, label, requiredFormat) {
|
|
9411
|
-
if (!(0,
|
|
9585
|
+
if (!(0, import_node_fs6.existsSync)(file)) return null;
|
|
9412
9586
|
const bytes = await (0, import_promises3.readFile)(file);
|
|
9413
9587
|
const metadata = imageMetadata(bytes);
|
|
9414
9588
|
if (!metadata) throw new Error(`${label} is not a readable PNG or JPEG: ${file}`);
|
|
@@ -9418,7 +9592,7 @@ async function optionalRevisionImage(file, label, requiredFormat) {
|
|
|
9418
9592
|
return { hash: sha256(bytes), ...metadata };
|
|
9419
9593
|
}
|
|
9420
9594
|
async function optionalSkeletonSet(file) {
|
|
9421
|
-
if (!(0,
|
|
9595
|
+
if (!(0, import_node_fs6.existsSync)(file)) return null;
|
|
9422
9596
|
const bytes = await (0, import_promises3.readFile)(file);
|
|
9423
9597
|
let json;
|
|
9424
9598
|
try {
|
|
@@ -9429,17 +9603,17 @@ async function optionalSkeletonSet(file) {
|
|
|
9429
9603
|
return { hash: sha256(bytes), skeleton: parseSkeletonSet(json, file) };
|
|
9430
9604
|
}
|
|
9431
9605
|
async function revisionSourceMembers(stem, label) {
|
|
9432
|
-
if ((0,
|
|
9606
|
+
if ((0, import_node_fs6.existsSync)(stem)) return null;
|
|
9433
9607
|
const at = (role) => memberPath(stem, role, 0, 2, MediaType.PNG);
|
|
9434
9608
|
let roles = [];
|
|
9435
9609
|
for (const set of [CHARACTER_DIRECTIONS_8, CHARACTER_DIRECTIONS_4]) {
|
|
9436
|
-
if (set.every((direction) => (0,
|
|
9610
|
+
if (set.every((direction) => (0, import_node_fs6.existsSync)(at(direction)))) {
|
|
9437
9611
|
roles = [...set];
|
|
9438
9612
|
break;
|
|
9439
9613
|
}
|
|
9440
9614
|
}
|
|
9441
9615
|
if (!roles.length) {
|
|
9442
|
-
for (let index = 0; (0,
|
|
9616
|
+
for (let index = 0; (0, import_node_fs6.existsSync)(at(`frame-${String(index).padStart(2, "0")}`)); index++) {
|
|
9443
9617
|
roles.push(`frame-${String(index).padStart(2, "0")}`);
|
|
9444
9618
|
}
|
|
9445
9619
|
}
|
|
@@ -9465,7 +9639,7 @@ async function resolveStyleImages(loaded, styleId) {
|
|
|
9465
9639
|
if (!style) throw new Error(`Unknown style "${styleId}"`);
|
|
9466
9640
|
const out = [];
|
|
9467
9641
|
for (const img of style.styleImages) {
|
|
9468
|
-
const file =
|
|
9642
|
+
const file = import_node_path6.default.resolve(loaded.root, img.path);
|
|
9469
9643
|
const buf = await (0, import_promises3.readFile)(file);
|
|
9470
9644
|
const metadata = imageMetadata(buf);
|
|
9471
9645
|
if (!metadata) throw new Error(`Style image is not a readable PNG or JPEG: ${file}`);
|
|
@@ -9496,7 +9670,7 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
9496
9670
|
};
|
|
9497
9671
|
if (asset.concept) {
|
|
9498
9672
|
const image = await files.load(asset.concept, "Concept image");
|
|
9499
|
-
shape.concept = { path:
|
|
9673
|
+
shape.concept = { path: import_node_path6.default.resolve(files.root, asset.concept), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
9500
9674
|
}
|
|
9501
9675
|
if (asset.reference) {
|
|
9502
9676
|
const byDirection = typeof asset.reference === "string" ? { south: asset.reference } : asset.reference;
|
|
@@ -9504,7 +9678,7 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
9504
9678
|
for (const [direction, rel] of Object.entries(byDirection)) {
|
|
9505
9679
|
const image = await files.load(rel, `Reference sprite (${direction})`);
|
|
9506
9680
|
shape.reference[direction] = {
|
|
9507
|
-
path:
|
|
9681
|
+
path: import_node_path6.default.resolve(files.root, rel),
|
|
9508
9682
|
sha256: image.hash,
|
|
9509
9683
|
width: image.width,
|
|
9510
9684
|
height: image.height,
|
|
@@ -9525,7 +9699,7 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
9525
9699
|
const animationMode = asset.animation.mode ?? (asset.animation.template ? "template" : "v3");
|
|
9526
9700
|
const pose = async (rel, what) => {
|
|
9527
9701
|
const image = await files.load(rel, what);
|
|
9528
|
-
return { path:
|
|
9702
|
+
return { path: import_node_path6.default.resolve(files.root, rel), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
9529
9703
|
};
|
|
9530
9704
|
shape.animation = {
|
|
9531
9705
|
mode: animationMode,
|
|
@@ -9556,7 +9730,7 @@ async function resolveObjectProShape(assetId, asset, style, kind, files) {
|
|
|
9556
9730
|
const south = typeof asset.reference === "string" ? asset.reference : asset.reference.south;
|
|
9557
9731
|
if (south) {
|
|
9558
9732
|
const image = await files.load(south, "Reference sprite");
|
|
9559
|
-
shape.reference = { path:
|
|
9733
|
+
shape.reference = { path: import_node_path6.default.resolve(files.root, south), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
9560
9734
|
}
|
|
9561
9735
|
}
|
|
9562
9736
|
if (asset.state) {
|
|
@@ -9584,7 +9758,7 @@ async function resolveObjectProShape(assetId, asset, style, kind, files) {
|
|
|
9584
9758
|
}
|
|
9585
9759
|
const pose = async (rel, what) => {
|
|
9586
9760
|
const image = await files.load(rel, what);
|
|
9587
|
-
return { path:
|
|
9761
|
+
return { path: import_node_path6.default.resolve(files.root, rel), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
9588
9762
|
};
|
|
9589
9763
|
shape.animation = {
|
|
9590
9764
|
mode: asset.animation.mode ?? "v3",
|
|
@@ -9604,11 +9778,11 @@ function characterDirections(directions) {
|
|
|
9604
9778
|
}
|
|
9605
9779
|
|
|
9606
9780
|
// src/project.ts
|
|
9607
|
-
var
|
|
9781
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
9608
9782
|
|
|
9609
9783
|
// src/env.ts
|
|
9610
|
-
var
|
|
9611
|
-
var
|
|
9784
|
+
var import_node_fs7 = require("fs");
|
|
9785
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
9612
9786
|
function loadEnvFiles(dir) {
|
|
9613
9787
|
const loaded = [];
|
|
9614
9788
|
for (const [file, vars] of envFiles(dir)) {
|
|
@@ -9622,10 +9796,10 @@ function loadEnvFiles(dir) {
|
|
|
9622
9796
|
function envFiles(dir) {
|
|
9623
9797
|
const out = [];
|
|
9624
9798
|
for (const name of [".env.local", ".env"]) {
|
|
9625
|
-
const file =
|
|
9626
|
-
if (!(0,
|
|
9799
|
+
const file = import_node_path7.default.join(dir, name);
|
|
9800
|
+
if (!(0, import_node_fs7.existsSync)(file)) continue;
|
|
9627
9801
|
try {
|
|
9628
|
-
out.push([file, parseEnv((0,
|
|
9802
|
+
out.push([file, parseEnv((0, import_node_fs7.readFileSync)(file, "utf8"))]);
|
|
9629
9803
|
} catch {
|
|
9630
9804
|
}
|
|
9631
9805
|
}
|
|
@@ -9651,12 +9825,12 @@ function parseEnv(contents) {
|
|
|
9651
9825
|
}
|
|
9652
9826
|
|
|
9653
9827
|
// src/pipeline/plan.ts
|
|
9654
|
-
var
|
|
9655
|
-
var
|
|
9828
|
+
var import_node_fs10 = require("fs");
|
|
9829
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
9656
9830
|
|
|
9657
9831
|
// src/pipeline/mirror.ts
|
|
9658
9832
|
var import_promises4 = require("fs/promises");
|
|
9659
|
-
var
|
|
9833
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
9660
9834
|
|
|
9661
9835
|
// src/palette.ts
|
|
9662
9836
|
function normalizePalette(values) {
|
|
@@ -9795,7 +9969,7 @@ async function deriveMirror(spec, lock, lockPath, opts = {}) {
|
|
|
9795
9969
|
for (const [index, member] of members.entries()) {
|
|
9796
9970
|
const bytes = flipHorizontal(await (0, import_promises4.readFile)(member.file));
|
|
9797
9971
|
const target = expectedOutputPath(spec, member.role, index, members.length, MediaType.PNG);
|
|
9798
|
-
await (0, import_promises4.mkdir)(
|
|
9972
|
+
await (0, import_promises4.mkdir)(import_node_path8.default.dirname(target), { recursive: true });
|
|
9799
9973
|
const tmp = `${target}.pixelkiln.tmp`;
|
|
9800
9974
|
await (0, import_promises4.writeFile)(tmp, bytes);
|
|
9801
9975
|
await (0, import_promises4.rename)(tmp, target);
|
|
@@ -9857,29 +10031,29 @@ async function deriveMirror(spec, lock, lockPath, opts = {}) {
|
|
|
9857
10031
|
}
|
|
9858
10032
|
|
|
9859
10033
|
// src/pipeline/quality-profile.ts
|
|
9860
|
-
var
|
|
9861
|
-
var
|
|
10034
|
+
var import_node_fs8 = require("fs");
|
|
10035
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
9862
10036
|
|
|
9863
10037
|
// src/pipeline/refine.ts
|
|
9864
|
-
var
|
|
10038
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
9865
10039
|
var import_node_child_process2 = require("child_process");
|
|
9866
10040
|
var import_promises6 = require("fs/promises");
|
|
9867
|
-
var
|
|
10041
|
+
var import_node_os3 = require("os");
|
|
9868
10042
|
var import_node_util = require("util");
|
|
9869
10043
|
|
|
9870
10044
|
// src/artifacts.ts
|
|
9871
|
-
var
|
|
9872
|
-
var
|
|
10045
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
10046
|
+
var import_node_crypto6 = require("crypto");
|
|
9873
10047
|
var import_promises5 = require("fs/promises");
|
|
9874
10048
|
var activeTransactions = /* @__PURE__ */ new Set();
|
|
9875
10049
|
function message(error) {
|
|
9876
10050
|
return error instanceof Error ? error.message : String(error);
|
|
9877
10051
|
}
|
|
9878
10052
|
function digest(data) {
|
|
9879
|
-
return (0,
|
|
10053
|
+
return (0, import_node_crypto6.createHash)("sha256").update(data).digest("hex");
|
|
9880
10054
|
}
|
|
9881
10055
|
function portableRelative(from, to) {
|
|
9882
|
-
return
|
|
10056
|
+
return import_node_path9.default.relative(from, import_node_path9.default.resolve(to)).split(import_node_path9.default.sep).join("/") || ".";
|
|
9883
10057
|
}
|
|
9884
10058
|
function canonical2(value) {
|
|
9885
10059
|
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
@@ -9929,7 +10103,7 @@ function parseArtifactManifest(absolute, data) {
|
|
|
9929
10103
|
return raw;
|
|
9930
10104
|
}
|
|
9931
10105
|
async function readArtifactBundleManifest(manifestPath) {
|
|
9932
|
-
const absolute =
|
|
10106
|
+
const absolute = import_node_path9.default.resolve(manifestPath);
|
|
9933
10107
|
return parseArtifactManifest(absolute, await (0, import_promises5.readFile)(absolute, "utf8"));
|
|
9934
10108
|
}
|
|
9935
10109
|
async function readOptional(file) {
|
|
@@ -9953,8 +10127,8 @@ function processIsAlive(pid) {
|
|
|
9953
10127
|
}
|
|
9954
10128
|
}
|
|
9955
10129
|
function validTemporaryPath(candidate, destination, type) {
|
|
9956
|
-
return
|
|
9957
|
-
`.${
|
|
10130
|
+
return import_node_path9.default.dirname(candidate) === import_node_path9.default.dirname(destination) && import_node_path9.default.basename(candidate).startsWith(
|
|
10131
|
+
`.${import_node_path9.default.basename(destination)}.pixelkiln-${type}-`
|
|
9958
10132
|
);
|
|
9959
10133
|
}
|
|
9960
10134
|
function parseTransaction(journal, data) {
|
|
@@ -9974,7 +10148,7 @@ async function removeTransactionFiles(journal) {
|
|
|
9974
10148
|
await (0, import_promises5.rm)(transactionMarker(journal), { force: true });
|
|
9975
10149
|
}
|
|
9976
10150
|
async function recoverArtifactTransaction(recoveryFile, allowedDestinations) {
|
|
9977
|
-
const journal =
|
|
10151
|
+
const journal = import_node_path9.default.resolve(recoveryFile);
|
|
9978
10152
|
const bytes = await readOptional(journal);
|
|
9979
10153
|
if (!bytes) {
|
|
9980
10154
|
await (0, import_promises5.rm)(transactionMarker(journal), { force: true });
|
|
@@ -9990,7 +10164,7 @@ async function recoverArtifactTransaction(recoveryFile, allowedDestinations) {
|
|
|
9990
10164
|
);
|
|
9991
10165
|
}
|
|
9992
10166
|
for (const entry of transaction.entries) {
|
|
9993
|
-
if (typeof entry.destination !== "string" || typeof entry.stage !== "string" || typeof entry.sha256 !== "string" || entry.backup !== void 0 && typeof entry.backup !== "string" || !allowedDestinations.has(
|
|
10167
|
+
if (typeof entry.destination !== "string" || typeof entry.stage !== "string" || typeof entry.sha256 !== "string" || entry.backup !== void 0 && typeof entry.backup !== "string" || !allowedDestinations.has(import_node_path9.default.resolve(entry.destination)) || !validTemporaryPath(entry.stage, entry.destination, "stage") || entry.backup !== void 0 && !validTemporaryPath(entry.backup, entry.destination, "backup")) {
|
|
9994
10168
|
throw new Error(
|
|
9995
10169
|
`Refusing unsafe artifact recovery from ${journal}; its destinations or temporary paths do not match the current bundle.`
|
|
9996
10170
|
);
|
|
@@ -10036,7 +10210,7 @@ async function recoverArtifactTransaction(recoveryFile, allowedDestinations) {
|
|
|
10036
10210
|
await removeTransactionFiles(journal);
|
|
10037
10211
|
}
|
|
10038
10212
|
function createArtifactBundleManifest(manifestPath, outputs, provenance) {
|
|
10039
|
-
const root =
|
|
10213
|
+
const root = import_node_path9.default.dirname(import_node_path9.default.resolve(manifestPath));
|
|
10040
10214
|
const sources = provenance.sources.map((source) => ({
|
|
10041
10215
|
...source,
|
|
10042
10216
|
path: portableRelative(root, source.path)
|
|
@@ -10066,11 +10240,11 @@ function withArtifactManifest(manifestPath, outputs, provenance) {
|
|
|
10066
10240
|
];
|
|
10067
10241
|
}
|
|
10068
10242
|
async function writeManagedArtifactBundle(manifestPath, outputs, provenance, options = {}) {
|
|
10069
|
-
const absoluteManifest =
|
|
10243
|
+
const absoluteManifest = import_node_path9.default.resolve(manifestPath);
|
|
10070
10244
|
const recoveryFile = `${absoluteManifest}.transaction`;
|
|
10071
10245
|
const allowedDestinations = /* @__PURE__ */ new Set([
|
|
10072
10246
|
absoluteManifest,
|
|
10073
|
-
...outputs.map((output) =>
|
|
10247
|
+
...outputs.map((output) => import_node_path9.default.resolve(output.path))
|
|
10074
10248
|
]);
|
|
10075
10249
|
await recoverArtifactTransaction(recoveryFile, allowedDestinations);
|
|
10076
10250
|
const existingManifestBytes = await readOptional(absoluteManifest);
|
|
@@ -10088,13 +10262,13 @@ async function writeManagedArtifactBundle(manifestPath, outputs, provenance, opt
|
|
|
10088
10262
|
}
|
|
10089
10263
|
}
|
|
10090
10264
|
if (!options.force) {
|
|
10091
|
-
const root =
|
|
10265
|
+
const root = import_node_path9.default.dirname(absoluteManifest);
|
|
10092
10266
|
const recorded = new Map(
|
|
10093
|
-
previous?.outputs.map((output) => [
|
|
10267
|
+
previous?.outputs.map((output) => [import_node_path9.default.resolve(root, output.path), output.sha256]) ?? []
|
|
10094
10268
|
);
|
|
10095
10269
|
const conflicts = [];
|
|
10096
10270
|
for (const output of outputs) {
|
|
10097
|
-
const destination =
|
|
10271
|
+
const destination = import_node_path9.default.resolve(output.path);
|
|
10098
10272
|
const current = await readOptional(destination);
|
|
10099
10273
|
if (!current || digest(current) === digest(output.data)) continue;
|
|
10100
10274
|
const expected = recorded.get(destination);
|
|
@@ -10116,15 +10290,15 @@ Pass { force: true } (CLI: --force) to take ownership and replace the complete b
|
|
|
10116
10290
|
);
|
|
10117
10291
|
}
|
|
10118
10292
|
async function verifyArtifactBundle(manifestPath) {
|
|
10119
|
-
const absolute =
|
|
10293
|
+
const absolute = import_node_path9.default.resolve(manifestPath);
|
|
10120
10294
|
const raw = await readArtifactBundleManifest(absolute);
|
|
10121
|
-
const root =
|
|
10295
|
+
const root = import_node_path9.default.dirname(absolute);
|
|
10122
10296
|
const changedSources = [];
|
|
10123
10297
|
const changedOutputs = [];
|
|
10124
10298
|
for (const source of raw.sources) {
|
|
10125
10299
|
let actual = null;
|
|
10126
10300
|
try {
|
|
10127
|
-
actual = digest(await (0, import_promises5.readFile)(
|
|
10301
|
+
actual = digest(await (0, import_promises5.readFile)(import_node_path9.default.resolve(root, source.path)));
|
|
10128
10302
|
} catch (error) {
|
|
10129
10303
|
if (!isCode(error, "ENOENT")) throw error;
|
|
10130
10304
|
}
|
|
@@ -10132,7 +10306,7 @@ async function verifyArtifactBundle(manifestPath) {
|
|
|
10132
10306
|
}
|
|
10133
10307
|
for (const output of raw.outputs) {
|
|
10134
10308
|
try {
|
|
10135
|
-
if (digest(await (0, import_promises5.readFile)(
|
|
10309
|
+
if (digest(await (0, import_promises5.readFile)(import_node_path9.default.resolve(root, output.path))) !== output.sha256) {
|
|
10136
10310
|
changedOutputs.push(output.path);
|
|
10137
10311
|
}
|
|
10138
10312
|
} catch (error) {
|
|
@@ -10188,7 +10362,7 @@ async function writeArtifactBundle(files, options = {}) {
|
|
|
10188
10362
|
if (!files.length) throw new Error("An artifact bundle must contain at least one file.");
|
|
10189
10363
|
const normalized = files.map((file) => {
|
|
10190
10364
|
if (!file.path.trim()) throw new Error("Artifact paths cannot be empty.");
|
|
10191
|
-
return { destination:
|
|
10365
|
+
return { destination: import_node_path9.default.resolve(file.path), data: Buffer.from(file.data) };
|
|
10192
10366
|
});
|
|
10193
10367
|
const destinations = /* @__PURE__ */ new Set();
|
|
10194
10368
|
for (const artifact of normalized) {
|
|
@@ -10216,23 +10390,23 @@ async function writeArtifactBundle(files, options = {}) {
|
|
|
10216
10390
|
}
|
|
10217
10391
|
}
|
|
10218
10392
|
if (!changed.length) return { changed: [], unchanged };
|
|
10219
|
-
const token = (0,
|
|
10393
|
+
const token = (0, import_node_crypto6.randomUUID)();
|
|
10220
10394
|
for (const [index, artifact] of changed.entries()) {
|
|
10221
|
-
const basename =
|
|
10222
|
-
artifact.stage =
|
|
10223
|
-
|
|
10395
|
+
const basename = import_node_path9.default.basename(artifact.destination);
|
|
10396
|
+
artifact.stage = import_node_path9.default.join(
|
|
10397
|
+
import_node_path9.default.dirname(artifact.destination),
|
|
10224
10398
|
`.${basename}.pixelkiln-stage-${token}-${index}`
|
|
10225
10399
|
);
|
|
10226
10400
|
if (artifact.existed) {
|
|
10227
|
-
artifact.backup =
|
|
10228
|
-
|
|
10401
|
+
artifact.backup = import_node_path9.default.join(
|
|
10402
|
+
import_node_path9.default.dirname(artifact.destination),
|
|
10229
10403
|
`.${basename}.pixelkiln-backup-${token}-${index}`
|
|
10230
10404
|
);
|
|
10231
10405
|
}
|
|
10232
10406
|
}
|
|
10233
|
-
const journal = options.recoveryFile ?
|
|
10407
|
+
const journal = options.recoveryFile ? import_node_path9.default.resolve(options.recoveryFile) : null;
|
|
10234
10408
|
if (journal) {
|
|
10235
|
-
await (0, import_promises5.mkdir)(
|
|
10409
|
+
await (0, import_promises5.mkdir)(import_node_path9.default.dirname(journal), { recursive: true });
|
|
10236
10410
|
const transaction = {
|
|
10237
10411
|
format: "pixelkiln-artifact-transaction",
|
|
10238
10412
|
version: 1,
|
|
@@ -10257,7 +10431,7 @@ async function writeArtifactBundle(files, options = {}) {
|
|
|
10257
10431
|
}
|
|
10258
10432
|
try {
|
|
10259
10433
|
for (const [index, artifact] of changed.entries()) {
|
|
10260
|
-
await (0, import_promises5.mkdir)(
|
|
10434
|
+
await (0, import_promises5.mkdir)(import_node_path9.default.dirname(artifact.destination), { recursive: true });
|
|
10261
10435
|
await options.beforeStage?.(artifact.destination, index);
|
|
10262
10436
|
await (0, import_promises5.writeFile)(artifact.stage, artifact.data, { flag: "wx" });
|
|
10263
10437
|
}
|
|
@@ -10523,10 +10697,10 @@ function sameFrameGrid(first, next) {
|
|
|
10523
10697
|
return next.detection.stepX === first.detection.stepX && next.detection.stepY === first.detection.stepY && next.detection.columns === first.detection.columns && next.detection.rows === first.detection.rows && phaseMatches(next.detection.phaseX, first.detection.phaseX, first.detection.stepX) && phaseMatches(next.detection.phaseY, first.detection.phaseY, first.detection.stepY) && next.audit.width === first.audit.width && next.audit.height === first.audit.height && next.sourceWidth === first.sourceWidth && next.sourceHeight === first.sourceHeight;
|
|
10524
10698
|
}
|
|
10525
10699
|
async function refineAsset(options) {
|
|
10526
|
-
const source =
|
|
10527
|
-
const output =
|
|
10700
|
+
const source = import_node_path10.default.resolve(options.source);
|
|
10701
|
+
const output = import_node_path10.default.resolve(options.output);
|
|
10528
10702
|
if (source === output) throw new Error("Refinement source and output must be different files.");
|
|
10529
|
-
const record =
|
|
10703
|
+
const record = import_node_path10.default.resolve(qualityRecordPath(output));
|
|
10530
10704
|
const palette = normalizePalette(options.palette).map((color) => color.hex);
|
|
10531
10705
|
const minGridConfidence = requireConfidence(options.minGridConfidence ?? "high");
|
|
10532
10706
|
if (options.minTransparency !== void 0 && (!Number.isFinite(options.minTransparency) || options.minTransparency < 0 || options.minTransparency > 1)) throw new Error("minTransparency must be between 0 and 1.");
|
|
@@ -10549,8 +10723,8 @@ async function refineAsset(options) {
|
|
|
10549
10723
|
);
|
|
10550
10724
|
}
|
|
10551
10725
|
}
|
|
10552
|
-
const temp = await (0, import_promises6.mkdtemp)(
|
|
10553
|
-
const recovered =
|
|
10726
|
+
const temp = await (0, import_promises6.mkdtemp)(import_node_path10.default.join((0, import_node_os3.tmpdir)(), "pixelkiln-refine-"));
|
|
10727
|
+
const recovered = import_node_path10.default.join(temp, "native.png");
|
|
10554
10728
|
const fixerCommand = compatibilityCommand ?? options.fixerPython ?? process.env.PIXELKILN_PIXEL_FIXER_PYTHON ?? "python3";
|
|
10555
10729
|
const args = compatibilityCommand ? [...options.fixerArgsPrefix ?? [], source, "--extract", recovered] : ["-c", PYTHON_FIXER_BRIDGE, source, recovered];
|
|
10556
10730
|
try {
|
|
@@ -10635,18 +10809,18 @@ async function refineFrameSet(options) {
|
|
|
10635
10809
|
if (roles.some((role) => !role.trim()) || new Set(roles).size !== roles.length) {
|
|
10636
10810
|
throw new Error("Frame-set roles must be non-empty and unique.");
|
|
10637
10811
|
}
|
|
10638
|
-
const output =
|
|
10639
|
-
const record =
|
|
10812
|
+
const output = import_node_path10.default.resolve(options.output);
|
|
10813
|
+
const record = import_node_path10.default.resolve(qualityRecordPath(output));
|
|
10640
10814
|
const palette = normalizeRefinementPalette(options.palette);
|
|
10641
10815
|
const finalOutputs = options.sources.map((source, index) => qualityFrameOutputPath(output, source.role, index));
|
|
10642
10816
|
if (new Set(finalOutputs).size !== finalOutputs.length) {
|
|
10643
10817
|
throw new Error("Frame-set roles resolve to duplicate output filenames.");
|
|
10644
10818
|
}
|
|
10645
|
-
const sourceSet = new Set(options.sources.map((source) =>
|
|
10819
|
+
const sourceSet = new Set(options.sources.map((source) => import_node_path10.default.resolve(source.path)));
|
|
10646
10820
|
if (finalOutputs.some((file) => sourceSet.has(file))) {
|
|
10647
10821
|
throw new Error("Frame-set refinement sources and outputs must be different files.");
|
|
10648
10822
|
}
|
|
10649
|
-
const temp = await (0, import_promises6.mkdtemp)(
|
|
10823
|
+
const temp = await (0, import_promises6.mkdtemp)(import_node_path10.default.join((0, import_node_os3.tmpdir)(), "pixelkiln-refine-frames-"));
|
|
10650
10824
|
try {
|
|
10651
10825
|
const frames = [];
|
|
10652
10826
|
const outputs = [];
|
|
@@ -10654,8 +10828,8 @@ async function refineFrameSet(options) {
|
|
|
10654
10828
|
const sourcePaths = [];
|
|
10655
10829
|
let firstOptions = null;
|
|
10656
10830
|
for (const [index, sourceEntry] of options.sources.entries()) {
|
|
10657
|
-
const source =
|
|
10658
|
-
const temporaryOutput =
|
|
10831
|
+
const source = import_node_path10.default.resolve(sourceEntry.path);
|
|
10832
|
+
const temporaryOutput = import_node_path10.default.join(temp, `${String(index).padStart(2, "0")}.png`);
|
|
10659
10833
|
const refined = await refineAsset({
|
|
10660
10834
|
source,
|
|
10661
10835
|
output: temporaryOutput,
|
|
@@ -10730,7 +10904,7 @@ async function refineFrameSet(options) {
|
|
|
10730
10904
|
);
|
|
10731
10905
|
return {
|
|
10732
10906
|
sources: sourcePaths,
|
|
10733
|
-
outputs: outputs.map((item) =>
|
|
10907
|
+
outputs: outputs.map((item) => import_node_path10.default.resolve(item.path)),
|
|
10734
10908
|
record,
|
|
10735
10909
|
frames,
|
|
10736
10910
|
palette,
|
|
@@ -10741,12 +10915,12 @@ async function refineFrameSet(options) {
|
|
|
10741
10915
|
}
|
|
10742
10916
|
}
|
|
10743
10917
|
async function checkQualityRecord(recordPath) {
|
|
10744
|
-
const record =
|
|
10918
|
+
const record = import_node_path10.default.resolve(recordPath);
|
|
10745
10919
|
const manifest = await readArtifactBundleManifest(record);
|
|
10746
10920
|
const options = parseRecordOptions(manifest, record);
|
|
10747
10921
|
const verification = await verifyArtifactBundle(record);
|
|
10748
|
-
const sources = manifest.sources.map((source2) =>
|
|
10749
|
-
const outputs = manifest.outputs.map((output2) =>
|
|
10922
|
+
const sources = manifest.sources.map((source2) => import_node_path10.default.resolve(import_node_path10.default.dirname(record), source2.path));
|
|
10923
|
+
const outputs = manifest.outputs.map((output2) => import_node_path10.default.resolve(import_node_path10.default.dirname(record), output2.path));
|
|
10750
10924
|
const source = sources[0] ?? "";
|
|
10751
10925
|
const output = outputs[0] ?? "";
|
|
10752
10926
|
const expectedCount = options.frameSet?.count ?? 1;
|
|
@@ -10779,7 +10953,7 @@ async function checkQualityRecord(recordPath) {
|
|
|
10779
10953
|
async function approveQualityRecord(recordPath, approval) {
|
|
10780
10954
|
const reviewer = approval.reviewer.trim();
|
|
10781
10955
|
if (!reviewer) throw new Error("A human reviewer name is required.");
|
|
10782
|
-
const record =
|
|
10956
|
+
const record = import_node_path10.default.resolve(recordPath);
|
|
10783
10957
|
const manifest = await readArtifactBundleManifest(record);
|
|
10784
10958
|
const options = parseRecordOptions(manifest, record);
|
|
10785
10959
|
const verification = await verifyArtifactBundle(record);
|
|
@@ -10800,14 +10974,14 @@ async function approveQualityRecord(recordPath, approval) {
|
|
|
10800
10974
|
`A refinement record must contain exactly ${expectedCount} source(s) and output(s).`
|
|
10801
10975
|
);
|
|
10802
10976
|
}
|
|
10803
|
-
const root =
|
|
10977
|
+
const root = import_node_path10.default.dirname(record);
|
|
10804
10978
|
const outputs = await Promise.all(manifest.outputs.map(async (entry) => {
|
|
10805
|
-
const outputPath =
|
|
10979
|
+
const outputPath = import_node_path10.default.resolve(root, entry.path);
|
|
10806
10980
|
return { path: outputPath, data: await (0, import_promises6.readFile)(outputPath) };
|
|
10807
10981
|
}));
|
|
10808
10982
|
const sources = manifest.sources.map((source) => ({
|
|
10809
10983
|
...source,
|
|
10810
|
-
path:
|
|
10984
|
+
path: import_node_path10.default.resolve(root, source.path)
|
|
10811
10985
|
}));
|
|
10812
10986
|
const approved = {
|
|
10813
10987
|
...options,
|
|
@@ -10839,8 +11013,8 @@ async function qualitySource(spec, lock) {
|
|
|
10839
11013
|
if (spec.generator === "frames") {
|
|
10840
11014
|
return { items: null, reason: "frame-set quality requires ordered downloaded provider outputs" };
|
|
10841
11015
|
}
|
|
10842
|
-
const source =
|
|
10843
|
-
return (0,
|
|
11016
|
+
const source = import_node_path11.default.resolve(spec.root, spec.source);
|
|
11017
|
+
return (0, import_node_fs8.existsSync)(source) ? { items: [{ role: "source", path: source }] } : { items: null, reason: `declared source is missing: ${spec.source}` };
|
|
10844
11018
|
}
|
|
10845
11019
|
const entry = lock.entries[lockKey(spec.styleId, spec.assetId)];
|
|
10846
11020
|
if (!entry) return { items: null, reason: "raw provider output is not in the lockfile" };
|
|
@@ -10869,7 +11043,7 @@ async function qualitySource(spec, lock) {
|
|
|
10869
11043
|
}
|
|
10870
11044
|
if (role) roles.add(role);
|
|
10871
11045
|
const source = currentEntryOutputPath(entry, spec, index);
|
|
10872
|
-
if (!(0,
|
|
11046
|
+
if (!(0, import_node_fs8.existsSync)(source)) return { items: null, reason: `raw provider output is missing: ${source}` };
|
|
10873
11047
|
if (await sha256File(source) !== output.sha256) {
|
|
10874
11048
|
return { items: null, reason: "raw provider output was modified after download" };
|
|
10875
11049
|
}
|
|
@@ -10888,7 +11062,7 @@ async function inspectQualityProfile(spec, lock) {
|
|
|
10888
11062
|
}
|
|
10889
11063
|
const sourcePaths = source.items.map((item) => item.path);
|
|
10890
11064
|
const expectedOutputs = spec.generator === "frames" ? source.items.map((item, index) => qualityFrameOutputPath(output, item.role, index)) : [output];
|
|
10891
|
-
if (!(0,
|
|
11065
|
+
if (!(0, import_node_fs8.existsSync)(record)) {
|
|
10892
11066
|
return {
|
|
10893
11067
|
key,
|
|
10894
11068
|
state: "needs-refinement",
|
|
@@ -10906,7 +11080,7 @@ async function inspectQualityProfile(spec, lock) {
|
|
|
10906
11080
|
const expectedRevision = spec.quality.fixerRevision ?? PIXEL_ART_FIXER_REVISION;
|
|
10907
11081
|
const expectedTransparency = spec.quality.minTransparency ?? null;
|
|
10908
11082
|
const expectedFrameSet = spec.generator === "frames" ? { fps: spec.quality.fps ?? 12, count: source.items.length, roles: source.items.map((item) => item.role) } : null;
|
|
10909
|
-
const contractChanged = JSON.stringify(check.sources.map((item) =>
|
|
11083
|
+
const contractChanged = JSON.stringify(check.sources.map((item) => import_node_path11.default.resolve(item))) !== JSON.stringify(sourcePaths.map((item) => import_node_path11.default.resolve(item))) || JSON.stringify(check.outputs.map((item) => import_node_path11.default.resolve(item))) !== JSON.stringify(expectedOutputs.map((item) => import_node_path11.default.resolve(item))) || JSON.stringify(check.options.palette.colors) !== JSON.stringify(expectedPalette) || check.options.nativeGrid.revision !== expectedRevision || check.options.audit.thresholds.minGridConfidence !== spec.quality.minGridConfidence || check.options.audit.thresholds.minTransparency !== expectedTransparency || (expectedFrameSet ? !check.options.frameSet || check.options.frameSet.fps !== expectedFrameSet.fps || check.options.frameSet.count !== expectedFrameSet.count || JSON.stringify(check.options.frameSet.roles) !== JSON.stringify(expectedFrameSet.roles) : check.options.frameSet !== void 0);
|
|
10910
11084
|
if (contractChanged) {
|
|
10911
11085
|
return {
|
|
10912
11086
|
key,
|
|
@@ -11069,8 +11243,8 @@ async function requireApprovedQualitySources(specs, lock, assetIds, outputRoles)
|
|
|
11069
11243
|
}
|
|
11070
11244
|
|
|
11071
11245
|
// src/pipeline/revision.ts
|
|
11072
|
-
var
|
|
11073
|
-
var
|
|
11246
|
+
var import_node_fs9 = require("fs");
|
|
11247
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
11074
11248
|
async function inspectRevisionReadiness(spec, lock) {
|
|
11075
11249
|
if (spec.mirror) return inspectMirror(spec, lock, /* @__PURE__ */ new Set());
|
|
11076
11250
|
if (spec.character?.kind === "outfit") return inspectOutfit(spec, lock, /* @__PURE__ */ new Set());
|
|
@@ -11086,7 +11260,7 @@ async function inspectStyleAnchor(spec, lock, seen) {
|
|
|
11086
11260
|
const label = `style anchor ${spec.styleId}/${anchor.assetId}`;
|
|
11087
11261
|
const dependency = await inspectParent(anchor.spec, lock, seen);
|
|
11088
11262
|
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
11089
|
-
if (!anchor.sha256 || !(0,
|
|
11263
|
+
if (!anchor.sha256 || !(0, import_node_fs9.existsSync)(anchor.file)) return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
11090
11264
|
if (await sha256File(anchor.file) !== anchor.sha256) {
|
|
11091
11265
|
return { ready: false, reason: `${label} changed after the manifest was resolved` };
|
|
11092
11266
|
}
|
|
@@ -11100,7 +11274,7 @@ async function inspectScene(spec) {
|
|
|
11100
11274
|
const files = [{ label: "scene image", file: scene.file, sha256: scene.sha256 }];
|
|
11101
11275
|
if (scene.placement.type === "mask") files.push({ label: "scene mask", file: scene.placement.file, sha256: scene.placement.sha256 });
|
|
11102
11276
|
for (const { label, file, sha256: sha2562 } of files) {
|
|
11103
|
-
if (!sha2562 || !(0,
|
|
11277
|
+
if (!sha2562 || !(0, import_node_fs9.existsSync)(file)) return { ready: false, reason: `${label} does not exist yet: ${file}` };
|
|
11104
11278
|
if (await sha256File(file) !== sha2562) return { ready: false, reason: `${label} changed after the manifest was resolved` };
|
|
11105
11279
|
}
|
|
11106
11280
|
return { ready: true, reason: "scene is current" };
|
|
@@ -11117,7 +11291,7 @@ async function inspectOutfit(spec, lock, seen) {
|
|
|
11117
11291
|
const label = `outfit source ${spec.styleId}/${character.parentAssetId}`;
|
|
11118
11292
|
const dependency = await inspectParent(character.parentSpec, lock, seen);
|
|
11119
11293
|
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
11120
|
-
if (!(0,
|
|
11294
|
+
if (!(0, import_node_fs9.existsSync)(outfit.reference.path)) {
|
|
11121
11295
|
return { ready: false, reason: `outfit reference is missing: ${outfit.reference.path}` };
|
|
11122
11296
|
}
|
|
11123
11297
|
if (await sha256File(outfit.reference.path) !== outfit.reference.sha256) {
|
|
@@ -11131,7 +11305,7 @@ async function inspectCharacterParent(spec, lock, seen) {
|
|
|
11131
11305
|
const label = `${character.kind} parent ${spec.styleId}/${character.parentAssetId}`;
|
|
11132
11306
|
const dependency = await inspectParent(parentSpec, lock, seen);
|
|
11133
11307
|
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
11134
|
-
if (!character.parentSha256 || !character.parentFile || !(0,
|
|
11308
|
+
if (!character.parentSha256 || !character.parentFile || !(0, import_node_fs9.existsSync)(character.parentFile)) {
|
|
11135
11309
|
return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
11136
11310
|
}
|
|
11137
11311
|
if (await sha256File(character.parentFile) !== character.parentSha256) {
|
|
@@ -11149,7 +11323,7 @@ async function inspectObjectProParent(spec, lock, seen) {
|
|
|
11149
11323
|
const label = `${object.kind} parent ${spec.styleId}/${object.parentAssetId}`;
|
|
11150
11324
|
const dependency = await inspectParent(parentSpec, lock, seen);
|
|
11151
11325
|
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
11152
|
-
if (!object.parentSha256 || !object.parentFile || !(0,
|
|
11326
|
+
if (!object.parentSha256 || !object.parentFile || !(0, import_node_fs9.existsSync)(object.parentFile)) {
|
|
11153
11327
|
return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
11154
11328
|
}
|
|
11155
11329
|
if (await sha256File(object.parentFile) !== object.parentSha256) {
|
|
@@ -11168,14 +11342,14 @@ async function inspectRevision(spec, lock, seen) {
|
|
|
11168
11342
|
if (!dependency.ready) {
|
|
11169
11343
|
return {
|
|
11170
11344
|
ready: false,
|
|
11171
|
-
reason: `revision source ${spec.styleId}/${revision.sourceAssetId} is not ready: ${dependency.reason}`
|
|
11345
|
+
reason: `revision source ${revision.sourceStyleId ?? spec.styleId}/${revision.sourceAssetId} is not ready: ${dependency.reason}`
|
|
11172
11346
|
};
|
|
11173
11347
|
}
|
|
11174
11348
|
if (revision.sourceMembers) {
|
|
11175
11349
|
const members = await inspectSourceMembers(spec, lock);
|
|
11176
11350
|
if (!members.ready) return members;
|
|
11177
11351
|
} else {
|
|
11178
|
-
if (!revision.sourceSha256 || !(0,
|
|
11352
|
+
if (!revision.sourceSha256 || !(0, import_node_fs9.existsSync)(revision.sourceFile)) {
|
|
11179
11353
|
return { ready: false, reason: `revision source is missing: ${revision.sourceFile}` };
|
|
11180
11354
|
}
|
|
11181
11355
|
if (await sha256File(revision.sourceFile) !== revision.sourceSha256) {
|
|
@@ -11183,7 +11357,7 @@ async function inspectRevision(spec, lock, seen) {
|
|
|
11183
11357
|
}
|
|
11184
11358
|
}
|
|
11185
11359
|
if (revision.maskFile) {
|
|
11186
|
-
if (!revision.maskSha256 || !(0,
|
|
11360
|
+
if (!revision.maskSha256 || !(0, import_node_fs9.existsSync)(revision.maskFile)) {
|
|
11187
11361
|
return { ready: false, reason: `revision mask is missing: ${revision.maskFile}` };
|
|
11188
11362
|
}
|
|
11189
11363
|
if (await sha256File(revision.maskFile) !== revision.maskSha256) {
|
|
@@ -11191,7 +11365,7 @@ async function inspectRevision(spec, lock, seen) {
|
|
|
11191
11365
|
}
|
|
11192
11366
|
}
|
|
11193
11367
|
if (revision.lastFrameFile) {
|
|
11194
|
-
if (!revision.lastFrameSha256 || !(0,
|
|
11368
|
+
if (!revision.lastFrameSha256 || !(0, import_node_fs9.existsSync)(revision.lastFrameFile)) {
|
|
11195
11369
|
return { ready: false, reason: `revision last frame is missing: ${revision.lastFrameFile}` };
|
|
11196
11370
|
}
|
|
11197
11371
|
if (await sha256File(revision.lastFrameFile) !== revision.lastFrameSha256) {
|
|
@@ -11204,7 +11378,7 @@ async function inspectSourceMembers(spec, lock) {
|
|
|
11204
11378
|
const revision = spec.revision;
|
|
11205
11379
|
const members = revision.sourceMembers;
|
|
11206
11380
|
for (const member of members) {
|
|
11207
|
-
if (!(0,
|
|
11381
|
+
if (!(0, import_node_fs9.existsSync)(member.file)) return { ready: false, reason: `revision source member is missing: ${member.file}` };
|
|
11208
11382
|
if (await sha256File(member.file) !== member.sha256) {
|
|
11209
11383
|
return { ready: false, reason: `revision source member changed after the manifest was resolved: ${member.file}` };
|
|
11210
11384
|
}
|
|
@@ -11213,8 +11387,8 @@ async function inspectSourceMembers(spec, lock) {
|
|
|
11213
11387
|
if (parent.source) return { ready: true, reason: "declared source set is present" };
|
|
11214
11388
|
const entry = lock.entries[lockKey(parent.styleId, parent.assetId)];
|
|
11215
11389
|
if (!entry) return { ready: false, reason: "revision source is not in the lockfile" };
|
|
11216
|
-
const recorded = entry.outputs.map((_, index) =>
|
|
11217
|
-
const found = members.map((member) =>
|
|
11390
|
+
const recorded = entry.outputs.map((_, index) => import_node_path12.default.resolve(currentEntryOutputPath(entry, parent, index))).sort();
|
|
11391
|
+
const found = members.map((member) => import_node_path12.default.resolve(member.file)).sort();
|
|
11218
11392
|
if (recorded.length !== found.length || recorded.some((file, index) => file !== found[index])) {
|
|
11219
11393
|
return {
|
|
11220
11394
|
ready: false,
|
|
@@ -11263,8 +11437,8 @@ async function inspectParent(spec, lock, seen) {
|
|
|
11263
11437
|
};
|
|
11264
11438
|
}
|
|
11265
11439
|
if (spec.source) {
|
|
11266
|
-
const source =
|
|
11267
|
-
return (0,
|
|
11440
|
+
const source = import_node_path12.default.resolve(spec.root, spec.source);
|
|
11441
|
+
return (0, import_node_fs9.existsSync)(source) ? { ready: true, reason: "declared source is present" } : { ready: false, reason: `declared source is missing: ${spec.source}` };
|
|
11268
11442
|
}
|
|
11269
11443
|
const entry = lock.entries[key];
|
|
11270
11444
|
if (!entry) return { ready: false, reason: "parent is not in the lockfile" };
|
|
@@ -11278,7 +11452,7 @@ async function inspectParent(spec, lock, seen) {
|
|
|
11278
11452
|
for (let index = 0; index < entry.outputs.length; index++) {
|
|
11279
11453
|
const output = entry.outputs[index];
|
|
11280
11454
|
const file = currentEntryOutputPath(entry, spec, index);
|
|
11281
|
-
if (!(0,
|
|
11455
|
+
if (!(0, import_node_fs9.existsSync)(file)) return { ready: false, reason: `parent output is missing: ${file}` };
|
|
11282
11456
|
if (await sha256File(file) !== output.sha256) {
|
|
11283
11457
|
return { ready: false, reason: `parent output was modified: ${file}` };
|
|
11284
11458
|
}
|
|
@@ -11342,7 +11516,7 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
11342
11516
|
state = "missing";
|
|
11343
11517
|
reason = "--force";
|
|
11344
11518
|
} else if (!entry && spec.source && spec.generator !== "frames") {
|
|
11345
|
-
if ((0,
|
|
11519
|
+
if ((0, import_node_fs10.existsSync)(import_node_path13.default.resolve(spec.root, spec.source))) {
|
|
11346
11520
|
state = "ok";
|
|
11347
11521
|
reason = `placed from ${spec.source}; not generated`;
|
|
11348
11522
|
} else {
|
|
@@ -11350,7 +11524,7 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
11350
11524
|
reason = `declared source is missing: ${spec.source}`;
|
|
11351
11525
|
}
|
|
11352
11526
|
} else if (!entry) {
|
|
11353
|
-
if ((0,
|
|
11527
|
+
if ((0, import_node_fs10.existsSync)(spec.outFile)) {
|
|
11354
11528
|
state = "untracked";
|
|
11355
11529
|
reason = "file on disk, no provenance recorded";
|
|
11356
11530
|
} else {
|
|
@@ -11363,7 +11537,7 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
11363
11537
|
} else if (spec.mirror && !mirrorCurrent(entry, lock.entries[lockKey(spec.styleId, spec.mirror.sourceAssetId)])) {
|
|
11364
11538
|
state = "stale";
|
|
11365
11539
|
reason = `mirror source ${spec.mirror.sourceAssetId} changed; flip it again (no generation cost)`;
|
|
11366
|
-
} else if (spec.mirror && entry.status === "downloaded" && entry.outputs.some((_output, index) => !(0,
|
|
11540
|
+
} else if (spec.mirror && entry.status === "downloaded" && entry.outputs.some((_output, index) => !(0, import_node_fs10.existsSync)(currentEntryOutputPath(entry, spec, index)))) {
|
|
11367
11541
|
state = "stale";
|
|
11368
11542
|
reason = "mirror output missing on disk; flip it again (no generation cost)";
|
|
11369
11543
|
} else if (spec.character?.kind === "outfit" && entry.outfit?.sourceSha256 !== mirrorSourceHash(lock.entries[lockKey(spec.styleId, spec.character.parentAssetId)] ?? { outputs: [] })) {
|
|
@@ -11391,9 +11565,9 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
11391
11565
|
} else if (entry.status !== "downloaded") {
|
|
11392
11566
|
state = "in-flight";
|
|
11393
11567
|
reason = `awaiting ${entry.status}`;
|
|
11394
|
-
} else if (entry.outputs.length === 0 || entry.outputs.some((_output, index) => !(0,
|
|
11568
|
+
} else if (entry.outputs.length === 0 || entry.outputs.some((_output, index) => !(0, import_node_fs10.existsSync)(currentEntryOutputPath(entry, spec, index)))) {
|
|
11395
11569
|
state = "orphaned";
|
|
11396
|
-
reason = entry.outputs.length === 0 ? "no recorded output files" : `missing on disk: ${entry.outputs.filter((_output, index) => !(0,
|
|
11570
|
+
reason = entry.outputs.length === 0 ? "no recorded output files" : `missing on disk: ${entry.outputs.filter((_output, index) => !(0, import_node_fs10.existsSync)(currentEntryOutputPath(entry, spec, index))).map((o) => o.role ?? "asset").join(", ")}`;
|
|
11397
11571
|
} else if (await anyOutputModified(entry, spec)) {
|
|
11398
11572
|
state = "orphaned";
|
|
11399
11573
|
reason = "output modified since download";
|
|
@@ -11484,13 +11658,13 @@ function summarize(plan) {
|
|
|
11484
11658
|
|
|
11485
11659
|
// src/project.ts
|
|
11486
11660
|
async function openProject(manifestPath = "pixelkiln.manifest.json", opts = {}) {
|
|
11487
|
-
const absolute =
|
|
11488
|
-
const root =
|
|
11661
|
+
const absolute = import_node_path14.default.resolve(manifestPath);
|
|
11662
|
+
const root = import_node_path14.default.dirname(absolute);
|
|
11489
11663
|
if (opts.env !== false) {
|
|
11490
11664
|
loadEnvFiles(root);
|
|
11491
|
-
if (
|
|
11665
|
+
if (import_node_path14.default.resolve(process.cwd()) !== root) loadEnvFiles(process.cwd());
|
|
11492
11666
|
}
|
|
11493
|
-
const lockPath =
|
|
11667
|
+
const lockPath = import_node_path14.default.resolve(opts.lockPath ?? import_node_path14.default.join(root, "pixelkiln.lock.json"));
|
|
11494
11668
|
const loaded = await loadManifest(absolute);
|
|
11495
11669
|
const specs = await resolveSpecs(loaded, { styles: opts.styles ?? [], assets: opts.assets ?? [] });
|
|
11496
11670
|
const lock = await loadLock(lockPath);
|
|
@@ -11557,8 +11731,8 @@ function shouldPersistSourceUrl(value) {
|
|
|
11557
11731
|
}
|
|
11558
11732
|
|
|
11559
11733
|
// src/recipes.ts
|
|
11560
|
-
var
|
|
11561
|
-
var
|
|
11734
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
11735
|
+
var import_node_fs11 = require("fs");
|
|
11562
11736
|
var import_promises7 = require("fs/promises");
|
|
11563
11737
|
var import_node_url = require("url");
|
|
11564
11738
|
var import_zod4 = require("zod");
|
|
@@ -11568,7 +11742,7 @@ var VERSION_RE = /^\d+\.\d+\.\d+$/;
|
|
|
11568
11742
|
var RECIPE_FILE = "pixelkiln.recipe.json";
|
|
11569
11743
|
var RECIPE_DIR_TOKEN = "{{recipeDir}}";
|
|
11570
11744
|
var PortablePathSchema = import_zod4.z.string().min(1).refine((value) => {
|
|
11571
|
-
if (value.includes("\\") ||
|
|
11745
|
+
if (value.includes("\\") || import_node_path15.default.posix.isAbsolute(value)) return false;
|
|
11572
11746
|
const parts = value.split("/");
|
|
11573
11747
|
return !parts.includes("") && !parts.includes(".") && !parts.includes("..");
|
|
11574
11748
|
}, "expected a portable relative path without '.', '..', or backslashes");
|
|
@@ -11744,7 +11918,7 @@ function message3(error) {
|
|
|
11744
11918
|
return error instanceof Error ? error.message : String(error);
|
|
11745
11919
|
}
|
|
11746
11920
|
async function readRecipeFile(recipePath, bundled) {
|
|
11747
|
-
const absolute =
|
|
11921
|
+
const absolute = import_node_path15.default.resolve(recipePath);
|
|
11748
11922
|
let raw;
|
|
11749
11923
|
try {
|
|
11750
11924
|
raw = JSON.parse(await (0, import_promises7.readFile)(absolute, "utf8"));
|
|
@@ -11758,13 +11932,13 @@ async function readRecipeFile(recipePath, bundled) {
|
|
|
11758
11932
|
` + parsed.error.issues.map((issue) => ` ${issue.path.join(".") || "$"}: ${issue.message}`).join("\n")
|
|
11759
11933
|
);
|
|
11760
11934
|
}
|
|
11761
|
-
return { recipe: parsed.data, authored: raw, path: absolute, dir:
|
|
11935
|
+
return { recipe: parsed.data, authored: raw, path: absolute, dir: import_node_path15.default.dirname(absolute), bundled };
|
|
11762
11936
|
}
|
|
11763
11937
|
async function walkRecipeFiles(root) {
|
|
11764
|
-
if (!(0,
|
|
11938
|
+
if (!(0, import_node_fs11.existsSync)(root)) return [];
|
|
11765
11939
|
const out = [];
|
|
11766
11940
|
for (const entry of await (0, import_promises7.readdir)(root, { withFileTypes: true })) {
|
|
11767
|
-
const resolved =
|
|
11941
|
+
const resolved = import_node_path15.default.join(root, entry.name);
|
|
11768
11942
|
if (entry.isDirectory()) out.push(...await walkRecipeFiles(resolved));
|
|
11769
11943
|
else if (entry.isFile() && entry.name === RECIPE_FILE) out.push(resolved);
|
|
11770
11944
|
}
|
|
@@ -11775,7 +11949,7 @@ function bundledRecipeRoot() {
|
|
|
11775
11949
|
}
|
|
11776
11950
|
async function listBundledRecipes(root = bundledRecipeRoot()) {
|
|
11777
11951
|
const recipes = await Promise.all(
|
|
11778
|
-
(await walkRecipeFiles(
|
|
11952
|
+
(await walkRecipeFiles(import_node_path15.default.resolve(root))).map((file) => readRecipeFile(file, true))
|
|
11779
11953
|
);
|
|
11780
11954
|
return recipes.sort(
|
|
11781
11955
|
(a, b) => a.recipe.id.localeCompare(b.recipe.id) || compareVersions(b.recipe.version, a.recipe.version)
|
|
@@ -11798,9 +11972,9 @@ function parseSelector(selector) {
|
|
|
11798
11972
|
return { id: match[1], version: match[2] };
|
|
11799
11973
|
}
|
|
11800
11974
|
async function resolveRecipe(target, bundledRoot = bundledRecipeRoot()) {
|
|
11801
|
-
const local =
|
|
11802
|
-
if ((0,
|
|
11803
|
-
const recipePath =
|
|
11975
|
+
const local = import_node_path15.default.resolve(target);
|
|
11976
|
+
if ((0, import_node_fs11.existsSync)(local)) {
|
|
11977
|
+
const recipePath = import_node_path15.default.basename(local) === RECIPE_FILE ? local : import_node_path15.default.join(local, RECIPE_FILE);
|
|
11804
11978
|
return readRecipeFile(recipePath, false);
|
|
11805
11979
|
}
|
|
11806
11980
|
if (target.startsWith(".") || target.startsWith("/") || target.includes("\\")) {
|
|
@@ -11814,8 +11988,8 @@ async function resolveRecipe(target, bundledRoot = bundledRecipeRoot()) {
|
|
|
11814
11988
|
return matches[0];
|
|
11815
11989
|
}
|
|
11816
11990
|
async function verifyFile(root, file) {
|
|
11817
|
-
const absolute =
|
|
11818
|
-
if (!(0,
|
|
11991
|
+
const absolute = import_node_path15.default.resolve(root, ...file.path.split("/"));
|
|
11992
|
+
if (!(0, import_node_fs11.existsSync)(absolute)) {
|
|
11819
11993
|
return { path: file.path, expectedSha256: file.sha256, actualSha256: null, status: "missing" };
|
|
11820
11994
|
}
|
|
11821
11995
|
const actualSha256 = await sha256File(absolute);
|
|
@@ -11830,7 +12004,7 @@ async function verifyRecipe(target, options = {}) {
|
|
|
11830
12004
|
const loaded = await resolveRecipe(target, options.bundledRoot);
|
|
11831
12005
|
const actualIntegrity = recipeDigest(loaded.authored);
|
|
11832
12006
|
const files = await Promise.all(loaded.recipe.files.map((file) => verifyFile(loaded.dir, file)));
|
|
11833
|
-
const modelRoot = options.modelRoot ?
|
|
12007
|
+
const modelRoot = options.modelRoot ? import_node_path15.default.resolve(options.modelRoot) : null;
|
|
11834
12008
|
const models = modelRoot ? await Promise.all(loaded.recipe.models.map(async (model) => ({
|
|
11835
12009
|
...await verifyFile(modelRoot, model),
|
|
11836
12010
|
source: model.source,
|
|
@@ -11864,7 +12038,7 @@ async function verifyRecipe(target, options = {}) {
|
|
|
11864
12038
|
};
|
|
11865
12039
|
}
|
|
11866
12040
|
function renderStyle(style, destination, cwd) {
|
|
11867
|
-
const portableDir =
|
|
12041
|
+
const portableDir = import_node_path15.default.relative(cwd, destination).split(import_node_path15.default.sep).join("/") || ".";
|
|
11868
12042
|
const replace = (value) => {
|
|
11869
12043
|
if (typeof value === "string") return value.replaceAll(RECIPE_DIR_TOKEN, portableDir);
|
|
11870
12044
|
if (Array.isArray(value)) return value.map(replace);
|
|
@@ -11876,24 +12050,24 @@ function renderStyle(style, destination, cwd) {
|
|
|
11876
12050
|
return replace(style);
|
|
11877
12051
|
}
|
|
11878
12052
|
async function installRecipe(target, options = {}) {
|
|
11879
|
-
const cwd =
|
|
12053
|
+
const cwd = import_node_path15.default.resolve(options.cwd ?? process.cwd());
|
|
11880
12054
|
const loaded = await resolveRecipe(target, options.bundledRoot);
|
|
11881
12055
|
const verification = await verifyRecipe(loaded.path);
|
|
11882
12056
|
if (!verification.ok) {
|
|
11883
12057
|
throw new Error(`Recipe ${loaded.recipe.id}@${loaded.recipe.version} failed integrity verification.`);
|
|
11884
12058
|
}
|
|
11885
|
-
const destination = options.out ?
|
|
12059
|
+
const destination = options.out ? import_node_path15.default.resolve(cwd, options.out) : import_node_path15.default.join(cwd, "pixelkiln-recipes", ...loaded.recipe.id.split("/"), loaded.recipe.version);
|
|
11886
12060
|
const sources = [
|
|
11887
12061
|
{ path: RECIPE_FILE, source: loaded.path },
|
|
11888
|
-
...loaded.recipe.files.map((file) => ({ path: file.path, source:
|
|
12062
|
+
...loaded.recipe.files.map((file) => ({ path: file.path, source: import_node_path15.default.join(loaded.dir, ...file.path.split("/")) }))
|
|
11889
12063
|
];
|
|
11890
12064
|
const files = await Promise.all(sources.map(async (file) => ({
|
|
11891
|
-
path:
|
|
12065
|
+
path: import_node_path15.default.join(destination, ...file.path.split("/")),
|
|
11892
12066
|
data: await (0, import_promises7.readFile)(file.source)
|
|
11893
12067
|
})));
|
|
11894
12068
|
if (!options.force) {
|
|
11895
12069
|
for (const file of files) {
|
|
11896
|
-
if (!(0,
|
|
12070
|
+
if (!(0, import_node_fs11.existsSync)(file.path)) continue;
|
|
11897
12071
|
const current = await (0, import_promises7.readFile)(file.path);
|
|
11898
12072
|
if (!current.equals(file.data)) {
|
|
11899
12073
|
throw new OverwriteRefusedError(
|
|
@@ -11919,8 +12093,8 @@ async function installRecipe(target, options = {}) {
|
|
|
11919
12093
|
|
|
11920
12094
|
// src/pipeline/audit.ts
|
|
11921
12095
|
var import_promises8 = require("fs/promises");
|
|
11922
|
-
var
|
|
11923
|
-
var
|
|
12096
|
+
var import_node_fs12 = require("fs");
|
|
12097
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
11924
12098
|
function paletteDistance(asset, reference) {
|
|
11925
12099
|
if (!asset.length || !reference.length) return 0;
|
|
11926
12100
|
let total = 0;
|
|
@@ -11956,7 +12130,7 @@ async function auditStyle(loaded, specs, styleId, lock) {
|
|
|
11956
12130
|
const unreadable = [];
|
|
11957
12131
|
for (const spec of mine) {
|
|
11958
12132
|
for (const output of resolveSpecOutputs(spec, lock, loaded.root)) {
|
|
11959
|
-
if (!(0,
|
|
12133
|
+
if (!(0, import_node_fs12.existsSync)(output.absolutePath)) {
|
|
11960
12134
|
missing.push(output.id);
|
|
11961
12135
|
continue;
|
|
11962
12136
|
}
|
|
@@ -11985,8 +12159,8 @@ async function auditStyle(loaded, specs, styleId, lock) {
|
|
|
11985
12159
|
let referenceFromStyleImages = false;
|
|
11986
12160
|
const refPalettes = [];
|
|
11987
12161
|
for (const img of style.styleImages) {
|
|
11988
|
-
const abs =
|
|
11989
|
-
if (!(0,
|
|
12162
|
+
const abs = import_node_path16.default.resolve(loaded.root, img.path);
|
|
12163
|
+
if (!(0, import_node_fs12.existsSync)(abs)) continue;
|
|
11990
12164
|
try {
|
|
11991
12165
|
refPalettes.push(extractPalette(decodePng(await (0, import_promises8.readFile)(abs)), 12));
|
|
11992
12166
|
} catch {
|
|
@@ -12064,19 +12238,19 @@ function hex(c) {
|
|
|
12064
12238
|
}
|
|
12065
12239
|
|
|
12066
12240
|
// src/pixellab-utils.ts
|
|
12067
|
-
var
|
|
12241
|
+
var import_node_fs13 = require("fs");
|
|
12068
12242
|
var import_promises9 = require("fs/promises");
|
|
12069
|
-
var
|
|
12243
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
12070
12244
|
var UNZOOM_MIN_SIDE = 256;
|
|
12071
12245
|
var UNZOOM_MAX_AREA = 2048 * 2048;
|
|
12072
12246
|
function defaultUnzoomOut(input) {
|
|
12073
|
-
const ext =
|
|
12247
|
+
const ext = import_node_path17.default.extname(input);
|
|
12074
12248
|
return `${ext ? input.slice(0, -ext.length) : input}.unzoomed.png`;
|
|
12075
12249
|
}
|
|
12076
12250
|
async function unzoomFile(client2, input, opts = {}) {
|
|
12077
|
-
const out =
|
|
12078
|
-
if (
|
|
12079
|
-
if ((0,
|
|
12251
|
+
const out = import_node_path17.default.resolve(opts.out ?? defaultUnzoomOut(input));
|
|
12252
|
+
if (import_node_path17.default.resolve(input) === out) throw new Error("unzoom will not overwrite its own input; pass a different --out");
|
|
12253
|
+
if ((0, import_node_fs13.existsSync)(out) && !opts.force) throw new Error(`${out} already exists; pass --force to replace it`);
|
|
12080
12254
|
const bytes = await (0, import_promises9.readFile)(input);
|
|
12081
12255
|
const metadata = imageMetadata(bytes);
|
|
12082
12256
|
if (!metadata) throw new Error(`${input} is not a readable PNG or JPEG`);
|
|
@@ -12090,7 +12264,7 @@ async function unzoomFile(client2, input, opts = {}) {
|
|
|
12090
12264
|
throw new Error(`${input} is ${width}x${height}; PixelLab's unzoom takes at most 2048x2048 worth of pixels`);
|
|
12091
12265
|
}
|
|
12092
12266
|
const res = await client2.unzoom({ image: { base64: bytes.toString("base64"), format }, quantize: opts.quantize });
|
|
12093
|
-
await (0, import_promises9.mkdir)(
|
|
12267
|
+
await (0, import_promises9.mkdir)(import_node_path17.default.dirname(out), { recursive: true });
|
|
12094
12268
|
await (0, import_promises9.writeFile)(out, res.png);
|
|
12095
12269
|
return { out, original: res.originalSize, unzoomed: res.unzoomedSize, zoomFactor: res.zoomFactor, usage: res.usage };
|
|
12096
12270
|
}
|
|
@@ -12098,14 +12272,14 @@ var FONT_WEIGHTS = ["Bold", "Regular"];
|
|
|
12098
12272
|
var FONT_GLYPH_SIZES = [8, 16, 32, 64];
|
|
12099
12273
|
var FONT_COST_GENERATIONS = 25;
|
|
12100
12274
|
function fontOutputPaths(out) {
|
|
12101
|
-
const base =
|
|
12275
|
+
const base = import_node_path17.default.resolve(out.replace(/\.(ttf|png)$/i, ""));
|
|
12102
12276
|
return { ttf: `${base}.ttf`, atlas: `${base}.png` };
|
|
12103
12277
|
}
|
|
12104
12278
|
async function generateFont(client2, opts) {
|
|
12105
12279
|
const { ttf, atlas } = fontOutputPaths(opts.out);
|
|
12106
12280
|
if (!opts.force) {
|
|
12107
12281
|
for (const file of [ttf, atlas]) {
|
|
12108
|
-
if ((0,
|
|
12282
|
+
if ((0, import_node_fs13.existsSync)(file)) throw new Error(`${file} already exists; pass --force to replace it`);
|
|
12109
12283
|
}
|
|
12110
12284
|
}
|
|
12111
12285
|
const sleep3 = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
@@ -12132,7 +12306,7 @@ async function generateFont(client2, opts) {
|
|
|
12132
12306
|
client2.download(job.download_ttf_url),
|
|
12133
12307
|
client2.download(job.download_atlas_url)
|
|
12134
12308
|
]);
|
|
12135
|
-
await (0, import_promises9.mkdir)(
|
|
12309
|
+
await (0, import_promises9.mkdir)(import_node_path17.default.dirname(ttf), { recursive: true });
|
|
12136
12310
|
await (0, import_promises9.writeFile)(ttf, ttfBytes);
|
|
12137
12311
|
await (0, import_promises9.writeFile)(atlas, atlasBytes);
|
|
12138
12312
|
return { jobId, ttf, atlas, glyphPx: job.glyph_px ?? null, usage: job.usage };
|
|
@@ -12147,15 +12321,15 @@ async function generateFont(client2, opts) {
|
|
|
12147
12321
|
}
|
|
12148
12322
|
|
|
12149
12323
|
// src/pipeline/quality-regression.ts
|
|
12150
|
-
var
|
|
12151
|
-
var
|
|
12324
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
12325
|
+
var import_node_fs14 = require("fs");
|
|
12152
12326
|
var import_promises10 = require("fs/promises");
|
|
12153
12327
|
var import_zod5 = require("zod");
|
|
12154
12328
|
var SHA256_RE2 = /^[0-9a-f]{64}$/;
|
|
12155
12329
|
var HEX_RE = /^#[0-9a-f]{6}$/;
|
|
12156
12330
|
var MAX_REDMEAN_DISTANCE = 765;
|
|
12157
12331
|
var QualityPathSchema = import_zod5.z.string().min(1).refine(
|
|
12158
|
-
(value) => !
|
|
12332
|
+
(value) => !import_node_path18.default.posix.isAbsolute(value) && !value.includes("\\"),
|
|
12159
12333
|
"expected a portable relative path"
|
|
12160
12334
|
);
|
|
12161
12335
|
var DEFAULT_QUALITY_TOLERANCES = {
|
|
@@ -12302,7 +12476,7 @@ function measureDecodedImageQuality(png, digest2) {
|
|
|
12302
12476
|
};
|
|
12303
12477
|
}
|
|
12304
12478
|
async function measureImageQuality(file) {
|
|
12305
|
-
const absolute =
|
|
12479
|
+
const absolute = import_node_path18.default.resolve(file);
|
|
12306
12480
|
let bytes;
|
|
12307
12481
|
try {
|
|
12308
12482
|
bytes = await (0, import_promises10.readFile)(absolute);
|
|
@@ -12324,11 +12498,11 @@ function resolveQualityInputs(raw, inputsFilePath) {
|
|
|
12324
12498
|
}
|
|
12325
12499
|
const ids = parsed.data.map((entry) => entry.id);
|
|
12326
12500
|
if (new Set(ids).size !== ids.length) throw new Error("--inputs contains a duplicate quality case id.");
|
|
12327
|
-
const root =
|
|
12501
|
+
const root = import_node_path18.default.dirname(import_node_path18.default.resolve(inputsFilePath));
|
|
12328
12502
|
return parsed.data.map((entry) => ({
|
|
12329
12503
|
id: entry.id,
|
|
12330
|
-
file:
|
|
12331
|
-
...entry.record ? { record:
|
|
12504
|
+
file: import_node_path18.default.resolve(root, entry.path),
|
|
12505
|
+
...entry.record ? { record: import_node_path18.default.resolve(root, entry.record) } : {},
|
|
12332
12506
|
tolerances: QualityToleranceSchema.parse({
|
|
12333
12507
|
...DEFAULT_QUALITY_TOLERANCES,
|
|
12334
12508
|
...entry.tolerances
|
|
@@ -12336,7 +12510,7 @@ function resolveQualityInputs(raw, inputsFilePath) {
|
|
|
12336
12510
|
}));
|
|
12337
12511
|
}
|
|
12338
12512
|
function portableRelative2(from, to) {
|
|
12339
|
-
return
|
|
12513
|
+
return import_node_path18.default.relative(from, import_node_path18.default.resolve(to)).split(import_node_path18.default.sep).join("/") || ".";
|
|
12340
12514
|
}
|
|
12341
12515
|
async function verifyRecordForImage(recordPath, imagePath) {
|
|
12342
12516
|
const verification = await verifyArtifactBundle(recordPath);
|
|
@@ -12351,20 +12525,20 @@ async function verifyRecordForImage(recordPath, imagePath) {
|
|
|
12351
12525
|
if (record.kind !== "refine" || options === null || typeof options !== "object" || options.schema !== "pixelkiln-quality") {
|
|
12352
12526
|
throw new Error(`Quality record ${recordPath} is not a PixelKiln refinement record.`);
|
|
12353
12527
|
}
|
|
12354
|
-
const outputs = record.outputs.map((output) =>
|
|
12355
|
-
if (!outputs.includes(
|
|
12528
|
+
const outputs = record.outputs.map((output) => import_node_path18.default.resolve(import_node_path18.default.dirname(recordPath), output.path));
|
|
12529
|
+
if (!outputs.includes(import_node_path18.default.resolve(imagePath))) {
|
|
12356
12530
|
throw new Error(`Quality record ${recordPath} does not own ${imagePath}.`);
|
|
12357
12531
|
}
|
|
12358
12532
|
}
|
|
12359
12533
|
async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
12360
12534
|
if (!inputs.length) throw new Error("Cannot snapshot an empty quality baseline.");
|
|
12361
|
-
const absolute =
|
|
12535
|
+
const absolute = import_node_path18.default.resolve(baselinePath);
|
|
12362
12536
|
for (const input of inputs) {
|
|
12363
|
-
if (
|
|
12537
|
+
if (import_node_path18.default.resolve(input.file) === absolute || input.record && import_node_path18.default.resolve(input.record) === absolute) {
|
|
12364
12538
|
throw new Error(`Quality baseline output would overwrite an input: ${absolute}.`);
|
|
12365
12539
|
}
|
|
12366
12540
|
}
|
|
12367
|
-
const root =
|
|
12541
|
+
const root = import_node_path18.default.dirname(absolute);
|
|
12368
12542
|
const cases = [];
|
|
12369
12543
|
for (const input of [...inputs].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
12370
12544
|
const expected = await measureImageQuality(input.file);
|
|
@@ -12391,7 +12565,7 @@ async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
|
12391
12565
|
cases
|
|
12392
12566
|
});
|
|
12393
12567
|
const data = Buffer.from(JSON.stringify(baseline, null, 2) + "\n");
|
|
12394
|
-
if ((0,
|
|
12568
|
+
if ((0, import_node_fs14.existsSync)(absolute) && !options.force) {
|
|
12395
12569
|
const current = await (0, import_promises10.readFile)(absolute);
|
|
12396
12570
|
if (!current.equals(data)) {
|
|
12397
12571
|
throw new OverwriteRefusedError(
|
|
@@ -12404,7 +12578,7 @@ async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
|
12404
12578
|
return { baseline, path: absolute, changed: result.changed.length > 0 };
|
|
12405
12579
|
}
|
|
12406
12580
|
async function readQualityBaseline(baselinePath) {
|
|
12407
|
-
const absolute =
|
|
12581
|
+
const absolute = import_node_path18.default.resolve(baselinePath);
|
|
12408
12582
|
let raw;
|
|
12409
12583
|
try {
|
|
12410
12584
|
raw = JSON.parse(await (0, import_promises10.readFile)(absolute, "utf8"));
|
|
@@ -12477,12 +12651,12 @@ function regressionViolations(expected, actual, tolerances) {
|
|
|
12477
12651
|
return { violations, warnings };
|
|
12478
12652
|
}
|
|
12479
12653
|
async function checkQualityBaseline(baselinePath) {
|
|
12480
|
-
const absolute =
|
|
12654
|
+
const absolute = import_node_path18.default.resolve(baselinePath);
|
|
12481
12655
|
const baseline = await readQualityBaseline(absolute);
|
|
12482
|
-
const root =
|
|
12656
|
+
const root = import_node_path18.default.dirname(absolute);
|
|
12483
12657
|
const cases = [];
|
|
12484
12658
|
for (const entry of baseline.cases) {
|
|
12485
|
-
const file =
|
|
12659
|
+
const file = import_node_path18.default.resolve(root, entry.file);
|
|
12486
12660
|
let actual = null;
|
|
12487
12661
|
const violations = [];
|
|
12488
12662
|
const warnings = [];
|
|
@@ -12495,7 +12669,7 @@ async function checkQualityBaseline(baselinePath) {
|
|
|
12495
12669
|
violations.push(message4(error));
|
|
12496
12670
|
}
|
|
12497
12671
|
if (entry.record) {
|
|
12498
|
-
const recordPath =
|
|
12672
|
+
const recordPath = import_node_path18.default.resolve(root, entry.record.path);
|
|
12499
12673
|
try {
|
|
12500
12674
|
const recordHash = await sha256File(recordPath);
|
|
12501
12675
|
if (recordHash !== entry.record.sha256) violations.push("quality record hash changed");
|
|
@@ -12531,14 +12705,14 @@ async function checkQualityBaseline(baselinePath) {
|
|
|
12531
12705
|
}
|
|
12532
12706
|
|
|
12533
12707
|
// src/pipeline/cache-health.ts
|
|
12534
|
-
var
|
|
12708
|
+
var import_node_fs18 = require("fs");
|
|
12535
12709
|
var import_promises13 = require("fs/promises");
|
|
12536
|
-
var
|
|
12710
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
12537
12711
|
|
|
12538
12712
|
// src/cache.ts
|
|
12539
12713
|
var import_promises11 = require("fs/promises");
|
|
12540
|
-
var
|
|
12541
|
-
var
|
|
12714
|
+
var import_node_fs15 = require("fs");
|
|
12715
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
12542
12716
|
var import_zod6 = require("zod");
|
|
12543
12717
|
var HashCacheSchema = import_zod6.z.object({
|
|
12544
12718
|
version: import_zod6.z.literal(1),
|
|
@@ -12552,10 +12726,10 @@ function isSha256Hash(value) {
|
|
|
12552
12726
|
function parseCache(value) {
|
|
12553
12727
|
return HashCacheSchema.parse(value);
|
|
12554
12728
|
}
|
|
12555
|
-
async function loadCache(
|
|
12556
|
-
if (!(0,
|
|
12729
|
+
async function loadCache(path38) {
|
|
12730
|
+
if (!(0, import_node_fs15.existsSync)(path38)) return { version: 1, hashes: {} };
|
|
12557
12731
|
try {
|
|
12558
|
-
const parsed = HashCacheSchema.safeParse(JSON.parse(await (0, import_promises11.readFile)(
|
|
12732
|
+
const parsed = HashCacheSchema.safeParse(JSON.parse(await (0, import_promises11.readFile)(path38, "utf8")));
|
|
12559
12733
|
if (!parsed.success) return { version: 1, hashes: {} };
|
|
12560
12734
|
return {
|
|
12561
12735
|
version: 1,
|
|
@@ -12567,18 +12741,18 @@ async function loadCache(path37) {
|
|
|
12567
12741
|
return { version: 1, hashes: {} };
|
|
12568
12742
|
}
|
|
12569
12743
|
}
|
|
12570
|
-
async function saveCache(
|
|
12744
|
+
async function saveCache(path38, cache) {
|
|
12571
12745
|
const sorted = {};
|
|
12572
12746
|
for (const key of Object.keys(cache.hashes).sort()) {
|
|
12573
12747
|
const hash = cache.hashes[key];
|
|
12574
12748
|
if (!isSha256Hash(hash)) throw new Error(`Refusing to cache invalid SHA-256 for ${key}`);
|
|
12575
12749
|
sorted[key] = hash;
|
|
12576
12750
|
}
|
|
12577
|
-
await (0, import_promises11.mkdir)(
|
|
12578
|
-
const tmp = `${
|
|
12751
|
+
await (0, import_promises11.mkdir)(import_node_path19.default.dirname(import_node_path19.default.resolve(path38)), { recursive: true });
|
|
12752
|
+
const tmp = `${path38}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
12579
12753
|
try {
|
|
12580
12754
|
await (0, import_promises11.writeFile)(tmp, JSON.stringify({ version: 1, hashes: sorted }, null, 2) + "\n");
|
|
12581
|
-
await (0, import_promises11.rename)(tmp,
|
|
12755
|
+
await (0, import_promises11.rename)(tmp, path38);
|
|
12582
12756
|
} finally {
|
|
12583
12757
|
await (0, import_promises11.rm)(tmp, { force: true });
|
|
12584
12758
|
}
|
|
@@ -12602,20 +12776,20 @@ function cachePathFor(lockPath) {
|
|
|
12602
12776
|
}
|
|
12603
12777
|
|
|
12604
12778
|
// src/pipeline/history.ts
|
|
12605
|
-
var
|
|
12606
|
-
var
|
|
12779
|
+
var import_node_fs17 = require("fs");
|
|
12780
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
12607
12781
|
|
|
12608
12782
|
// src/pipeline/fetch.ts
|
|
12609
|
-
var
|
|
12783
|
+
var import_node_fs16 = require("fs");
|
|
12610
12784
|
var import_promises12 = require("fs/promises");
|
|
12611
|
-
var
|
|
12785
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
12612
12786
|
async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
12613
12787
|
const log = opts.onProgress ?? (() => {
|
|
12614
12788
|
});
|
|
12615
12789
|
const result = { downloaded: 0, skipped: 0, failed: 0, ...opts.refresh ? { unchanged: 0 } : {} };
|
|
12616
12790
|
const specByKey = new Map(specs.map((s) => [lockKey(s.styleId, s.assetId), s]));
|
|
12617
12791
|
normalizeLockOutputPaths(lock, specs);
|
|
12618
|
-
const cacheDir = opts.cacheDir === false ? null :
|
|
12792
|
+
const cacheDir = opts.cacheDir === false ? null : import_node_path20.default.resolve(opts.cacheDir ?? import_node_path20.default.join(import_node_path20.default.dirname(lockPath), ".pixelkiln", "cache"));
|
|
12619
12793
|
const pending = Object.entries(lock.entries).filter(([key, e]) => {
|
|
12620
12794
|
if (e.provider !== provider.id || !specByKey.has(key)) return false;
|
|
12621
12795
|
if (specByKey.get(key)?.mirror) return false;
|
|
@@ -12626,15 +12800,15 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12626
12800
|
if (spec && e.specHash === spec.specHash && e.outputs.length && !postprocessCurrent(e, spec)) {
|
|
12627
12801
|
return Boolean(opts.force) || e.outputs.every((output) => {
|
|
12628
12802
|
const file = resolveOutputPath(output.path, spec.root);
|
|
12629
|
-
return (0,
|
|
12803
|
+
return (0, import_node_fs16.existsSync)(file) && sha256((0, import_node_fs16.readFileSync)(file)) === output.sha256;
|
|
12630
12804
|
});
|
|
12631
12805
|
}
|
|
12632
12806
|
if (!opts.repair) return false;
|
|
12633
12807
|
if (e.outputs.length === 0 || !spec) return true;
|
|
12634
12808
|
return e.outputs.some((output) => {
|
|
12635
12809
|
const file = resolveOutputPath(output.path, spec.root);
|
|
12636
|
-
if (!(0,
|
|
12637
|
-
const current = sha256((0,
|
|
12810
|
+
if (!(0, import_node_fs16.existsSync)(file)) return true;
|
|
12811
|
+
const current = sha256((0, import_node_fs16.readFileSync)(file));
|
|
12638
12812
|
if (current === output.sha256) return false;
|
|
12639
12813
|
return Boolean(opts.force) || (e.supersededOutputs ?? []).some((old) => old.sha256 === current);
|
|
12640
12814
|
});
|
|
@@ -12717,14 +12891,14 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12717
12891
|
if (!source.url) throw new Error(`no durable source URL to refresh ${source.role ?? "asset"} from`);
|
|
12718
12892
|
fresh = await provider.download(source.url);
|
|
12719
12893
|
if (recorded && sha256(fresh) === recordedRaw && !reapply) {
|
|
12720
|
-
log(` current ${
|
|
12894
|
+
log(` current ${import_node_path20.default.relative(process.cwd(), target)}`);
|
|
12721
12895
|
outputs.push({ ...recorded, path: portableOutputPath(target, spec.root) });
|
|
12722
12896
|
continue;
|
|
12723
12897
|
}
|
|
12724
12898
|
}
|
|
12725
12899
|
let raw = null;
|
|
12726
12900
|
let final = null;
|
|
12727
|
-
if ((0,
|
|
12901
|
+
if ((0, import_node_fs16.existsSync)(target)) {
|
|
12728
12902
|
const currentHash = await sha256File(target);
|
|
12729
12903
|
if (recorded && currentHash === recorded.sha256) {
|
|
12730
12904
|
if (!fresh && !reapply) {
|
|
@@ -12741,9 +12915,9 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12741
12915
|
}
|
|
12742
12916
|
if (reapply && !fresh) {
|
|
12743
12917
|
if (!recorded.raw) raw = await (0, import_promises12.readFile)(target);
|
|
12744
|
-
log(` reapply ${
|
|
12918
|
+
log(` reapply ${import_node_path20.default.relative(process.cwd(), target)}`);
|
|
12745
12919
|
} else {
|
|
12746
|
-
log(` changed ${
|
|
12920
|
+
log(` changed ${import_node_path20.default.relative(process.cwd(), target)} (upstream)`);
|
|
12747
12921
|
}
|
|
12748
12922
|
} else {
|
|
12749
12923
|
const superseded = (entry.supersededOutputs ?? []).find(
|
|
@@ -12771,7 +12945,7 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12771
12945
|
}
|
|
12772
12946
|
}
|
|
12773
12947
|
log(
|
|
12774
|
-
` replace ${
|
|
12948
|
+
` replace ${import_node_path20.default.relative(process.cwd(), target)}` + (opts.force ? " (--force)" : " (previous tracked generation)")
|
|
12775
12949
|
);
|
|
12776
12950
|
}
|
|
12777
12951
|
}
|
|
@@ -12780,7 +12954,7 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12780
12954
|
} else if (!raw && recorded && cacheDir) {
|
|
12781
12955
|
if (!reapply) final = await readCachedMedia(cacheDir, recorded.sha256, expectedMediaType);
|
|
12782
12956
|
if (!final && recordedRaw) raw = await readCachedMedia(cacheDir, recordedRaw, expectedMediaType);
|
|
12783
|
-
if (final || raw) log(` cached ${
|
|
12957
|
+
if (final || raw) log(` cached ${import_node_path20.default.relative(process.cwd(), target)}`);
|
|
12784
12958
|
}
|
|
12785
12959
|
if (!final && !raw) {
|
|
12786
12960
|
if (!source.url) {
|
|
@@ -12807,11 +12981,11 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12807
12981
|
buf = processed.bytes;
|
|
12808
12982
|
if (processed.changed) {
|
|
12809
12983
|
rawHash = sha256(raw);
|
|
12810
|
-
log(` snapped ${
|
|
12984
|
+
log(` snapped ${import_node_path20.default.relative(process.cwd(), target)} to ${wanted.palette.colors.length} colours`);
|
|
12811
12985
|
}
|
|
12812
12986
|
}
|
|
12813
12987
|
if (cacheDir) await cacheMedia(cacheDir, buf, mediaType);
|
|
12814
|
-
await (0, import_promises12.mkdir)(
|
|
12988
|
+
await (0, import_promises12.mkdir)(import_node_path20.default.dirname(target), { recursive: true });
|
|
12815
12989
|
const tmp = `${target}.pixelkiln.tmp`;
|
|
12816
12990
|
await (0, import_promises12.writeFile)(tmp, buf);
|
|
12817
12991
|
outputs.push({
|
|
@@ -12825,7 +12999,7 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12825
12999
|
await saveLock(lockPath, lock);
|
|
12826
13000
|
await (0, import_promises12.rename)(tmp, target);
|
|
12827
13001
|
wrote = true;
|
|
12828
|
-
log(` wrote ${
|
|
13002
|
+
log(` wrote ${import_node_path20.default.relative(process.cwd(), target)}`);
|
|
12829
13003
|
}
|
|
12830
13004
|
if (opts.refresh && !wrote) {
|
|
12831
13005
|
if (rotated) {
|
|
@@ -12869,8 +13043,8 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12869
13043
|
return result;
|
|
12870
13044
|
}
|
|
12871
13045
|
async function readCachedMedia(cacheDir, hash, mediaType) {
|
|
12872
|
-
const file =
|
|
12873
|
-
if (!(0,
|
|
13046
|
+
const file = import_node_path20.default.join(cacheDir, cacheFileName(hash, mediaType));
|
|
13047
|
+
if (!(0, import_node_fs16.existsSync)(file)) return null;
|
|
12874
13048
|
try {
|
|
12875
13049
|
const buf = await (0, import_promises12.readFile)(file);
|
|
12876
13050
|
if (sha256(buf) !== hash) return null;
|
|
@@ -12883,7 +13057,7 @@ async function readCachedMedia(cacheDir, hash, mediaType) {
|
|
|
12883
13057
|
async function cacheMedia(cacheDir, buf, mediaType, knownHash) {
|
|
12884
13058
|
const hash = knownHash ?? sha256(buf);
|
|
12885
13059
|
validateMedia(buf, mediaType);
|
|
12886
|
-
const file =
|
|
13060
|
+
const file = import_node_path20.default.join(cacheDir, cacheFileName(hash, mediaType));
|
|
12887
13061
|
if (await readCachedMedia(cacheDir, hash, mediaType)) return;
|
|
12888
13062
|
await (0, import_promises12.mkdir)(cacheDir, { recursive: true });
|
|
12889
13063
|
const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
@@ -12891,7 +13065,7 @@ async function cacheMedia(cacheDir, buf, mediaType, knownHash) {
|
|
|
12891
13065
|
try {
|
|
12892
13066
|
await (0, import_promises12.rename)(tmp, file);
|
|
12893
13067
|
} catch (err) {
|
|
12894
|
-
if (!(0,
|
|
13068
|
+
if (!(0, import_node_fs16.existsSync)(file)) throw err;
|
|
12895
13069
|
} finally {
|
|
12896
13070
|
await (0, import_promises12.rm)(tmp, { force: true });
|
|
12897
13071
|
}
|
|
@@ -13010,10 +13184,10 @@ async function revertGeneration(provider, spec, lock, lockPath, opts) {
|
|
|
13010
13184
|
}
|
|
13011
13185
|
for (let i = 0; i < entry.outputs.length; i++) {
|
|
13012
13186
|
const file = currentEntryOutputPath(entry, spec, i);
|
|
13013
|
-
if (!(0,
|
|
13187
|
+
if (!(0, import_node_fs17.existsSync)(file)) continue;
|
|
13014
13188
|
if (await sha256File(file) !== entry.outputs[i].sha256 && !opts.force) {
|
|
13015
13189
|
throw new OverwriteRefusedError(
|
|
13016
|
-
`${
|
|
13190
|
+
`${import_node_path21.default.relative(process.cwd(), file)} was changed after download; keep it with pixelkiln edit, or pass --force to replace it`,
|
|
13017
13191
|
[file]
|
|
13018
13192
|
);
|
|
13019
13193
|
}
|
|
@@ -13065,11 +13239,11 @@ async function revertGeneration(provider, spec, lock, lockPath, opts) {
|
|
|
13065
13239
|
|
|
13066
13240
|
// src/pipeline/cache-health.ts
|
|
13067
13241
|
async function inspectCaches(lock, lockPath, options = {}) {
|
|
13068
|
-
if (options.prune && !(0,
|
|
13069
|
-
throw new Error(`Refusing to prune without an existing lockfile at ${
|
|
13242
|
+
if (options.prune && !(0, import_node_fs18.existsSync)(lockPath)) {
|
|
13243
|
+
throw new Error(`Refusing to prune without an existing lockfile at ${import_node_path22.default.resolve(lockPath)}`);
|
|
13070
13244
|
}
|
|
13071
|
-
const contentDir =
|
|
13072
|
-
const remotePath =
|
|
13245
|
+
const contentDir = import_node_path22.default.resolve(import_node_path22.default.dirname(lockPath), ".pixelkiln", "cache");
|
|
13246
|
+
const remotePath = import_node_path22.default.resolve(cachePathFor(lockPath));
|
|
13073
13247
|
const referenced = referencedHashes(lock);
|
|
13074
13248
|
let content = await inspectContentCache(contentDir, referenced);
|
|
13075
13249
|
let remoteHashes = await inspectRemoteHashCache(remotePath);
|
|
@@ -13081,7 +13255,7 @@ async function inspectCaches(lock, lockPath, options = {}) {
|
|
|
13081
13255
|
]);
|
|
13082
13256
|
for (const name of names) {
|
|
13083
13257
|
try {
|
|
13084
|
-
await (0, import_promises13.rm)(
|
|
13258
|
+
await (0, import_promises13.rm)(import_node_path22.default.join(contentDir, name), { force: true });
|
|
13085
13259
|
removed.contentFiles++;
|
|
13086
13260
|
} catch {
|
|
13087
13261
|
}
|
|
@@ -13108,7 +13282,7 @@ async function inspectCaches(lock, lockPath, options = {}) {
|
|
|
13108
13282
|
async function inspectContentCache(contentDir, referenced) {
|
|
13109
13283
|
const report = {
|
|
13110
13284
|
path: contentDir,
|
|
13111
|
-
exists: (0,
|
|
13285
|
+
exists: (0, import_node_fs18.existsSync)(contentDir),
|
|
13112
13286
|
files: 0,
|
|
13113
13287
|
bytes: 0,
|
|
13114
13288
|
valid: 0,
|
|
@@ -13129,7 +13303,7 @@ async function inspectContentCache(contentDir, referenced) {
|
|
|
13129
13303
|
continue;
|
|
13130
13304
|
}
|
|
13131
13305
|
report.files++;
|
|
13132
|
-
const file =
|
|
13306
|
+
const file = import_node_path22.default.join(contentDir, entry.name);
|
|
13133
13307
|
const mediaType = mediaTypeFromExtension(entry.name);
|
|
13134
13308
|
const expected = mediaType ? entry.name.slice(0, -4) : "";
|
|
13135
13309
|
let bytes;
|
|
@@ -13172,7 +13346,7 @@ async function inspectContentCache(contentDir, referenced) {
|
|
|
13172
13346
|
async function inspectRemoteHashCache(remotePath) {
|
|
13173
13347
|
const report = {
|
|
13174
13348
|
path: remotePath,
|
|
13175
|
-
exists: (0,
|
|
13349
|
+
exists: (0, import_node_fs18.existsSync)(remotePath),
|
|
13176
13350
|
entries: 0,
|
|
13177
13351
|
valid: 0,
|
|
13178
13352
|
invalidIds: [],
|
|
@@ -13196,20 +13370,20 @@ async function inspectRemoteHashCache(remotePath) {
|
|
|
13196
13370
|
}
|
|
13197
13371
|
|
|
13198
13372
|
// src/pipeline/pack.ts
|
|
13199
|
-
var
|
|
13200
|
-
var
|
|
13373
|
+
var import_node_fs19 = require("fs");
|
|
13374
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
13201
13375
|
var DEFAULT_FRAME_SET_FPS = 12;
|
|
13202
13376
|
function resolvePackInputs(raw, inputsFilePath) {
|
|
13203
13377
|
if (!Array.isArray(raw) || !raw.length) {
|
|
13204
13378
|
throw new Error("--inputs must be a non-empty JSON array of { id, path }");
|
|
13205
13379
|
}
|
|
13206
|
-
const baseDir =
|
|
13380
|
+
const baseDir = import_node_path23.default.dirname(import_node_path23.default.resolve(inputsFilePath));
|
|
13207
13381
|
return raw.map((entry, i) => {
|
|
13208
13382
|
const e = entry;
|
|
13209
13383
|
if (typeof e.id !== "string" || typeof e.path !== "string") {
|
|
13210
13384
|
throw new Error(`--inputs[${i}] needs string "id" and "path"`);
|
|
13211
13385
|
}
|
|
13212
|
-
return { id: e.id, path:
|
|
13386
|
+
return { id: e.id, path: import_node_path23.default.resolve(baseDir, e.path) };
|
|
13213
13387
|
});
|
|
13214
13388
|
}
|
|
13215
13389
|
function packSprites(inputs, options = {}) {
|
|
@@ -13230,7 +13404,7 @@ function packSprites(inputs, options = {}) {
|
|
|
13230
13404
|
for (const input of ordered) {
|
|
13231
13405
|
let sourceHash = null;
|
|
13232
13406
|
try {
|
|
13233
|
-
const bytes = (0,
|
|
13407
|
+
const bytes = (0, import_node_fs19.readFileSync)(input.path);
|
|
13234
13408
|
sourceHash = sha256(bytes);
|
|
13235
13409
|
const png = decodePng(bytes);
|
|
13236
13410
|
sprites.push({ id: input.id, width: png.width, height: png.height, pixels: png.pixels });
|
|
@@ -13293,7 +13467,7 @@ function packStyle(lock, styleId, manifestDir, options = {}) {
|
|
|
13293
13467
|
}
|
|
13294
13468
|
const inputs2 = Object.entries(options.sourceOverrides).map(([id, source]) => ({
|
|
13295
13469
|
id,
|
|
13296
|
-
path:
|
|
13470
|
+
path: import_node_path23.default.resolve(source)
|
|
13297
13471
|
}));
|
|
13298
13472
|
if (!inputs2.length) throw new Error(`No approved quality outputs for style "${styleId}".`);
|
|
13299
13473
|
const packed2 = packSprites(inputs2, options);
|
|
@@ -13317,7 +13491,7 @@ function packStyle(lock, styleId, manifestDir, options = {}) {
|
|
|
13317
13491
|
let outputs = resolveEntryOutputs(entry, id, manifestDir);
|
|
13318
13492
|
const source = sources[id];
|
|
13319
13493
|
if (source && outputs.length <= 1) {
|
|
13320
|
-
inputs.push({ id, path:
|
|
13494
|
+
inputs.push({ id, path: import_node_path23.default.resolve(manifestDir, source) });
|
|
13321
13495
|
continue;
|
|
13322
13496
|
}
|
|
13323
13497
|
if (source && sourceIsStem(source, entry, manifestDir)) {
|
|
@@ -13351,7 +13525,7 @@ function packStyle(lock, styleId, manifestDir, options = {}) {
|
|
|
13351
13525
|
}
|
|
13352
13526
|
}
|
|
13353
13527
|
for (const [id, source] of Object.entries(sources)) {
|
|
13354
|
-
if (!locked.has(id)) inputs.push({ id, path:
|
|
13528
|
+
if (!locked.has(id)) inputs.push({ id, path: import_node_path23.default.resolve(manifestDir, source) });
|
|
13355
13529
|
}
|
|
13356
13530
|
if (!inputs.length) {
|
|
13357
13531
|
throw new Error(`No readable sprites for style "${styleId}"; ${noOutput.length} skipped.`);
|
|
@@ -13388,7 +13562,7 @@ function mountSprites(placements, options) {
|
|
|
13388
13562
|
for (const p of placements) {
|
|
13389
13563
|
let sourceHash = null;
|
|
13390
13564
|
try {
|
|
13391
|
-
const bytes = (0,
|
|
13565
|
+
const bytes = (0, import_node_fs19.readFileSync)(p.path);
|
|
13392
13566
|
sourceHash = sha256(bytes);
|
|
13393
13567
|
const png = decodePng(bytes);
|
|
13394
13568
|
if (png.width > cellWidth || png.height > cellHeight) {
|
|
@@ -13459,7 +13633,7 @@ function mountStyle(lock, styleId, manifestDir, mount, cells, sources = {}, outp
|
|
|
13459
13633
|
const source = sources[id];
|
|
13460
13634
|
const entry = lock.entries[`${styleId}/${id}`];
|
|
13461
13635
|
if (source && !(entry && sourceIsStem(source, entry, manifestDir))) {
|
|
13462
|
-
placements.push({ id, path:
|
|
13636
|
+
placements.push({ id, path: import_node_path23.default.resolve(manifestDir, source), cell });
|
|
13463
13637
|
continue;
|
|
13464
13638
|
}
|
|
13465
13639
|
if (source && entry) {
|
|
@@ -13492,7 +13666,7 @@ function mountStyle(lock, styleId, manifestDir, mount, cells, sources = {}, outp
|
|
|
13492
13666
|
`Nothing to mount for style "${styleId}": ${ids.length} asset(s) declare a cell, but none has generated output or a \`source\`. Run \`pixelkiln gen --style ${styleId}\`, or point each at a file.${detail}`
|
|
13493
13667
|
);
|
|
13494
13668
|
}
|
|
13495
|
-
const basePng = mount.base ? (0,
|
|
13669
|
+
const basePng = mount.base ? (0, import_node_fs19.readFileSync)(import_node_path23.default.resolve(manifestDir, mount.base)) : void 0;
|
|
13496
13670
|
const mounted = mountSprites(placements, {
|
|
13497
13671
|
cellWidth: mount.cellWidth,
|
|
13498
13672
|
cellHeight: mount.cellHeight,
|
|
@@ -13502,7 +13676,7 @@ function mountStyle(lock, styleId, manifestDir, mount, cells, sources = {}, outp
|
|
|
13502
13676
|
...mounted,
|
|
13503
13677
|
sources: mount.base ? [{
|
|
13504
13678
|
id: "$base",
|
|
13505
|
-
path:
|
|
13679
|
+
path: import_node_path23.default.resolve(manifestDir, mount.base),
|
|
13506
13680
|
sha256: sha256(basePng),
|
|
13507
13681
|
included: true
|
|
13508
13682
|
}, ...mounted.sources] : mounted.sources,
|
|
@@ -14079,6 +14253,7 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
14079
14253
|
revision: spec.revision ? {
|
|
14080
14254
|
mode: spec.revision.mode,
|
|
14081
14255
|
sourceAssetId: spec.revision.sourceAssetId,
|
|
14256
|
+
...spec.revision.sourceStyleId ? { sourceStyleId: spec.revision.sourceStyleId } : {},
|
|
14082
14257
|
sourceSha256: spec.revision.sourceSha256,
|
|
14083
14258
|
...spec.revision.maskSha256 ? { maskSha256: spec.revision.maskSha256 } : {},
|
|
14084
14259
|
...spec.revision.strength == null ? {} : { strength: spec.revision.strength },
|
|
@@ -14312,9 +14487,9 @@ async function poll(provider, lock, lockPath, opts = {}) {
|
|
|
14312
14487
|
}
|
|
14313
14488
|
|
|
14314
14489
|
// src/pipeline/doctor.ts
|
|
14315
|
-
var
|
|
14490
|
+
var import_node_fs20 = require("fs");
|
|
14316
14491
|
var import_promises14 = require("fs/promises");
|
|
14317
|
-
var
|
|
14492
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
14318
14493
|
async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
14319
14494
|
const checks = [];
|
|
14320
14495
|
const add = (id, level, message6) => checks.push({ id, level, message: message6 });
|
|
@@ -14325,14 +14500,14 @@ async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
|
14325
14500
|
);
|
|
14326
14501
|
add("lock", "ok", `${Object.keys(lock.entries).length} valid v2 lock entr(ies)`);
|
|
14327
14502
|
const dirs = [...new Set(specs.flatMap((spec) => [
|
|
14328
|
-
|
|
14329
|
-
...spec.quality ? [
|
|
14503
|
+
import_node_path24.default.dirname(spec.outFile),
|
|
14504
|
+
...spec.quality ? [import_node_path24.default.dirname(spec.quality.outFile)] : []
|
|
14330
14505
|
]))];
|
|
14331
14506
|
const unwritable = [];
|
|
14332
14507
|
for (const dir of dirs) {
|
|
14333
14508
|
const ancestor = await nearestExistingDirectory(dir);
|
|
14334
14509
|
try {
|
|
14335
|
-
await (0, import_promises14.access)(ancestor,
|
|
14510
|
+
await (0, import_promises14.access)(ancestor, import_node_fs20.constants.W_OK);
|
|
14336
14511
|
} catch {
|
|
14337
14512
|
unwritable.push(dir);
|
|
14338
14513
|
}
|
|
@@ -14349,7 +14524,7 @@ async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
|
14349
14524
|
for (let index = 0; index < entry.outputs.length; index++) {
|
|
14350
14525
|
const output = entry.outputs[index];
|
|
14351
14526
|
const spec = specByKey.get(key);
|
|
14352
|
-
const absolute = spec ? currentEntryOutputPath(entry, spec, index) : resolveOutputPath(output.path,
|
|
14527
|
+
const absolute = spec ? currentEntryOutputPath(entry, spec, index) : resolveOutputPath(output.path, import_node_path24.default.dirname(lockPath));
|
|
14353
14528
|
const owner = outputOwners.get(absolute);
|
|
14354
14529
|
if (owner && owner !== key) duplicateOutputs.push(`${output.path} (${owner}, ${key})`);
|
|
14355
14530
|
outputOwners.set(absolute, key);
|
|
@@ -14364,7 +14539,7 @@ async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
|
14364
14539
|
} else {
|
|
14365
14540
|
add("lock-outputs", "ok", `${outputOwners.size} recorded output path(s) are uniquely owned`);
|
|
14366
14541
|
}
|
|
14367
|
-
const cacheDir =
|
|
14542
|
+
const cacheDir = import_node_path24.default.join(import_node_path24.default.dirname(lockPath), ".pixelkiln", "cache");
|
|
14368
14543
|
const stranded = [];
|
|
14369
14544
|
const invalidState = [];
|
|
14370
14545
|
const staleMapUrls = [];
|
|
@@ -14380,11 +14555,11 @@ async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
|
14380
14555
|
const hasSource = Boolean(entry.sourceUrl || entry.sourceUrls?.length);
|
|
14381
14556
|
let hasCache = false;
|
|
14382
14557
|
for (const output of entry.outputs) {
|
|
14383
|
-
const cached =
|
|
14558
|
+
const cached = import_node_path24.default.join(
|
|
14384
14559
|
cacheDir,
|
|
14385
14560
|
cacheFileName(output.sha256, output.mediaType ?? MediaType.PNG)
|
|
14386
14561
|
);
|
|
14387
|
-
if ((0,
|
|
14562
|
+
if ((0, import_node_fs20.existsSync)(cached) && await sha256File(cached) === output.sha256) {
|
|
14388
14563
|
hasCache = true;
|
|
14389
14564
|
break;
|
|
14390
14565
|
}
|
|
@@ -14480,16 +14655,16 @@ async function nearestExistingDirectory(start) {
|
|
|
14480
14655
|
if ((await (0, import_promises14.stat)(current)).isDirectory()) return current;
|
|
14481
14656
|
} catch {
|
|
14482
14657
|
}
|
|
14483
|
-
const parent =
|
|
14658
|
+
const parent = import_node_path24.default.dirname(current);
|
|
14484
14659
|
if (parent === current) return current;
|
|
14485
14660
|
current = parent;
|
|
14486
14661
|
}
|
|
14487
14662
|
}
|
|
14488
14663
|
|
|
14489
14664
|
// src/pipeline/adopt.ts
|
|
14490
|
-
var
|
|
14665
|
+
var import_node_fs21 = require("fs");
|
|
14491
14666
|
var import_promises15 = require("fs/promises");
|
|
14492
|
-
var
|
|
14667
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
14493
14668
|
async function adopt(provider, specs, lock, lockPath, opts = {}) {
|
|
14494
14669
|
const log = opts.onProgress ?? (() => {
|
|
14495
14670
|
});
|
|
@@ -14500,7 +14675,7 @@ async function adopt(provider, specs, lock, lockPath, opts = {}) {
|
|
|
14500
14675
|
const localByHash = /* @__PURE__ */ new Map();
|
|
14501
14676
|
const unmatchedLocal = [];
|
|
14502
14677
|
for (const spec of specs) {
|
|
14503
|
-
if (!(0,
|
|
14678
|
+
if (!(0, import_node_fs21.existsSync)(spec.outFile)) {
|
|
14504
14679
|
unmatchedLocal.push(`${spec.styleId}/${spec.assetId} (no file at ${spec.outFile})`);
|
|
14505
14680
|
continue;
|
|
14506
14681
|
}
|
|
@@ -14622,13 +14797,13 @@ async function tagAdopted(provider, specs, lock, opts = {}) {
|
|
|
14622
14797
|
}
|
|
14623
14798
|
|
|
14624
14799
|
// src/pick/server.ts
|
|
14625
|
-
var
|
|
14800
|
+
var import_node_fs23 = require("fs");
|
|
14626
14801
|
var import_promises18 = require("fs/promises");
|
|
14627
14802
|
|
|
14628
14803
|
// src/pipeline/init.ts
|
|
14629
14804
|
var import_promises16 = require("fs/promises");
|
|
14630
|
-
var
|
|
14631
|
-
var
|
|
14805
|
+
var import_node_fs22 = require("fs");
|
|
14806
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
14632
14807
|
function pngSize(buf) {
|
|
14633
14808
|
if (buf.length < 24) return null;
|
|
14634
14809
|
const isPng = buf.readUInt32BE(0) === 2303741511 && buf.readUInt32BE(4) === 218765834;
|
|
@@ -14642,7 +14817,7 @@ function slugify(value) {
|
|
|
14642
14817
|
async function walk(dir, exclude) {
|
|
14643
14818
|
const out = [];
|
|
14644
14819
|
for (const entry of await (0, import_promises16.readdir)(dir, { withFileTypes: true })) {
|
|
14645
|
-
const full =
|
|
14820
|
+
const full = import_node_path26.default.join(dir, entry.name);
|
|
14646
14821
|
if (exclude.some((x) => full.includes(x))) continue;
|
|
14647
14822
|
if (entry.isDirectory()) out.push(...await walk(full, exclude));
|
|
14648
14823
|
else if (entry.name.toLowerCase().endsWith(".png")) out.push(full);
|
|
@@ -14661,9 +14836,9 @@ async function scanAssets(root, opts = {}) {
|
|
|
14661
14836
|
skipped.push(`${file} (not a readable PNG)`);
|
|
14662
14837
|
continue;
|
|
14663
14838
|
}
|
|
14664
|
-
const rel =
|
|
14665
|
-
const category =
|
|
14666
|
-
let id = slugify(
|
|
14839
|
+
const rel = import_node_path26.default.relative(root, file);
|
|
14840
|
+
const category = import_node_path26.default.dirname(rel) === "." ? "" : import_node_path26.default.dirname(rel);
|
|
14841
|
+
let id = slugify(import_node_path26.default.basename(rel));
|
|
14667
14842
|
if (seen.has(id)) {
|
|
14668
14843
|
const n = seen.get(id) + 1;
|
|
14669
14844
|
seen.set(id, n);
|
|
@@ -15212,7 +15387,7 @@ async function currentArt(entry, spec) {
|
|
|
15212
15387
|
const previous = primaryOutput({ ...entry, outputs: entry.supersededOutputs });
|
|
15213
15388
|
if (!previous) return null;
|
|
15214
15389
|
const file = resolveOutputPath(previous.path, spec.root);
|
|
15215
|
-
if (!(0,
|
|
15390
|
+
if (!(0, import_node_fs23.existsSync)(file)) return null;
|
|
15216
15391
|
const contentType = previous.mediaType ?? mediaTypeFromExtension(file);
|
|
15217
15392
|
if (!contentType) return null;
|
|
15218
15393
|
let width = entry.width;
|
|
@@ -15386,15 +15561,15 @@ async function runPicker(provider, lock, lockPath, opts = {}) {
|
|
|
15386
15561
|
}
|
|
15387
15562
|
|
|
15388
15563
|
// src/gallery/snapshot.ts
|
|
15389
|
-
var
|
|
15390
|
-
var
|
|
15564
|
+
var import_node_crypto7 = require("crypto");
|
|
15565
|
+
var import_node_fs29 = require("fs");
|
|
15391
15566
|
var import_promises26 = require("fs/promises");
|
|
15392
|
-
var
|
|
15567
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
15393
15568
|
|
|
15394
15569
|
// src/workspace.ts
|
|
15395
15570
|
var import_promises19 = require("fs/promises");
|
|
15396
|
-
var
|
|
15397
|
-
var
|
|
15571
|
+
var import_node_fs24 = require("fs");
|
|
15572
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
15398
15573
|
var import_zod7 = require("zod");
|
|
15399
15574
|
var WorkspaceProjectSchema = import_zod7.z.object({
|
|
15400
15575
|
id: import_zod7.z.string().min(1),
|
|
@@ -15419,7 +15594,7 @@ ${parsed.error.issues.slice(0, 5).map((i) => ` ${i.path.join(".")}: ${i.message
|
|
|
15419
15594
|
);
|
|
15420
15595
|
}
|
|
15421
15596
|
async function loadWorkspace(workspacePath) {
|
|
15422
|
-
if (!(0,
|
|
15597
|
+
if (!(0, import_node_fs24.existsSync)(workspacePath)) return { version: 1, projects: [] };
|
|
15423
15598
|
let raw;
|
|
15424
15599
|
try {
|
|
15425
15600
|
raw = JSON.parse(await (0, import_promises19.readFile)(workspacePath, "utf8"));
|
|
@@ -15436,7 +15611,7 @@ async function saveWorkspace(workspacePath, ws) {
|
|
|
15436
15611
|
version: 1,
|
|
15437
15612
|
projects: [...ws.projects].sort((a, b) => a.id.localeCompare(b.id))
|
|
15438
15613
|
};
|
|
15439
|
-
await (0, import_promises19.mkdir)(
|
|
15614
|
+
await (0, import_promises19.mkdir)(import_node_path27.default.dirname(import_node_path27.default.resolve(workspacePath)), { recursive: true });
|
|
15440
15615
|
const tmp = `${workspacePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
15441
15616
|
try {
|
|
15442
15617
|
await (0, import_promises19.writeFile)(tmp, JSON.stringify(sorted, null, 2) + "\n");
|
|
@@ -15446,12 +15621,12 @@ async function saveWorkspace(workspacePath, ws) {
|
|
|
15446
15621
|
}
|
|
15447
15622
|
}
|
|
15448
15623
|
function toPortablePath(dir, absolute) {
|
|
15449
|
-
return
|
|
15624
|
+
return import_node_path27.default.relative(dir, absolute).split(import_node_path27.default.sep).join("/");
|
|
15450
15625
|
}
|
|
15451
15626
|
function resolveProject(dir, project) {
|
|
15452
15627
|
return {
|
|
15453
|
-
manifestPath:
|
|
15454
|
-
lockPath:
|
|
15628
|
+
manifestPath: import_node_path27.default.resolve(dir, project.manifest.split("/").join(import_node_path27.default.sep)),
|
|
15629
|
+
lockPath: import_node_path27.default.resolve(dir, project.lock.split("/").join(import_node_path27.default.sep))
|
|
15455
15630
|
};
|
|
15456
15631
|
}
|
|
15457
15632
|
function validateWorkspace(ws, dir) {
|
|
@@ -15464,21 +15639,21 @@ function validateWorkspace(ws, dir) {
|
|
|
15464
15639
|
const { manifestPath, lockPath } = resolveProject(dir, project);
|
|
15465
15640
|
lockOwners.set(lockPath, [...lockOwners.get(lockPath) ?? [], project.id]);
|
|
15466
15641
|
manifestOwners.set(manifestPath, [...manifestOwners.get(manifestPath) ?? [], project.id]);
|
|
15467
|
-
if (
|
|
15642
|
+
if (import_node_path27.default.isAbsolute(project.manifest) || import_node_path27.default.isAbsolute(project.lock)) {
|
|
15468
15643
|
diagnostics.push({
|
|
15469
15644
|
id: "absolute-path",
|
|
15470
15645
|
level: "warning",
|
|
15471
15646
|
message: `project "${project.id}" stores an absolute path; the catalog will not resolve correctly if this tree is cloned or moved elsewhere`
|
|
15472
15647
|
});
|
|
15473
15648
|
}
|
|
15474
|
-
if (!(0,
|
|
15649
|
+
if (!(0, import_node_fs24.existsSync)(manifestPath)) {
|
|
15475
15650
|
diagnostics.push({
|
|
15476
15651
|
id: "missing-manifest",
|
|
15477
15652
|
level: "error",
|
|
15478
15653
|
message: `project "${project.id}" manifest not found: ${manifestPath}`
|
|
15479
15654
|
});
|
|
15480
15655
|
}
|
|
15481
|
-
if (!(0,
|
|
15656
|
+
if (!(0, import_node_fs24.existsSync)(lockPath)) {
|
|
15482
15657
|
diagnostics.push({
|
|
15483
15658
|
id: "missing-lock",
|
|
15484
15659
|
level: "error",
|
|
@@ -15526,15 +15701,15 @@ function validateWorkspace(ws, dir) {
|
|
|
15526
15701
|
|
|
15527
15702
|
// src/gallery/edit.ts
|
|
15528
15703
|
var import_promises25 = require("fs/promises");
|
|
15529
|
-
var
|
|
15704
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
15530
15705
|
var import_zod13 = require("zod");
|
|
15531
15706
|
|
|
15532
15707
|
// src/manifest-edit.ts
|
|
15533
15708
|
var import_promises20 = require("fs/promises");
|
|
15534
|
-
var
|
|
15709
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
15535
15710
|
var import_zod8 = require("zod");
|
|
15536
15711
|
var HexSha = import_zod8.z.string().regex(/^[0-9a-f]{64}$/);
|
|
15537
|
-
var ProjectFileSchema = import_zod8.z.string().min(1).refine((file) => !
|
|
15712
|
+
var ProjectFileSchema = import_zod8.z.string().min(1).refine((file) => !import_node_path28.default.isAbsolute(file) && !file.split(/[\\/]/).includes(".."), "must be a path inside the project");
|
|
15538
15713
|
var AssetPatchSchema = import_zod8.z.object({
|
|
15539
15714
|
prompt: import_zod8.z.string().optional(),
|
|
15540
15715
|
/** Per-style override for the style being viewed; null removes it. */
|
|
@@ -15953,7 +16128,7 @@ function applyEdit(raw, edit) {
|
|
|
15953
16128
|
}
|
|
15954
16129
|
}
|
|
15955
16130
|
async function withCandidate(manifestPath, edit, use, unchanged) {
|
|
15956
|
-
const absolute =
|
|
16131
|
+
const absolute = import_node_path28.default.resolve(manifestPath);
|
|
15957
16132
|
const original = await (0, import_promises20.readFile)(absolute, "utf8");
|
|
15958
16133
|
if (sha256(original) !== edit.expectedSha256) throw new ManifestDriftError(absolute);
|
|
15959
16134
|
let raw;
|
|
@@ -15967,9 +16142,9 @@ async function withCandidate(manifestPath, edit, use, unchanged) {
|
|
|
15967
16142
|
applyEdit(raw, edit);
|
|
15968
16143
|
const next = serializeLike(original, raw);
|
|
15969
16144
|
if (next === original) return unchanged(absolute, original);
|
|
15970
|
-
const tmp =
|
|
15971
|
-
|
|
15972
|
-
`.${
|
|
16145
|
+
const tmp = import_node_path28.default.join(
|
|
16146
|
+
import_node_path28.default.dirname(absolute),
|
|
16147
|
+
`.${import_node_path28.default.basename(absolute)}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`
|
|
15973
16148
|
);
|
|
15974
16149
|
try {
|
|
15975
16150
|
await (0, import_promises20.writeFile)(tmp, next);
|
|
@@ -15998,18 +16173,18 @@ async function applyManifestEdit(manifestPath, edit) {
|
|
|
15998
16173
|
}
|
|
15999
16174
|
|
|
16000
16175
|
// src/pipeline/hand-edit.ts
|
|
16001
|
-
var
|
|
16176
|
+
var import_node_fs25 = require("fs");
|
|
16002
16177
|
var import_promises21 = require("fs/promises");
|
|
16003
|
-
var
|
|
16178
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
16004
16179
|
var import_zod9 = require("zod");
|
|
16005
16180
|
var HAND_EDIT_DIR = "edits";
|
|
16006
16181
|
function handEditPath(loaded, spec) {
|
|
16007
16182
|
const style = loaded.manifest.styles[spec.styleId];
|
|
16008
|
-
const outDir =
|
|
16009
|
-
const relative =
|
|
16010
|
-
const inside = relative && !relative.startsWith("..") && !
|
|
16011
|
-
const file = inside ? relative :
|
|
16012
|
-
return
|
|
16183
|
+
const outDir = import_node_path29.default.resolve(loaded.root, style?.outDir ?? ".");
|
|
16184
|
+
const relative = import_node_path29.default.relative(outDir, spec.outFile);
|
|
16185
|
+
const inside = relative && !relative.startsWith("..") && !import_node_path29.default.isAbsolute(relative);
|
|
16186
|
+
const file = inside ? relative : import_node_path29.default.basename(spec.outFile);
|
|
16187
|
+
return import_node_path29.default.join(outDir, HAND_EDIT_DIR, file.replace(/\.gif$/i, ".png"));
|
|
16013
16188
|
}
|
|
16014
16189
|
function handEditBases(spec, lock) {
|
|
16015
16190
|
const entry = lock.entries[lockKey(spec.styleId, spec.assetId)];
|
|
@@ -16022,10 +16197,10 @@ function handEditBases(spec, lock) {
|
|
|
16022
16197
|
}));
|
|
16023
16198
|
if (members.some((member) => member.mediaType && member.mediaType !== "image/png")) return null;
|
|
16024
16199
|
if (entry.outputs.length === 1 && !primaryOutput(entry)) return null;
|
|
16025
|
-
if (!members.every((member) => (0,
|
|
16200
|
+
if (!members.every((member) => (0, import_node_fs25.existsSync)(member.path))) return null;
|
|
16026
16201
|
return members.map(({ role, path: file }) => ({ role, path: file }));
|
|
16027
16202
|
}
|
|
16028
|
-
return (0,
|
|
16203
|
+
return (0, import_node_fs25.existsSync)(spec.outFile) ? [{ role: null, path: spec.outFile }] : null;
|
|
16029
16204
|
}
|
|
16030
16205
|
function handEditBase(spec, lock) {
|
|
16031
16206
|
const bases = handEditBases(spec, lock);
|
|
@@ -16055,18 +16230,18 @@ async function startHandEdit(loaded, lock, spec, opts = {}) {
|
|
|
16055
16230
|
`${spec.styleId}/${spec.assetId} declares ${spec.source}, one committed file placed for the whole set; clear that source (pixelkiln edit detach) to hand-edit the members one by one`
|
|
16056
16231
|
);
|
|
16057
16232
|
}
|
|
16058
|
-
const existing = spec.source ?
|
|
16233
|
+
const existing = spec.source ? import_node_path29.default.resolve(loaded.root, spec.source) : null;
|
|
16059
16234
|
const editPath = existing ?? handEditPath(loaded, spec);
|
|
16060
16235
|
const members = handEditMembers(loaded, spec, lock, editPath);
|
|
16061
16236
|
let created = false;
|
|
16062
16237
|
for (const member of members) {
|
|
16063
|
-
if ((0,
|
|
16238
|
+
if ((0, import_node_fs25.existsSync)(member.path)) continue;
|
|
16064
16239
|
if (!member.base) {
|
|
16065
16240
|
throw new ManifestEditError(
|
|
16066
16241
|
`${spec.styleId}/${spec.assetId} has no generated PNG on disk to start from; generate or restore it first`
|
|
16067
16242
|
);
|
|
16068
16243
|
}
|
|
16069
|
-
await (0, import_promises21.mkdir)(
|
|
16244
|
+
await (0, import_promises21.mkdir)(import_node_path29.default.dirname(member.path), { recursive: true });
|
|
16070
16245
|
await (0, import_promises21.copyFile)(member.base, member.path);
|
|
16071
16246
|
created = true;
|
|
16072
16247
|
}
|
|
@@ -16204,7 +16379,7 @@ async function saveHandEdit(loaded, lock, spec, input) {
|
|
|
16204
16379
|
editor: input.editor,
|
|
16205
16380
|
protocol: input.protocol,
|
|
16206
16381
|
savedAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
16207
|
-
project: projectPath ?
|
|
16382
|
+
project: projectPath ? import_node_path29.default.basename(projectPath) : null,
|
|
16208
16383
|
outputs: incoming.map((frame, index) => ({
|
|
16209
16384
|
role: frame.role,
|
|
16210
16385
|
basedOn: basedOnHash(entry, index),
|
|
@@ -16246,9 +16421,9 @@ function openInEditor(file, editor = process.env.PIXELKILN_EDITOR) {
|
|
|
16246
16421
|
}
|
|
16247
16422
|
|
|
16248
16423
|
// src/gallery/upload.ts
|
|
16249
|
-
var
|
|
16424
|
+
var import_node_fs26 = require("fs");
|
|
16250
16425
|
var import_promises22 = require("fs/promises");
|
|
16251
|
-
var
|
|
16426
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
16252
16427
|
var import_zod10 = require("zod");
|
|
16253
16428
|
var MAX_UPLOAD_BYTES = 8 * 1024 * 1024;
|
|
16254
16429
|
var UploadImageSchema = import_zod10.z.object({
|
|
@@ -16261,12 +16436,12 @@ var UploadImageSchema = import_zod10.z.object({
|
|
|
16261
16436
|
overwrite: import_zod10.z.boolean().optional()
|
|
16262
16437
|
}).strict();
|
|
16263
16438
|
async function saveProjectImage(root, request) {
|
|
16264
|
-
const file =
|
|
16265
|
-
const relative =
|
|
16266
|
-
if (relative.startsWith("..") ||
|
|
16439
|
+
const file = import_node_path30.default.resolve(root, request.path);
|
|
16440
|
+
const relative = import_node_path30.default.relative(root, file);
|
|
16441
|
+
if (relative.startsWith("..") || import_node_path30.default.isAbsolute(relative)) {
|
|
16267
16442
|
throw new ManifestEditError("an upload must stay inside the project");
|
|
16268
16443
|
}
|
|
16269
|
-
const extension =
|
|
16444
|
+
const extension = import_node_path30.default.extname(file).toLowerCase();
|
|
16270
16445
|
const expected = extension === ".png" ? "png" : extension === ".jpg" || extension === ".jpeg" ? "jpeg" : null;
|
|
16271
16446
|
if (!expected) throw new ManifestEditError("an upload must be a .png, .jpg, or .jpeg path");
|
|
16272
16447
|
const bytes = Buffer.from(request.base64, "base64");
|
|
@@ -16278,18 +16453,18 @@ async function saveProjectImage(root, request) {
|
|
|
16278
16453
|
if (metadata.format !== expected) {
|
|
16279
16454
|
throw new ManifestEditError(`the upload is a ${metadata.format.toUpperCase()}; name it ${metadata.format === "png" ? ".png" : ".jpg"}`);
|
|
16280
16455
|
}
|
|
16281
|
-
if ((0,
|
|
16282
|
-
throw new ManifestEditError(`${relative.split(
|
|
16456
|
+
if ((0, import_node_fs26.existsSync)(file) && !request.overwrite) {
|
|
16457
|
+
throw new ManifestEditError(`${relative.split(import_node_path30.default.sep).join("/")} already exists; choose another name, or replace it`);
|
|
16283
16458
|
}
|
|
16284
|
-
await (0, import_promises22.mkdir)(
|
|
16459
|
+
await (0, import_promises22.mkdir)(import_node_path30.default.dirname(file), { recursive: true });
|
|
16285
16460
|
await (0, import_promises22.writeFile)(file, bytes);
|
|
16286
|
-
return relative.split(
|
|
16461
|
+
return relative.split(import_node_path30.default.sep).join("/");
|
|
16287
16462
|
}
|
|
16288
16463
|
|
|
16289
16464
|
// src/gallery/image-revision.ts
|
|
16290
|
-
var
|
|
16465
|
+
var import_node_fs27 = require("fs");
|
|
16291
16466
|
var import_promises23 = require("fs/promises");
|
|
16292
|
-
var
|
|
16467
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
16293
16468
|
var import_zod11 = require("zod");
|
|
16294
16469
|
var InpaintRevisionRequestSchema = import_zod11.z.object({
|
|
16295
16470
|
action: import_zod11.z.literal("create-inpaint-revision"),
|
|
@@ -16308,15 +16483,15 @@ var InpaintRevisionRequestSchema = import_zod11.z.object({
|
|
|
16308
16483
|
overwrite: import_zod11.z.boolean().optional()
|
|
16309
16484
|
}).strict();
|
|
16310
16485
|
async function createInpaintRevision(manifestPath, request) {
|
|
16311
|
-
const root =
|
|
16312
|
-
if (
|
|
16486
|
+
const root = import_node_path31.default.dirname(import_node_path31.default.resolve(manifestPath));
|
|
16487
|
+
if (import_node_path31.default.extname(request.maskPath).toLowerCase() !== ".png") {
|
|
16313
16488
|
throw new ManifestEditError("the mask must be a .png path inside the project");
|
|
16314
16489
|
}
|
|
16315
16490
|
const bytes = Buffer.from(request.maskBase64, "base64");
|
|
16316
16491
|
const metadata = imageMetadata(bytes);
|
|
16317
16492
|
if (!metadata || metadata.format !== "png") throw new ManifestEditError("the mask is not a readable PNG");
|
|
16318
|
-
const file =
|
|
16319
|
-
const previous = (0,
|
|
16493
|
+
const file = import_node_path31.default.resolve(root, request.maskPath);
|
|
16494
|
+
const previous = (0, import_node_fs27.existsSync)(file) ? await (0, import_promises23.readFile)(file) : null;
|
|
16320
16495
|
const mask = await saveProjectImage(root, {
|
|
16321
16496
|
action: "upload-image",
|
|
16322
16497
|
path: request.maskPath,
|
|
@@ -16343,9 +16518,9 @@ async function createInpaintRevision(manifestPath, request) {
|
|
|
16343
16518
|
}
|
|
16344
16519
|
|
|
16345
16520
|
// src/gallery/skeleton.ts
|
|
16346
|
-
var
|
|
16521
|
+
var import_node_fs28 = require("fs");
|
|
16347
16522
|
var import_promises24 = require("fs/promises");
|
|
16348
|
-
var
|
|
16523
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
16349
16524
|
var import_zod12 = require("zod");
|
|
16350
16525
|
var SkeletonEstimateRequestSchema = import_zod12.z.object({
|
|
16351
16526
|
project: import_zod12.z.string().min(1).optional(),
|
|
@@ -16383,20 +16558,20 @@ var SkeletonKeypointsUpdateSchema = import_zod12.z.object({
|
|
|
16383
16558
|
set: SkeletonSetSchema
|
|
16384
16559
|
}).strict();
|
|
16385
16560
|
async function createSkeletonAnimation(manifestPath, request) {
|
|
16386
|
-
const root =
|
|
16387
|
-
const file =
|
|
16388
|
-
const relative =
|
|
16389
|
-
if (!request.keypointsFile.endsWith(".json") || relative.startsWith("..") ||
|
|
16561
|
+
const root = import_node_path32.default.dirname(import_node_path32.default.resolve(manifestPath));
|
|
16562
|
+
const file = import_node_path32.default.resolve(root, request.keypointsFile);
|
|
16563
|
+
const relative = import_node_path32.default.relative(root, file);
|
|
16564
|
+
if (!request.keypointsFile.endsWith(".json") || relative.startsWith("..") || import_node_path32.default.isAbsolute(relative)) {
|
|
16390
16565
|
throw new ManifestEditError("the keypoints file must be a .json path inside the project");
|
|
16391
16566
|
}
|
|
16392
|
-
const existed = (0,
|
|
16567
|
+
const existed = (0, import_node_fs28.existsSync)(file);
|
|
16393
16568
|
let previous = null;
|
|
16394
16569
|
if (request.set) {
|
|
16395
16570
|
if (existed && !request.overwrite) {
|
|
16396
16571
|
throw new ManifestEditError(`${request.keypointsFile} already exists; choose another path, or replace it`);
|
|
16397
16572
|
}
|
|
16398
16573
|
if (existed) previous = await (0, import_promises24.readFile)(file);
|
|
16399
|
-
await (0, import_promises24.mkdir)(
|
|
16574
|
+
await (0, import_promises24.mkdir)(import_node_path32.default.dirname(file), { recursive: true });
|
|
16400
16575
|
await (0, import_promises24.writeFile)(file, JSON.stringify(request.set, null, 2) + "\n");
|
|
16401
16576
|
} else if (!existed) {
|
|
16402
16577
|
throw new ManifestEditError(`${request.keypointsFile} does not exist; paste poses or estimate them from the sprite`);
|
|
@@ -16412,7 +16587,7 @@ async function createSkeletonAnimation(manifestPath, request) {
|
|
|
16412
16587
|
revision: {
|
|
16413
16588
|
mode: "animate-skeleton",
|
|
16414
16589
|
from: request.from,
|
|
16415
|
-
keypointsFile: relative.split(
|
|
16590
|
+
keypointsFile: relative.split(import_node_path32.default.sep).join("/"),
|
|
16416
16591
|
direction: request.direction,
|
|
16417
16592
|
...request.description ? { description: request.description } : {},
|
|
16418
16593
|
...request.skeletonTemplate ? { skeletonTemplate: request.skeletonTemplate } : {}
|
|
@@ -16433,19 +16608,19 @@ async function updateSkeletonKeypoints(ctx, request) {
|
|
|
16433
16608
|
if (!spec) throw new ManifestEditError(`"${key}" is not declared by the manifest`);
|
|
16434
16609
|
const file = spec.revision?.mode === "animate-skeleton" ? spec.revision.keypointsFile : void 0;
|
|
16435
16610
|
if (!file) throw new ManifestEditError(`${key} is not an animate-skeleton revision`);
|
|
16436
|
-
const relative =
|
|
16437
|
-
if (relative.startsWith("..") ||
|
|
16611
|
+
const relative = import_node_path32.default.relative(ctx.loaded.root, file);
|
|
16612
|
+
if (relative.startsWith("..") || import_node_path32.default.isAbsolute(relative)) {
|
|
16438
16613
|
throw new ManifestEditError(`${key}'s keypoints file is outside the project; edit it by hand`);
|
|
16439
16614
|
}
|
|
16440
|
-
const current = (0,
|
|
16615
|
+
const current = (0, import_node_fs28.existsSync)(file) ? sha256(await (0, import_promises24.readFile)(file)) : null;
|
|
16441
16616
|
if (current !== request.expectedSha256) {
|
|
16442
16617
|
throw Object.assign(
|
|
16443
|
-
new Error(`${relative.split(
|
|
16618
|
+
new Error(`${relative.split(import_node_path32.default.sep).join("/")} changed on disk since this page loaded it; press Refresh and edit again`),
|
|
16444
16619
|
{ status: 409 }
|
|
16445
16620
|
);
|
|
16446
16621
|
}
|
|
16447
16622
|
await (0, import_promises24.writeFile)(file, JSON.stringify(request.set, null, 2) + "\n");
|
|
16448
|
-
return relative.split(
|
|
16623
|
+
return relative.split(import_node_path32.default.sep).join("/");
|
|
16449
16624
|
}
|
|
16450
16625
|
|
|
16451
16626
|
// src/gallery/edit.ts
|
|
@@ -16499,7 +16674,7 @@ function createGalleryEditHandler(opts) {
|
|
|
16499
16674
|
const history = /* @__PURE__ */ new Map();
|
|
16500
16675
|
const manifestOf = async (project) => {
|
|
16501
16676
|
try {
|
|
16502
|
-
return
|
|
16677
|
+
return import_node_path33.default.resolve(await opts.manifestFor(typeof project === "string" ? project : void 0));
|
|
16503
16678
|
} catch {
|
|
16504
16679
|
return null;
|
|
16505
16680
|
}
|
|
@@ -16522,7 +16697,7 @@ function createGalleryEditHandler(opts) {
|
|
|
16522
16697
|
await (0, import_promises25.writeFile)(tmp, last.before);
|
|
16523
16698
|
await (0, import_promises25.rename)(tmp, manifestPath2);
|
|
16524
16699
|
stack.pop();
|
|
16525
|
-
log(` manifest edit undone in ${
|
|
16700
|
+
log(` manifest edit undone in ${import_node_path33.default.relative(process.cwd(), manifestPath2) || manifestPath2}`);
|
|
16526
16701
|
return opts.reload();
|
|
16527
16702
|
}
|
|
16528
16703
|
const manifestPath = await manifestOf(body?.project);
|
|
@@ -16584,7 +16759,7 @@ function createEditApplier(opts, log) {
|
|
|
16584
16759
|
} catch (error) {
|
|
16585
16760
|
throw new ManifestEditError(error instanceof Error ? error.message : String(error));
|
|
16586
16761
|
}
|
|
16587
|
-
const written = await saveProjectImage(
|
|
16762
|
+
const written = await saveProjectImage(import_node_path33.default.dirname(import_node_path33.default.resolve(manifestPath2)), upload.data);
|
|
16588
16763
|
log(` image added: ${written}`);
|
|
16589
16764
|
return opts.reload();
|
|
16590
16765
|
}
|
|
@@ -16651,7 +16826,7 @@ function createEditApplier(opts, log) {
|
|
|
16651
16826
|
}
|
|
16652
16827
|
const result = await applyManifestEdit(manifestPath, parsed.data);
|
|
16653
16828
|
if (result.changed) {
|
|
16654
|
-
const relative =
|
|
16829
|
+
const relative = import_node_path33.default.relative(process.cwd(), result.manifestPath);
|
|
16655
16830
|
const shown = !relative || relative.startsWith("..") ? result.manifestPath : relative;
|
|
16656
16831
|
log(` manifest edited: ${describeEdit(parsed.data)} in ${shown}`);
|
|
16657
16832
|
}
|
|
@@ -16679,7 +16854,7 @@ function describeEdit(edit) {
|
|
|
16679
16854
|
// src/gallery/snapshot.ts
|
|
16680
16855
|
var PROJECT_FILE_TYPE = "application/octet-stream";
|
|
16681
16856
|
function galleryMediaId(absolutePath) {
|
|
16682
|
-
return (0,
|
|
16857
|
+
return (0, import_node_crypto7.createHash)("sha256").update(import_node_path34.default.resolve(absolutePath)).digest("hex").slice(0, 24);
|
|
16683
16858
|
}
|
|
16684
16859
|
function galleryMediaRoute(id) {
|
|
16685
16860
|
return `/media/${id}`;
|
|
@@ -16698,9 +16873,9 @@ function describeHistory(media, entry, cacheDir) {
|
|
|
16698
16873
|
return (entry.history ?? []).map((generation, i) => {
|
|
16699
16874
|
const outputs = generation.outputs.map((output) => {
|
|
16700
16875
|
const mediaType = output.mediaType ?? MediaType.PNG;
|
|
16701
|
-
const file =
|
|
16876
|
+
const file = import_node_path34.default.join(cacheDir, cacheFileName(output.sha256, mediaType));
|
|
16702
16877
|
let url = null;
|
|
16703
|
-
if ((0,
|
|
16878
|
+
if ((0, import_node_fs29.existsSync)(file)) {
|
|
16704
16879
|
const id = galleryMediaId(file);
|
|
16705
16880
|
media.set(id, { path: file, contentType: mediaType });
|
|
16706
16881
|
url = `${galleryMediaRoute(id)}?v=${output.sha256.slice(0, 24)}`;
|
|
@@ -16830,10 +17005,10 @@ async function describeOutput(media, root, absolutePath, recorded = {}) {
|
|
|
16830
17005
|
async function describeQuality(media, root, inspection) {
|
|
16831
17006
|
const outputPaths = inspection.outputs ?? [inspection.output];
|
|
16832
17007
|
const outputs = await Promise.all(
|
|
16833
|
-
outputPaths.map((file) => describeOutput(media, root,
|
|
17008
|
+
outputPaths.map((file) => describeOutput(media, root, import_node_path34.default.resolve(root, file)))
|
|
16834
17009
|
);
|
|
16835
|
-
const recordPath =
|
|
16836
|
-
const recordExists = (0,
|
|
17010
|
+
const recordPath = import_node_path34.default.resolve(root, inspection.record);
|
|
17011
|
+
const recordExists = (0, import_node_fs29.existsSync)(recordPath);
|
|
16837
17012
|
let options = null;
|
|
16838
17013
|
let check = null;
|
|
16839
17014
|
if (recordExists) {
|
|
@@ -16852,7 +17027,7 @@ async function describeQuality(media, root, inspection) {
|
|
|
16852
17027
|
return {
|
|
16853
17028
|
state: inspection.state,
|
|
16854
17029
|
reason: inspection.reason,
|
|
16855
|
-
output: portableOutputPath(
|
|
17030
|
+
output: portableOutputPath(import_node_path34.default.resolve(root, inspection.output), root),
|
|
16856
17031
|
record: portableOutputPath(recordPath, root),
|
|
16857
17032
|
recordExists,
|
|
16858
17033
|
outputs,
|
|
@@ -16892,7 +17067,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
16892
17067
|
const items = [];
|
|
16893
17068
|
const manifestText = await (0, import_promises26.readFile)(loaded.path, "utf8");
|
|
16894
17069
|
const manifestSha256 = sha256(manifestText);
|
|
16895
|
-
const cacheDir =
|
|
17070
|
+
const cacheDir = import_node_path34.default.resolve(import_node_path34.default.dirname(opts.lockPath), ".pixelkiln", "cache");
|
|
16896
17071
|
let rawStyles = {};
|
|
16897
17072
|
try {
|
|
16898
17073
|
const raw = JSON.parse(manifestText);
|
|
@@ -16916,12 +17091,12 @@ async function buildGallerySnapshot(opts) {
|
|
|
16916
17091
|
)
|
|
16917
17092
|
);
|
|
16918
17093
|
if (spec.source) {
|
|
16919
|
-
const editPath =
|
|
17094
|
+
const editPath = import_node_path34.default.resolve(root, spec.source);
|
|
16920
17095
|
const memberCount = sourceIsStem(spec.source, entry, root) ? entry.outputs.length : 1;
|
|
16921
17096
|
const editShas = await Promise.all(
|
|
16922
17097
|
Array.from({ length: memberCount }, async (_, index) => {
|
|
16923
17098
|
const file = sourceOutputPath(spec.source, entry, index, root);
|
|
16924
|
-
return { file, sha256: (0,
|
|
17099
|
+
return { file, sha256: (0, import_node_fs29.existsSync)(file) ? await sha256File(file) : null };
|
|
16925
17100
|
})
|
|
16926
17101
|
);
|
|
16927
17102
|
edits = await Promise.all(editShas.map(({ file, sha256: editSha }) => describeOutput(media, root, file, { sha256: editSha })));
|
|
@@ -16929,7 +17104,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
16929
17104
|
const companion = await readHandEditCompanion(editPath);
|
|
16930
17105
|
if (companion) {
|
|
16931
17106
|
const projectPath = handEditProjectPath(editPath);
|
|
16932
|
-
const project = companion.project && (0,
|
|
17107
|
+
const project = companion.project && (0, import_node_fs29.existsSync)(projectPath) ? await fileInfo(projectPath) : null;
|
|
16933
17108
|
let projectUrl = null;
|
|
16934
17109
|
if (project) {
|
|
16935
17110
|
const id = galleryMediaId(projectPath);
|
|
@@ -16965,8 +17140,8 @@ async function buildGallerySnapshot(opts) {
|
|
|
16965
17140
|
editStatus = edits.some((member) => !member.exists) ? "missing" : editChanged.every((changed) => !changed) ? "same" : regenerated ? "regenerated-since" : "edited";
|
|
16966
17141
|
}
|
|
16967
17142
|
} else if (spec.source) {
|
|
16968
|
-
outputs = [await describeOutput(media, root,
|
|
16969
|
-
} else if (!entry && (0,
|
|
17143
|
+
outputs = [await describeOutput(media, root, import_node_path34.default.resolve(root, spec.source))];
|
|
17144
|
+
} else if (!entry && (0, import_node_fs29.existsSync)(spec.outFile)) {
|
|
16970
17145
|
outputs = [await describeOutput(media, root, spec.outFile)];
|
|
16971
17146
|
} else {
|
|
16972
17147
|
outputs = [];
|
|
@@ -17006,7 +17181,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
17006
17181
|
recordedSpecHash: entry?.specHash ?? null,
|
|
17007
17182
|
currentSpecHash: spec.specHash,
|
|
17008
17183
|
revision: entry?.revision ?? null,
|
|
17009
|
-
revisionParentKey: spec.revision ? lockKey(spec.styleId, spec.revision.sourceAssetId) : entry?.revision ? lockKey(spec.styleId, entry.revision.sourceAssetId) : null,
|
|
17184
|
+
revisionParentKey: spec.revision ? lockKey(spec.revision.sourceStyleId ?? spec.styleId, spec.revision.sourceAssetId) : entry?.revision ? lockKey(entry.revision.sourceStyleId ?? spec.styleId, entry.revision.sourceAssetId) : null,
|
|
17010
17185
|
mirrorOfKey: spec.mirror ? lockKey(spec.styleId, spec.mirror.sourceAssetId) : entry?.mirror ? lockKey(spec.styleId, entry.mirror.sourceAssetId) : null,
|
|
17011
17186
|
skeleton: spec.revision?.mode === "animate-skeleton" && spec.revision.keypointsFile ? {
|
|
17012
17187
|
keypointsFile: portableOutputPath(spec.revision.keypointsFile, root),
|
|
@@ -17076,7 +17251,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
17076
17251
|
recordedSpecHash: entry.specHash,
|
|
17077
17252
|
currentSpecHash: null,
|
|
17078
17253
|
revision: entry.revision,
|
|
17079
|
-
revisionParentKey: entry.revision ? lockKey(entry.styleId, entry.revision.sourceAssetId) : null,
|
|
17254
|
+
revisionParentKey: entry.revision ? lockKey(entry.revision.sourceStyleId ?? entry.styleId, entry.revision.sourceAssetId) : null,
|
|
17080
17255
|
mirrorOfKey: entry.mirror ? lockKey(entry.styleId, entry.mirror.sourceAssetId) : null,
|
|
17081
17256
|
skeleton: null,
|
|
17082
17257
|
character: describeCharacter(void 0, entry),
|
|
@@ -17169,7 +17344,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
17169
17344
|
id: loaded.manifest.name,
|
|
17170
17345
|
name: loaded.manifest.name,
|
|
17171
17346
|
manifest: loaded.path,
|
|
17172
|
-
lock:
|
|
17347
|
+
lock: import_node_path34.default.resolve(opts.lockPath),
|
|
17173
17348
|
root,
|
|
17174
17349
|
provider: loaded.manifest.provider,
|
|
17175
17350
|
account: null,
|
|
@@ -17195,7 +17370,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
17195
17370
|
return { snapshot, media };
|
|
17196
17371
|
}
|
|
17197
17372
|
async function buildWorkspaceGallerySnapshot(opts) {
|
|
17198
|
-
const dir =
|
|
17373
|
+
const dir = import_node_path34.default.dirname(import_node_path34.default.resolve(opts.workspacePath));
|
|
17199
17374
|
const media = /* @__PURE__ */ new Map();
|
|
17200
17375
|
const projects = [];
|
|
17201
17376
|
const items = [];
|
|
@@ -17238,7 +17413,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
17238
17413
|
id: project.id,
|
|
17239
17414
|
name: loaded.manifest.name,
|
|
17240
17415
|
manifest: manifestPath,
|
|
17241
|
-
lock:
|
|
17416
|
+
lock: import_node_path34.default.resolve(lockPath),
|
|
17242
17417
|
root: loaded.root,
|
|
17243
17418
|
provider: loaded.manifest.provider,
|
|
17244
17419
|
account: project.account ?? null,
|
|
@@ -17254,8 +17429,8 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
17254
17429
|
id: project.id,
|
|
17255
17430
|
name: project.id,
|
|
17256
17431
|
manifest: manifestPath,
|
|
17257
|
-
lock:
|
|
17258
|
-
root:
|
|
17432
|
+
lock: import_node_path34.default.resolve(lockPath),
|
|
17433
|
+
root: import_node_path34.default.dirname(manifestPath),
|
|
17259
17434
|
provider: project.provider,
|
|
17260
17435
|
account: project.account ?? null,
|
|
17261
17436
|
manifestSha256: null,
|
|
@@ -17282,7 +17457,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
17282
17457
|
version: 1,
|
|
17283
17458
|
generatedAt: (opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
|
|
17284
17459
|
project: null,
|
|
17285
|
-
workspace: { path:
|
|
17460
|
+
workspace: { path: import_node_path34.default.resolve(opts.workspacePath), dir, projects },
|
|
17286
17461
|
filter,
|
|
17287
17462
|
totals: {
|
|
17288
17463
|
entries: projects.reduce((sum, project) => sum + project.entries, 0),
|
|
@@ -17299,7 +17474,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
17299
17474
|
}
|
|
17300
17475
|
|
|
17301
17476
|
// src/gallery/page.ts
|
|
17302
|
-
var
|
|
17477
|
+
var import_node_fs30 = require("fs");
|
|
17303
17478
|
|
|
17304
17479
|
// src/gallery/client-bundle.ts
|
|
17305
17480
|
var import_node_module = require("module");
|
|
@@ -17339,8 +17514,8 @@ var client;
|
|
|
17339
17514
|
function clientAssets() {
|
|
17340
17515
|
const prebuilt = new URL("gallery.js", CLIENT_DIR);
|
|
17341
17516
|
client ??= {
|
|
17342
|
-
css: (0,
|
|
17343
|
-
js: (0,
|
|
17517
|
+
css: (0, import_node_fs30.readFileSync)(new URL("gallery.css", CLIENT_DIR), "utf8"),
|
|
17518
|
+
js: (0, import_node_fs30.existsSync)(prebuilt) ? (0, import_node_fs30.readFileSync)(prebuilt, "utf8") : bundleGalleryClient()
|
|
17344
17519
|
};
|
|
17345
17520
|
return client;
|
|
17346
17521
|
}
|
|
@@ -17482,11 +17657,11 @@ var EDITOR_PIN = pin_default;
|
|
|
17482
17657
|
var EDITOR_RELEASE_BASE = "https://github.com/gfargo/pixelkiln/releases/download";
|
|
17483
17658
|
|
|
17484
17659
|
// src/editor/install.ts
|
|
17485
|
-
var
|
|
17486
|
-
var
|
|
17660
|
+
var import_node_crypto8 = require("crypto");
|
|
17661
|
+
var import_node_fs31 = require("fs");
|
|
17487
17662
|
var import_promises27 = require("fs/promises");
|
|
17488
|
-
var
|
|
17489
|
-
var
|
|
17663
|
+
var import_node_os4 = __toESM(require("os"), 1);
|
|
17664
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
17490
17665
|
var EditorInstallError = class extends Error {
|
|
17491
17666
|
constructor(message6, file) {
|
|
17492
17667
|
super(message6);
|
|
@@ -17497,15 +17672,15 @@ var EditorInstallError = class extends Error {
|
|
|
17497
17672
|
};
|
|
17498
17673
|
function editorToolsRoot() {
|
|
17499
17674
|
const override = process.env.PIXELKILN_TOOLS_DIR?.trim();
|
|
17500
|
-
if (override) return
|
|
17501
|
-
if (process.platform === "darwin") return
|
|
17675
|
+
if (override) return import_node_path35.default.resolve(override);
|
|
17676
|
+
if (process.platform === "darwin") return import_node_path35.default.join(import_node_os4.default.homedir(), "Library", "Caches", "pixelkiln", "tools");
|
|
17502
17677
|
if (process.platform === "win32") {
|
|
17503
|
-
return
|
|
17678
|
+
return import_node_path35.default.join(process.env.LOCALAPPDATA ?? import_node_path35.default.join(import_node_os4.default.homedir(), "AppData", "Local"), "pixelkiln", "tools");
|
|
17504
17679
|
}
|
|
17505
|
-
return
|
|
17680
|
+
return import_node_path35.default.join(process.env.XDG_CACHE_HOME ?? import_node_path35.default.join(import_node_os4.default.homedir(), ".cache"), "pixelkiln", "tools");
|
|
17506
17681
|
}
|
|
17507
17682
|
function editorDir(pin = EDITOR_PIN, root = editorToolsRoot()) {
|
|
17508
|
-
return
|
|
17683
|
+
return import_node_path35.default.join(root, "pixelorama", pin.release ?? "unpublished");
|
|
17509
17684
|
}
|
|
17510
17685
|
function editorBaseUrl(pin = EDITOR_PIN) {
|
|
17511
17686
|
const override = process.env.PIXELKILN_EDITOR_URL?.trim();
|
|
@@ -17515,7 +17690,7 @@ function editorBaseUrl(pin = EDITOR_PIN) {
|
|
|
17515
17690
|
}
|
|
17516
17691
|
async function sha256Of(file) {
|
|
17517
17692
|
try {
|
|
17518
|
-
return (0,
|
|
17693
|
+
return (0, import_node_crypto8.createHash)("sha256").update(await (0, import_promises27.readFile)(file)).digest("hex");
|
|
17519
17694
|
} catch {
|
|
17520
17695
|
return null;
|
|
17521
17696
|
}
|
|
@@ -17528,8 +17703,8 @@ async function editorStatus(opts = {}) {
|
|
|
17528
17703
|
let totalBytes = 0;
|
|
17529
17704
|
for (const [name, expected] of Object.entries(pin.files)) {
|
|
17530
17705
|
totalBytes += expected.bytes;
|
|
17531
|
-
const file =
|
|
17532
|
-
const info = (0,
|
|
17706
|
+
const file = import_node_path35.default.join(dir, name);
|
|
17707
|
+
const info = (0, import_node_fs31.existsSync)(file) ? await (0, import_promises27.stat)(file).catch(() => null) : null;
|
|
17533
17708
|
if (info && info.size === expected.bytes && await sha256Of(file) === expected.sha256) {
|
|
17534
17709
|
installedBytes += expected.bytes;
|
|
17535
17710
|
} else {
|
|
@@ -17582,11 +17757,11 @@ async function installEditor(opts = {}) {
|
|
|
17582
17757
|
if (bytes.length !== expected.bytes) {
|
|
17583
17758
|
throw new EditorInstallError(`${name}: expected ${expected.bytes} bytes, got ${bytes.length}`, name);
|
|
17584
17759
|
}
|
|
17585
|
-
const digest2 = (0,
|
|
17760
|
+
const digest2 = (0, import_node_crypto8.createHash)("sha256").update(bytes).digest("hex");
|
|
17586
17761
|
if (digest2 !== expected.sha256) {
|
|
17587
17762
|
throw new EditorInstallError(`${name}: sha256 ${digest2.slice(0, 12)}\u2026 does not match the pinned ${expected.sha256.slice(0, 12)}\u2026`, name);
|
|
17588
17763
|
}
|
|
17589
|
-
const final =
|
|
17764
|
+
const final = import_node_path35.default.join(dir, name);
|
|
17590
17765
|
const tmp = `${final}.${process.pid}.part`;
|
|
17591
17766
|
try {
|
|
17592
17767
|
await (0, import_promises27.writeFile)(tmp, bytes);
|
|
@@ -17632,12 +17807,12 @@ var EDITOR_CONTENT_TYPES = {
|
|
|
17632
17807
|
".json": "application/json; charset=utf-8"
|
|
17633
17808
|
};
|
|
17634
17809
|
function editorContentType(name) {
|
|
17635
|
-
return EDITOR_CONTENT_TYPES[
|
|
17810
|
+
return EDITOR_CONTENT_TYPES[import_node_path35.default.extname(name).toLowerCase()] ?? null;
|
|
17636
17811
|
}
|
|
17637
17812
|
|
|
17638
17813
|
// src/gallery/editor.ts
|
|
17639
17814
|
var import_promises28 = require("fs/promises");
|
|
17640
|
-
var
|
|
17815
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
17641
17816
|
function editorRoute(release, name = "index.html") {
|
|
17642
17817
|
return `/editor/${encodeURIComponent(release)}/${encodeURIComponent(name)}`;
|
|
17643
17818
|
}
|
|
@@ -17710,7 +17885,7 @@ function createGalleryEditorHandlers(opts = {}) {
|
|
|
17710
17885
|
if (!status.installed) return null;
|
|
17711
17886
|
let bytes;
|
|
17712
17887
|
try {
|
|
17713
|
-
bytes = await (0, import_promises28.readFile)(
|
|
17888
|
+
bytes = await (0, import_promises28.readFile)(import_node_path36.default.join(dir, name));
|
|
17714
17889
|
} catch {
|
|
17715
17890
|
verified = null;
|
|
17716
17891
|
return null;
|
|
@@ -17725,7 +17900,7 @@ function createGalleryEditorHandlers(opts = {}) {
|
|
|
17725
17900
|
}
|
|
17726
17901
|
|
|
17727
17902
|
// src/gallery/server.ts
|
|
17728
|
-
var
|
|
17903
|
+
var import_node_crypto9 = require("crypto");
|
|
17729
17904
|
var import_node_http2 = require("http");
|
|
17730
17905
|
var import_promises29 = require("fs/promises");
|
|
17731
17906
|
var MAX_EDIT_BYTES = 48 * 1024 * 1024;
|
|
@@ -17755,7 +17930,7 @@ async function readJsonBody(req) {
|
|
|
17755
17930
|
async function serveGallery(opts) {
|
|
17756
17931
|
const log = opts.onProgress ?? (() => {
|
|
17757
17932
|
});
|
|
17758
|
-
const session = opts.edit || opts.generate || opts.editor || opts.skeleton || opts.price ? (0,
|
|
17933
|
+
const session = opts.edit || opts.generate || opts.editor || opts.skeleton || opts.price ? (0, import_node_crypto9.randomBytes)(16).toString("hex") : null;
|
|
17759
17934
|
let media = /* @__PURE__ */ new Map();
|
|
17760
17935
|
let loading = null;
|
|
17761
17936
|
const reviewAssets = /* @__PURE__ */ new Map();
|
|
@@ -17959,7 +18134,7 @@ async function serveGallery(opts) {
|
|
|
17959
18134
|
try {
|
|
17960
18135
|
if (url2.pathname === "/") {
|
|
17961
18136
|
const { snapshot } = await load();
|
|
17962
|
-
const nonce = (0,
|
|
18137
|
+
const nonce = (0, import_node_crypto9.randomBytes)(16).toString("base64");
|
|
17963
18138
|
res.writeHead(200, {
|
|
17964
18139
|
"Content-Type": "text/html; charset=utf-8",
|
|
17965
18140
|
"Cache-Control": "no-store",
|
|
@@ -18034,7 +18209,7 @@ async function serveGallery(opts) {
|
|
|
18034
18209
|
}
|
|
18035
18210
|
|
|
18036
18211
|
// src/gallery/generate.ts
|
|
18037
|
-
var
|
|
18212
|
+
var import_node_crypto10 = require("crypto");
|
|
18038
18213
|
var import_zod14 = require("zod");
|
|
18039
18214
|
var GenerateRequestSchema = import_zod14.z.object({
|
|
18040
18215
|
project: import_zod14.z.string().min(1).optional(),
|
|
@@ -18334,7 +18509,7 @@ function createGenerateHandlers(opts) {
|
|
|
18334
18509
|
}
|
|
18335
18510
|
}
|
|
18336
18511
|
const job = {
|
|
18337
|
-
id: (0,
|
|
18512
|
+
id: (0, import_node_crypto10.randomBytes)(8).toString("hex"),
|
|
18338
18513
|
project: request.project ?? null,
|
|
18339
18514
|
keys: request.keys,
|
|
18340
18515
|
mode: request.refresh ? "refresh" : request.resume ? "resume" : request.restore ? "restore" : request.revert !== void 0 ? "revert" : "generate",
|
|
@@ -18402,15 +18577,15 @@ function createGenerateHandlers(opts) {
|
|
|
18402
18577
|
|
|
18403
18578
|
// src/pipeline/salvage.ts
|
|
18404
18579
|
var import_promises30 = require("fs/promises");
|
|
18405
|
-
var
|
|
18406
|
-
var
|
|
18580
|
+
var import_node_fs32 = require("fs");
|
|
18581
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
18407
18582
|
function providerClaimId(provider, objectId) {
|
|
18408
18583
|
return `${encodeURIComponent(provider)}:${encodeURIComponent(objectId)}`;
|
|
18409
18584
|
}
|
|
18410
18585
|
async function loadClaims(lockPaths, opts = {}) {
|
|
18411
18586
|
const claimed = /* @__PURE__ */ new Set();
|
|
18412
18587
|
for (const p of lockPaths) {
|
|
18413
|
-
if (!(0,
|
|
18588
|
+
if (!(0, import_node_fs32.existsSync)(p)) throw new Error(`Claim lockfile not found: ${p}`);
|
|
18414
18589
|
let parsed;
|
|
18415
18590
|
try {
|
|
18416
18591
|
parsed = parseLock(JSON.parse(await (0, import_promises30.readFile)(p, "utf8")));
|
|
@@ -18517,20 +18692,20 @@ function matchOrphanStyle(prompt, manifest) {
|
|
|
18517
18692
|
return matchStyleByPattern(prompt, manifest);
|
|
18518
18693
|
}
|
|
18519
18694
|
async function loadSiblingManifests(ownManifestPath, workspaceManifestPaths, claimPaths) {
|
|
18520
|
-
const own =
|
|
18695
|
+
const own = import_node_path37.default.resolve(ownManifestPath);
|
|
18521
18696
|
const siblingManifestPaths = [
|
|
18522
18697
|
.../* @__PURE__ */ new Set([
|
|
18523
18698
|
...workspaceManifestPaths,
|
|
18524
|
-
...claimPaths.map((c) =>
|
|
18699
|
+
...claimPaths.map((c) => import_node_path37.default.join(import_node_path37.default.dirname(import_node_path37.default.resolve(c)), "pixelkiln.manifest.json"))
|
|
18525
18700
|
])
|
|
18526
18701
|
];
|
|
18527
18702
|
const siblings = [];
|
|
18528
18703
|
for (const siblingManifestPath of siblingManifestPaths) {
|
|
18529
|
-
if (
|
|
18530
|
-
if (!(0,
|
|
18704
|
+
if (import_node_path37.default.resolve(siblingManifestPath) === own) continue;
|
|
18705
|
+
if (!(0, import_node_fs32.existsSync)(siblingManifestPath)) continue;
|
|
18531
18706
|
try {
|
|
18532
18707
|
const { manifest } = await loadManifest(siblingManifestPath);
|
|
18533
|
-
siblings.push({ label:
|
|
18708
|
+
siblings.push({ label: import_node_path37.default.basename(import_node_path37.default.dirname(siblingManifestPath)), manifest });
|
|
18534
18709
|
} catch {
|
|
18535
18710
|
}
|
|
18536
18711
|
}
|
|
@@ -18651,7 +18826,7 @@ async function applyTags(provider, decisions, existing, opts = {}) {
|
|
|
18651
18826
|
|
|
18652
18827
|
// src/pick/salvage-server.ts
|
|
18653
18828
|
var import_promises31 = require("fs/promises");
|
|
18654
|
-
var
|
|
18829
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
18655
18830
|
|
|
18656
18831
|
// src/pick/salvage-sheet.ts
|
|
18657
18832
|
var escapeHtml3 = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
@@ -18817,7 +18992,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
18817
18992
|
});
|
|
18818
18993
|
const html = renderSalvageSheet(orphans, {
|
|
18819
18994
|
styleId: ctx.styleId,
|
|
18820
|
-
importDir:
|
|
18995
|
+
importDir: import_node_path38.default.relative(process.cwd(), ctx.importDir) || "."
|
|
18821
18996
|
});
|
|
18822
18997
|
const byId = new Map(orphans.map((o) => [o.id, o]));
|
|
18823
18998
|
const existingTags = new Map(orphans.map((o) => [o.id, o.tags]));
|
|
@@ -18873,9 +19048,9 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
18873
19048
|
if (!buf.subarray(0, 8).equals(PNG_SIGNATURE2)) throw new Error("not a PNG");
|
|
18874
19049
|
decodePng(buf);
|
|
18875
19050
|
const assetId = idFromPrompt(orphan.prompt, taken);
|
|
18876
|
-
const rel =
|
|
18877
|
-
const outFile =
|
|
18878
|
-
await (0, import_promises31.mkdir)(
|
|
19051
|
+
const rel = import_node_path38.default.join("_salvaged", `${assetId}.png`);
|
|
19052
|
+
const outFile = import_node_path38.default.resolve(ctx.importDir, rel);
|
|
19053
|
+
await (0, import_promises31.mkdir)(import_node_path38.default.dirname(outFile), { recursive: true });
|
|
18879
19054
|
await (0, import_promises31.writeFile)(outFile, buf);
|
|
18880
19055
|
ctx.manifest.assets[assetId] = {
|
|
18881
19056
|
prompt: orphan.prompt,
|
|
@@ -18908,7 +19083,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
18908
19083
|
sourceUrl: durableSource,
|
|
18909
19084
|
sourceUrls: durableSource ? [{ url: durableSource }] : [],
|
|
18910
19085
|
outputs: [{
|
|
18911
|
-
path: portableOutputPath(outFile,
|
|
19086
|
+
path: portableOutputPath(outFile, import_node_path38.default.dirname(ctx.manifestPath)),
|
|
18912
19087
|
sha256: sha256(buf)
|
|
18913
19088
|
}],
|
|
18914
19089
|
submittedAt: orphan.createdAt,
|