pixelkiln 0.84.0 → 0.85.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 +462 -346
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +549 -433
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +444 -328
- package/dist/index.js.map +1 -1
- package/docs/REVISIONS.md +4 -2
- package/docs/SCENARIO.md +174 -7
- package/package.json +1 -1
- package/skills/pixelkiln/references/scenario.md +8 -4
package/dist/index.cjs
CHANGED
|
@@ -2141,9 +2141,9 @@ var PixelLabClient = class {
|
|
|
2141
2141
|
apiKey;
|
|
2142
2142
|
http;
|
|
2143
2143
|
/** Retries transport failures, 429, and 5xx; see http.ts for the policy. */
|
|
2144
|
-
async request(
|
|
2144
|
+
async request(path38, init) {
|
|
2145
2145
|
const auth = this.apiKey.startsWith("Bearer ") ? this.apiKey : `Bearer ${this.apiKey}`;
|
|
2146
|
-
const res = await this.http(`${BASE}${
|
|
2146
|
+
const res = await this.http(`${BASE}${path38}`, {
|
|
2147
2147
|
...init,
|
|
2148
2148
|
headers: {
|
|
2149
2149
|
Authorization: auth,
|
|
@@ -2153,13 +2153,13 @@ var PixelLabClient = class {
|
|
|
2153
2153
|
});
|
|
2154
2154
|
const text = await res.text();
|
|
2155
2155
|
if (!res.ok) {
|
|
2156
|
-
throw new PixelLabError(`${init?.method ?? "GET"} ${
|
|
2156
|
+
throw new PixelLabError(`${init?.method ?? "GET"} ${path38} \u2192 ${res.status}`, res.status, text);
|
|
2157
2157
|
}
|
|
2158
2158
|
if (!text) return {};
|
|
2159
2159
|
try {
|
|
2160
2160
|
return JSON.parse(text);
|
|
2161
2161
|
} catch {
|
|
2162
|
-
throw new Error(`${init?.method ?? "GET"} ${
|
|
2162
|
+
throw new Error(`${init?.method ?? "GET"} ${path38} returned invalid JSON`);
|
|
2163
2163
|
}
|
|
2164
2164
|
}
|
|
2165
2165
|
async balance() {
|
|
@@ -2564,10 +2564,10 @@ var PixelLabClient = class {
|
|
|
2564
2564
|
const palette = args.paletteSwatchBase64 ? { color_image: { type: "base64", base64: args.paletteSwatchBase64, format: "png" }, force_colors: true } : {};
|
|
2565
2565
|
const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
|
|
2566
2566
|
const south = args.reference?.south;
|
|
2567
|
-
let
|
|
2567
|
+
let path38;
|
|
2568
2568
|
let body;
|
|
2569
2569
|
if (args.mode === "v3") {
|
|
2570
|
-
|
|
2570
|
+
path38 = "/create-character-v3";
|
|
2571
2571
|
body = {
|
|
2572
2572
|
description: args.description,
|
|
2573
2573
|
image_size: imageSize,
|
|
@@ -2581,7 +2581,7 @@ var PixelLabClient = class {
|
|
|
2581
2581
|
...args.enhancePrompt ? { enhance_prompt: true } : {}
|
|
2582
2582
|
};
|
|
2583
2583
|
} else if (args.mode === "pro-flash") {
|
|
2584
|
-
|
|
2584
|
+
path38 = "/create-character-pro-flash";
|
|
2585
2585
|
const traits = args.styleTraits;
|
|
2586
2586
|
body = {
|
|
2587
2587
|
description: args.description,
|
|
@@ -2605,7 +2605,7 @@ var PixelLabClient = class {
|
|
|
2605
2605
|
} : {}
|
|
2606
2606
|
};
|
|
2607
2607
|
} else if (args.mode === "pro") {
|
|
2608
|
-
|
|
2608
|
+
path38 = "/create-character-pro";
|
|
2609
2609
|
body = {
|
|
2610
2610
|
description: args.description,
|
|
2611
2611
|
image_size: imageSize,
|
|
@@ -2619,7 +2619,7 @@ var PixelLabClient = class {
|
|
|
2619
2619
|
...!south && args.styleCharacterId ? { style_character_id: args.styleCharacterId } : {}
|
|
2620
2620
|
};
|
|
2621
2621
|
} else {
|
|
2622
|
-
|
|
2622
|
+
path38 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
|
|
2623
2623
|
body = {
|
|
2624
2624
|
description: args.description,
|
|
2625
2625
|
image_size: imageSize,
|
|
@@ -2636,8 +2636,8 @@ var PixelLabClient = class {
|
|
|
2636
2636
|
...palette
|
|
2637
2637
|
};
|
|
2638
2638
|
}
|
|
2639
|
-
const raw = await this.request(
|
|
2640
|
-
return validateResponse(CharacterSubmitSchema, raw,
|
|
2639
|
+
const raw = await this.request(path38, { method: "POST", body: JSON.stringify(body) });
|
|
2640
|
+
return validateResponse(CharacterSubmitSchema, raw, path38.slice(1));
|
|
2641
2641
|
}
|
|
2642
2642
|
/** A text edit of an existing character, applied to every direction and stored as a sibling. */
|
|
2643
2643
|
async createCharacterState(args) {
|
|
@@ -3850,10 +3850,10 @@ var import_node_fs = require("fs");
|
|
|
3850
3850
|
function sha256(data) {
|
|
3851
3851
|
return (0, import_node_crypto.createHash)("sha256").update(data).digest("hex");
|
|
3852
3852
|
}
|
|
3853
|
-
async function sha256File(
|
|
3853
|
+
async function sha256File(path38) {
|
|
3854
3854
|
return new Promise((resolve, reject) => {
|
|
3855
3855
|
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
3856
|
-
const stream = (0, import_node_fs.createReadStream)(
|
|
3856
|
+
const stream = (0, import_node_fs.createReadStream)(path38);
|
|
3857
3857
|
stream.on("data", (chunk2) => hash.update(chunk2));
|
|
3858
3858
|
stream.on("error", reject);
|
|
3859
3859
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
@@ -6257,9 +6257,9 @@ var RetroDiffusionClient = class {
|
|
|
6257
6257
|
if (!response.ok) throw new ProviderError("retrodiffusion", `Retro Diffusion download failed (${response.status})`, { status: response.status });
|
|
6258
6258
|
return Buffer.from(await response.arrayBuffer());
|
|
6259
6259
|
}
|
|
6260
|
-
async call(
|
|
6260
|
+
async call(path38, init = {}) {
|
|
6261
6261
|
if (!this.token) throw new Error("RD_API_KEY is not set");
|
|
6262
|
-
const response = await this.request(`${this.baseUrl}${
|
|
6262
|
+
const response = await this.request(`${this.baseUrl}${path38}`, {
|
|
6263
6263
|
...init,
|
|
6264
6264
|
headers: {
|
|
6265
6265
|
"Content-Type": "application/json",
|
|
@@ -6568,8 +6568,13 @@ function retroError(value) {
|
|
|
6568
6568
|
}
|
|
6569
6569
|
|
|
6570
6570
|
// src/providers/scenario.ts
|
|
6571
|
+
var import_node_crypto3 = require("crypto");
|
|
6572
|
+
var import_node_fs3 = require("fs");
|
|
6573
|
+
var import_node_os2 = __toESM(require("os"), 1);
|
|
6574
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
6571
6575
|
var DEFAULT_BASE_URL2 = "https://api.cloud.scenario.com/v1";
|
|
6572
6576
|
var SOURCE_PROTOCOL2 = "scenario:";
|
|
6577
|
+
var SUBMIT_RETRIES = 8;
|
|
6573
6578
|
var PROTECTED_PARAMETERS = /* @__PURE__ */ new Set([
|
|
6574
6579
|
"dryRun",
|
|
6575
6580
|
"height",
|
|
@@ -6616,7 +6621,11 @@ var ScenarioClient = class {
|
|
|
6616
6621
|
async submit(modelId, body, projectId) {
|
|
6617
6622
|
const response = await this.call(
|
|
6618
6623
|
`/generate/custom/${encodeURIComponent(modelId)}`,
|
|
6619
|
-
|
|
6624
|
+
// A 429 here means this account's concurrent-job limit refused the
|
|
6625
|
+
// request, so nothing was accepted or billed; wait it out instead of
|
|
6626
|
+
// failing the asset. The default four retries give up after about half
|
|
6627
|
+
// a minute, before a running job has finished.
|
|
6628
|
+
{ method: "POST", body: JSON.stringify(body), retries: SUBMIT_RETRIES },
|
|
6620
6629
|
{ projectId }
|
|
6621
6630
|
);
|
|
6622
6631
|
const job = response?.job;
|
|
@@ -6624,6 +6633,29 @@ var ScenarioClient = class {
|
|
|
6624
6633
|
if (!jobId) throw new Error("Scenario did not return an async job id");
|
|
6625
6634
|
return jobId;
|
|
6626
6635
|
}
|
|
6636
|
+
/**
|
|
6637
|
+
* Uploads one image (POST /assets) and returns its asset id, which a model's
|
|
6638
|
+
* reference input takes. Free, and idempotent per image: the id is kept by
|
|
6639
|
+
* content hash so a rerun does not upload the same bytes again.
|
|
6640
|
+
*/
|
|
6641
|
+
async uploadImage(image, projectId) {
|
|
6642
|
+
const bytes = Buffer.from(image.base64, "base64");
|
|
6643
|
+
const digest2 = sha256(bytes);
|
|
6644
|
+
const cached = readUploadCache()[uploadCacheKey(this.baseUrl, projectId, digest2)];
|
|
6645
|
+
if (cached) return cached;
|
|
6646
|
+
const response = await this.call(
|
|
6647
|
+
"/assets",
|
|
6648
|
+
{
|
|
6649
|
+
method: "POST",
|
|
6650
|
+
body: JSON.stringify({ image: image.base64, name: `pixelkiln-${digest2.slice(0, 12)}.${image.format === "png" ? "png" : "jpg"}` })
|
|
6651
|
+
},
|
|
6652
|
+
{ projectId }
|
|
6653
|
+
);
|
|
6654
|
+
const id = response?.asset?.id;
|
|
6655
|
+
if (typeof id !== "string" || !id) throw new Error("Scenario did not return an uploaded asset id");
|
|
6656
|
+
writeUploadCache(uploadCacheKey(this.baseUrl, projectId, digest2), id);
|
|
6657
|
+
return id;
|
|
6658
|
+
}
|
|
6627
6659
|
async job(id, projectId) {
|
|
6628
6660
|
const response = await this.call(`/jobs/${encodeURIComponent(id)}`, {}, { projectId });
|
|
6629
6661
|
const raw = response?.job;
|
|
@@ -6750,6 +6782,14 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6750
6782
|
supports(generator) {
|
|
6751
6783
|
return generator === "map";
|
|
6752
6784
|
}
|
|
6785
|
+
/**
|
|
6786
|
+
* An edit is a generation with the parent as a reference image, which the
|
|
6787
|
+
* editing models (GPT Image, Gemini, FLUX) take. Masked inpainting and the
|
|
6788
|
+
* PixelLab-specific modes have no common Scenario input.
|
|
6789
|
+
*/
|
|
6790
|
+
supportsRevision(mode2) {
|
|
6791
|
+
return mode2 === "image-to-image";
|
|
6792
|
+
}
|
|
6753
6793
|
estimate(spec) {
|
|
6754
6794
|
const options = scenarioOptions(spec);
|
|
6755
6795
|
return {
|
|
@@ -6770,11 +6810,31 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6770
6810
|
if (!Number.isInteger(count) || count < 1 || count > 4) {
|
|
6771
6811
|
throw new Error("Scenario numOutputs must be a whole number from 1 to 4");
|
|
6772
6812
|
}
|
|
6773
|
-
if (spec.width
|
|
6774
|
-
throw new Error("Scenario width and height must be
|
|
6813
|
+
if (!Number.isInteger(spec.width) || spec.width < 16 || spec.width > 4096 || !Number.isInteger(spec.height) || spec.height < 16 || spec.height > 4096) {
|
|
6814
|
+
throw new Error("Scenario width and height must be whole numbers from 16 to 4096");
|
|
6775
6815
|
}
|
|
6776
|
-
|
|
6777
|
-
|
|
6816
|
+
const revision = spec.revision;
|
|
6817
|
+
if (revision) {
|
|
6818
|
+
if (revision.sourceMembers?.length) {
|
|
6819
|
+
throw new Error(
|
|
6820
|
+
`Scenario edits one image; ${revision.sourceAssetId} is a ${revision.sourceMembers.length}-member set`
|
|
6821
|
+
);
|
|
6822
|
+
}
|
|
6823
|
+
if (revision.strength != null) {
|
|
6824
|
+
throw new Error("Scenario image-to-image takes no strength; use the model's own parameters");
|
|
6825
|
+
}
|
|
6826
|
+
}
|
|
6827
|
+
const parameter = referenceParameter(options);
|
|
6828
|
+
if ((styleImages.length || revision) && Object.hasOwn(options.parameters ?? {}, parameter)) {
|
|
6829
|
+
throw new Error(
|
|
6830
|
+
`Scenario parameters.${parameter} is the reference-image input and is filled from styleImages and revisions`
|
|
6831
|
+
);
|
|
6832
|
+
}
|
|
6833
|
+
if (styleImages.length + (revision ? 1 : 0) > 1 && options.referenceArray === false) {
|
|
6834
|
+
throw new Error(`Scenario ${parameter} takes one image; use one style image or a revision, not both`);
|
|
6835
|
+
}
|
|
6836
|
+
if (options.referenceParameter != null && (typeof options.referenceParameter !== "string" || !options.referenceParameter.trim())) {
|
|
6837
|
+
throw new Error("Scenario referenceParameter must be a non-empty string");
|
|
6778
6838
|
}
|
|
6779
6839
|
if (options.projectId != null && (typeof options.projectId !== "string" || !options.projectId.trim())) {
|
|
6780
6840
|
throw new Error("Scenario projectId must be a non-empty string");
|
|
@@ -6787,7 +6847,12 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6787
6847
|
async submit(spec, styleImages) {
|
|
6788
6848
|
this.validate(spec, styleImages);
|
|
6789
6849
|
const options = scenarioOptions(spec);
|
|
6790
|
-
const
|
|
6850
|
+
const references = [];
|
|
6851
|
+
if (spec.revision) references.push(readRevisionSource(spec));
|
|
6852
|
+
for (const image of styleImages) references.push({ base64: image.base64, format: image.format });
|
|
6853
|
+
const referenceAssetIds = [];
|
|
6854
|
+
for (const image of references) referenceAssetIds.push(await this.client.uploadImage(image, options.projectId));
|
|
6855
|
+
const body = requestBody(spec, options, referenceAssetIds);
|
|
6791
6856
|
const quote = await this.client.quote(options.modelId, body, options.projectId);
|
|
6792
6857
|
if (quote.total > options.maxComputeUnits + Number.EPSILON) {
|
|
6793
6858
|
throw new Error(
|
|
@@ -6804,7 +6869,8 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6804
6869
|
quotedComputeUnits: quote.total,
|
|
6805
6870
|
creativeUnitsCost: quote.creativeUnitsCost,
|
|
6806
6871
|
costDetails: quote.costDetails,
|
|
6807
|
-
ipDetectionCost: quote.ipDetectionCost
|
|
6872
|
+
ipDetectionCost: quote.ipDetectionCost,
|
|
6873
|
+
...referenceAssetIds.length ? { referenceAssetIds } : {}
|
|
6808
6874
|
}
|
|
6809
6875
|
};
|
|
6810
6876
|
}
|
|
@@ -6826,7 +6892,13 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6826
6892
|
}
|
|
6827
6893
|
const assetIds = scenarioAssetIds(job);
|
|
6828
6894
|
if (!assetIds.length) return { status: "failed", error: "Scenario job returned no asset IDs" };
|
|
6829
|
-
|
|
6895
|
+
let assets;
|
|
6896
|
+
try {
|
|
6897
|
+
assets = await this.assets(assetIds, reference.projectId);
|
|
6898
|
+
} catch (error) {
|
|
6899
|
+
if (error instanceof UnsupportedScenarioOutput) return { status: "failed", error: error.message };
|
|
6900
|
+
throw error;
|
|
6901
|
+
}
|
|
6830
6902
|
if (assets.length > 1) {
|
|
6831
6903
|
return {
|
|
6832
6904
|
status: "review",
|
|
@@ -6881,6 +6953,32 @@ var ScenarioProvider = class _ScenarioProvider {
|
|
|
6881
6953
|
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
6954
|
}
|
|
6883
6955
|
};
|
|
6956
|
+
function uploadCachePath() {
|
|
6957
|
+
return process.env.SCENARIO_UPLOAD_CACHE ?? import_node_path2.default.join(import_node_os2.default.tmpdir(), "pixelkiln-scenario", "uploads.json");
|
|
6958
|
+
}
|
|
6959
|
+
function uploadCacheKey(baseUrl, projectId, digest2) {
|
|
6960
|
+
return (0, import_node_crypto3.createHash)("sha256").update(`${baseUrl}
|
|
6961
|
+
${projectId ?? ""}
|
|
6962
|
+
${digest2}`).digest("hex");
|
|
6963
|
+
}
|
|
6964
|
+
function readUploadCache() {
|
|
6965
|
+
try {
|
|
6966
|
+
const value = JSON.parse((0, import_node_fs3.readFileSync)(uploadCachePath(), "utf8"));
|
|
6967
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
6968
|
+
} catch {
|
|
6969
|
+
return {};
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
function writeUploadCache(key, assetId) {
|
|
6973
|
+
try {
|
|
6974
|
+
const file = uploadCachePath();
|
|
6975
|
+
(0, import_node_fs3.mkdirSync)(import_node_path2.default.dirname(file), { recursive: true });
|
|
6976
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
6977
|
+
(0, import_node_fs3.writeFileSync)(tmp, JSON.stringify({ ...readUploadCache(), [key]: assetId }));
|
|
6978
|
+
(0, import_node_fs3.renameSync)(tmp, file);
|
|
6979
|
+
} catch {
|
|
6980
|
+
}
|
|
6981
|
+
}
|
|
6884
6982
|
function scenarioOptions(spec) {
|
|
6885
6983
|
return spec.providerOptions;
|
|
6886
6984
|
}
|
|
@@ -6896,9 +6994,25 @@ function scenarioBaseUrl(value) {
|
|
|
6896
6994
|
}
|
|
6897
6995
|
return url.href.replace(/\/$/, "");
|
|
6898
6996
|
}
|
|
6899
|
-
function
|
|
6997
|
+
function referenceParameter(options) {
|
|
6998
|
+
return options.referenceParameter?.trim() || "referenceImages";
|
|
6999
|
+
}
|
|
7000
|
+
function readRevisionSource(spec) {
|
|
7001
|
+
const revision = spec.revision;
|
|
7002
|
+
if (!revision.sourceFile || !revision.sourceSha256 || !revision.sourceFormat) {
|
|
7003
|
+
throw new Error(`${spec.styleId}/${spec.assetId}: revision source is not ready`);
|
|
7004
|
+
}
|
|
7005
|
+
const bytes = (0, import_node_fs3.readFileSync)(revision.sourceFile);
|
|
7006
|
+
if (sha256(bytes) !== revision.sourceSha256) {
|
|
7007
|
+
throw new Error(`${spec.styleId}/${spec.assetId}: revision source changed after the manifest was resolved`);
|
|
7008
|
+
}
|
|
7009
|
+
return { base64: bytes.toString("base64"), format: revision.sourceFormat };
|
|
7010
|
+
}
|
|
7011
|
+
function requestBody(spec, options, referenceAssetIds = []) {
|
|
7012
|
+
const parameter = referenceParameter(options);
|
|
6900
7013
|
return {
|
|
6901
7014
|
...options.parameters ?? {},
|
|
7015
|
+
...referenceAssetIds.length ? { [parameter]: options.referenceArray === false ? referenceAssetIds[0] : referenceAssetIds } : {},
|
|
6902
7016
|
prompt: spec.prompt,
|
|
6903
7017
|
width: spec.width,
|
|
6904
7018
|
height: spec.height,
|
|
@@ -6988,9 +7102,11 @@ function scenarioAssetIds(job) {
|
|
|
6988
7102
|
function assetDownloadUrl(asset) {
|
|
6989
7103
|
return asset.originalFileUrl || asset.url;
|
|
6990
7104
|
}
|
|
7105
|
+
var UnsupportedScenarioOutput = class extends Error {
|
|
7106
|
+
};
|
|
6991
7107
|
function assertPngAsset(asset) {
|
|
6992
7108
|
if (asset.originalMimeType && asset.originalMimeType !== MediaType.PNG) {
|
|
6993
|
-
throw new
|
|
7109
|
+
throw new UnsupportedScenarioOutput(
|
|
6994
7110
|
`Scenario asset ${asset.id} is ${asset.originalMimeType}; PixelKiln's Scenario MVP requires PNG output`
|
|
6995
7111
|
);
|
|
6996
7112
|
}
|
|
@@ -7055,9 +7171,9 @@ function redactScenarioError(value, secrets) {
|
|
|
7055
7171
|
}
|
|
7056
7172
|
|
|
7057
7173
|
// src/providers/comfyui.ts
|
|
7058
|
-
var
|
|
7174
|
+
var import_node_crypto4 = require("crypto");
|
|
7059
7175
|
var import_promises = require("fs/promises");
|
|
7060
|
-
var
|
|
7176
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
7061
7177
|
var DEFAULT_BASE_URL3 = "http://127.0.0.1:8188";
|
|
7062
7178
|
var BUILTIN_BINDINGS = /* @__PURE__ */ new Set([
|
|
7063
7179
|
"prompt",
|
|
@@ -7084,7 +7200,7 @@ var ComfyUIClient = class {
|
|
|
7084
7200
|
async submit(workflow) {
|
|
7085
7201
|
const value = await this.json("prompt", {
|
|
7086
7202
|
method: "POST",
|
|
7087
|
-
body: JSON.stringify({ prompt: workflow, client_id: (0,
|
|
7203
|
+
body: JSON.stringify({ prompt: workflow, client_id: (0, import_node_crypto4.randomUUID)() })
|
|
7088
7204
|
});
|
|
7089
7205
|
if (!isObject(value)) throw new Error("ComfyUI returned an invalid queue response");
|
|
7090
7206
|
const promptId = value.prompt_id;
|
|
@@ -7194,7 +7310,7 @@ var ComfyUIProvider = class _ComfyUIProvider {
|
|
|
7194
7310
|
}
|
|
7195
7311
|
async resolveOptions(value, context) {
|
|
7196
7312
|
const options = parseOptions(value);
|
|
7197
|
-
const workflowPath =
|
|
7313
|
+
const workflowPath = import_node_path3.default.resolve(context.root, options.workflowFile);
|
|
7198
7314
|
let raw;
|
|
7199
7315
|
try {
|
|
7200
7316
|
raw = await (0, import_promises.readFile)(workflowPath, "utf8");
|
|
@@ -7249,7 +7365,7 @@ var ComfyUIProvider = class _ComfyUIProvider {
|
|
|
7249
7365
|
if (typeof item !== "string") {
|
|
7250
7366
|
throw new Error(`ComfyUI ${label} must be a manifest-relative PNG or JPEG path`);
|
|
7251
7367
|
}
|
|
7252
|
-
const file =
|
|
7368
|
+
const file = import_node_path3.default.resolve(context.root, item);
|
|
7253
7369
|
let bytes;
|
|
7254
7370
|
try {
|
|
7255
7371
|
bytes = await (0, import_promises.readFile)(file);
|
|
@@ -7961,7 +8077,7 @@ function canonical(value) {
|
|
|
7961
8077
|
}
|
|
7962
8078
|
|
|
7963
8079
|
// src/providers/fake.ts
|
|
7964
|
-
var
|
|
8080
|
+
var import_node_crypto5 = require("crypto");
|
|
7965
8081
|
var FAKE_PNG = Buffer.from(
|
|
7966
8082
|
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
|
|
7967
8083
|
"base64"
|
|
@@ -8063,7 +8179,7 @@ var FakeProvider = class {
|
|
|
8063
8179
|
}
|
|
8064
8180
|
async download(url) {
|
|
8065
8181
|
if (!url.startsWith("fake://")) throw new Error(`fake provider cannot download ${url}`);
|
|
8066
|
-
const salt = (0,
|
|
8182
|
+
const salt = (0, import_node_crypto5.createHash)("sha256").update(url).digest();
|
|
8067
8183
|
return Buffer.concat([FAKE_PNG, salt.subarray(0, 4)]);
|
|
8068
8184
|
}
|
|
8069
8185
|
async balance() {
|
|
@@ -8211,8 +8327,8 @@ registerProvider({
|
|
|
8211
8327
|
|
|
8212
8328
|
// src/manifest.ts
|
|
8213
8329
|
var import_promises3 = require("fs/promises");
|
|
8214
|
-
var
|
|
8215
|
-
var
|
|
8330
|
+
var import_node_fs6 = require("fs");
|
|
8331
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
8216
8332
|
|
|
8217
8333
|
// src/loop-directions.ts
|
|
8218
8334
|
var SINGLE_OUTPUT_FIELDS = ["file", "cell", "remoteId", "source", "sourceByStyle", "outputRole"];
|
|
@@ -8300,15 +8416,15 @@ function isObject2(value) {
|
|
|
8300
8416
|
}
|
|
8301
8417
|
|
|
8302
8418
|
// src/outputs.ts
|
|
8303
|
-
var
|
|
8304
|
-
var
|
|
8419
|
+
var import_node_fs5 = require("fs");
|
|
8420
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
8305
8421
|
|
|
8306
8422
|
// src/lock.ts
|
|
8307
8423
|
var import_promises2 = require("fs/promises");
|
|
8308
|
-
var
|
|
8309
|
-
var
|
|
8424
|
+
var import_node_fs4 = require("fs");
|
|
8425
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
8310
8426
|
async function loadLock(lockPath) {
|
|
8311
|
-
if (!(0,
|
|
8427
|
+
if (!(0, import_node_fs4.existsSync)(lockPath)) return { version: 2, entries: {} };
|
|
8312
8428
|
try {
|
|
8313
8429
|
return parseLock(JSON.parse(await (0, import_promises2.readFile)(lockPath, "utf8")));
|
|
8314
8430
|
} catch (err) {
|
|
@@ -8323,7 +8439,7 @@ var saveQueues = /* @__PURE__ */ new Map();
|
|
|
8323
8439
|
var dirtyPatches = /* @__PURE__ */ new WeakMap();
|
|
8324
8440
|
var dirtyDeletes = /* @__PURE__ */ new WeakMap();
|
|
8325
8441
|
function saveLock(lockPath, lock) {
|
|
8326
|
-
const queueKey =
|
|
8442
|
+
const queueKey = import_node_path4.default.resolve(lockPath);
|
|
8327
8443
|
const previous = saveQueues.get(queueKey) ?? Promise.resolve();
|
|
8328
8444
|
const next = previous.catch(() => {
|
|
8329
8445
|
}).then(() => writeLockNow(lockPath, lock));
|
|
@@ -8335,7 +8451,7 @@ function saveLock(lockPath, lock) {
|
|
|
8335
8451
|
return next;
|
|
8336
8452
|
}
|
|
8337
8453
|
async function writeLockNow(lockPath, lock) {
|
|
8338
|
-
await (0, import_promises2.mkdir)(
|
|
8454
|
+
await (0, import_promises2.mkdir)(import_node_path4.default.dirname(import_node_path4.default.resolve(lockPath)), { recursive: true });
|
|
8339
8455
|
const release = await acquireFileLock(lockPath);
|
|
8340
8456
|
try {
|
|
8341
8457
|
await writeLockWhileHeld(lockPath, lock);
|
|
@@ -8345,7 +8461,7 @@ async function writeLockNow(lockPath, lock) {
|
|
|
8345
8461
|
}
|
|
8346
8462
|
async function writeLockWhileHeld(file, lock) {
|
|
8347
8463
|
let disk = { version: 2, entries: {} };
|
|
8348
|
-
if ((0,
|
|
8464
|
+
if ((0, import_node_fs4.existsSync)(file)) {
|
|
8349
8465
|
try {
|
|
8350
8466
|
disk = parseLock(JSON.parse(await (0, import_promises2.readFile)(file, "utf8")));
|
|
8351
8467
|
} catch (err) {
|
|
@@ -8480,22 +8596,22 @@ function totalSpend(lock, unit = "generations") {
|
|
|
8480
8596
|
|
|
8481
8597
|
// src/outputs.ts
|
|
8482
8598
|
function portableOutputPath(file, manifestDir) {
|
|
8483
|
-
const root =
|
|
8484
|
-
const absolute =
|
|
8485
|
-
const relative =
|
|
8486
|
-
const value =
|
|
8487
|
-
return value.split(
|
|
8599
|
+
const root = import_node_path5.default.resolve(manifestDir);
|
|
8600
|
+
const absolute = import_node_path5.default.isAbsolute(file) ? import_node_path5.default.normalize(file) : import_node_path5.default.resolve(root, file);
|
|
8601
|
+
const relative = import_node_path5.default.relative(root, absolute);
|
|
8602
|
+
const value = import_node_path5.default.isAbsolute(relative) ? absolute : relative;
|
|
8603
|
+
return value.split(import_node_path5.default.sep).join("/");
|
|
8488
8604
|
}
|
|
8489
8605
|
function resolveOutputPath(recordedPath, manifestDir) {
|
|
8490
|
-
if (
|
|
8491
|
-
if (
|
|
8492
|
-
return
|
|
8606
|
+
if (import_node_path5.default.isAbsolute(recordedPath)) return import_node_path5.default.normalize(recordedPath);
|
|
8607
|
+
if (import_node_path5.default.win32.isAbsolute(recordedPath)) return recordedPath;
|
|
8608
|
+
return import_node_path5.default.resolve(manifestDir, recordedPath.split(/[\\/]/).join(import_node_path5.default.sep));
|
|
8493
8609
|
}
|
|
8494
8610
|
function expectedOutputPath(spec, role, index, total, mediaType) {
|
|
8495
8611
|
return memberPath(spec.outFile, role, index, total, mediaType);
|
|
8496
8612
|
}
|
|
8497
8613
|
function memberPath(file, role, index, total, mediaType) {
|
|
8498
|
-
const originalExt =
|
|
8614
|
+
const originalExt = import_node_path5.default.extname(file);
|
|
8499
8615
|
const ext = mediaType ? mediaExtension(mediaType) : originalExt || ".png";
|
|
8500
8616
|
const stem = originalExt ? file.slice(0, -originalExt.length) : file;
|
|
8501
8617
|
if (total === 1) return `${stem}${ext}`;
|
|
@@ -8509,10 +8625,10 @@ function isFrameSetEntry(entry) {
|
|
|
8509
8625
|
return entry.generator === "frames" && isMemberSetEntry(entry);
|
|
8510
8626
|
}
|
|
8511
8627
|
function sourceIsStem(source, entry, manifestDir) {
|
|
8512
|
-
return isMemberSetEntry(entry) && !(0,
|
|
8628
|
+
return isMemberSetEntry(entry) && !(0, import_node_fs5.existsSync)(import_node_path5.default.resolve(manifestDir, source));
|
|
8513
8629
|
}
|
|
8514
8630
|
function sourceOutputPath(source, entry, index, manifestDir) {
|
|
8515
|
-
const absolute =
|
|
8631
|
+
const absolute = import_node_path5.default.resolve(manifestDir, source);
|
|
8516
8632
|
if (!sourceIsStem(source, entry, manifestDir)) return absolute;
|
|
8517
8633
|
const output = entry.outputs[index];
|
|
8518
8634
|
return memberPath(absolute, output.role, index, entry.outputs.length, output.mediaType);
|
|
@@ -8599,7 +8715,7 @@ function resolveSpecOutputs(spec, lock, manifestDir) {
|
|
|
8599
8715
|
id: spec.assetId,
|
|
8600
8716
|
index: 0,
|
|
8601
8717
|
path: spec.outFile,
|
|
8602
|
-
absolutePath:
|
|
8718
|
+
absolutePath: import_node_path5.default.resolve(spec.outFile),
|
|
8603
8719
|
sha256: ""
|
|
8604
8720
|
}];
|
|
8605
8721
|
}
|
|
@@ -8615,8 +8731,8 @@ function frameSetFps(entry) {
|
|
|
8615
8731
|
|
|
8616
8732
|
// src/manifest.ts
|
|
8617
8733
|
async function loadManifest(manifestPath) {
|
|
8618
|
-
const abs =
|
|
8619
|
-
if (!(0,
|
|
8734
|
+
const abs = import_node_path6.default.resolve(manifestPath);
|
|
8735
|
+
if (!(0, import_node_fs6.existsSync)(abs)) throw new ProjectError(`No manifest at ${abs}`);
|
|
8620
8736
|
const expansion = expandLoopDirections(JSON.parse(await (0, import_promises3.readFile)(abs, "utf8")));
|
|
8621
8737
|
if (expansion.issues.length) {
|
|
8622
8738
|
throw new ProjectError(`Manifest at ${abs} is invalid:
|
|
@@ -8708,7 +8824,7 @@ ${issues}`);
|
|
|
8708
8824
|
${unknownReferences.map((i) => ` ${i}`).join("\n")}`);
|
|
8709
8825
|
}
|
|
8710
8826
|
const loopFamilies = Object.keys(expansion.families).length ? { loopFamilies: expansion.families } : {};
|
|
8711
|
-
return { manifest: parsed.data, root:
|
|
8827
|
+
return { manifest: parsed.data, root: import_node_path6.default.dirname(abs), path: abs, ...loopFamilies };
|
|
8712
8828
|
}
|
|
8713
8829
|
function parentAssetId(asset) {
|
|
8714
8830
|
return asset?.revision?.from ?? asset?.state?.of ?? asset?.animation?.of ?? asset?.portrait?.of ?? asset?.outfit?.of;
|
|
@@ -8809,10 +8925,10 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8809
8925
|
}
|
|
8810
8926
|
const styleImageCache = /* @__PURE__ */ new Map();
|
|
8811
8927
|
async function loadStyleImage(rel, what = "Style image") {
|
|
8812
|
-
const abs =
|
|
8928
|
+
const abs = import_node_path6.default.resolve(root, rel);
|
|
8813
8929
|
let hit = styleImageCache.get(abs);
|
|
8814
8930
|
if (!hit) {
|
|
8815
|
-
if (!(0,
|
|
8931
|
+
if (!(0, import_node_fs6.existsSync)(abs)) throw new Error(`${what} not found: ${abs}`);
|
|
8816
8932
|
const buf = await (0, import_promises3.readFile)(abs);
|
|
8817
8933
|
const metadata = imageMetadata(buf);
|
|
8818
8934
|
if (!metadata) throw new Error(`${what} is not a readable PNG or JPEG: ${abs}`);
|
|
@@ -8839,7 +8955,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8839
8955
|
styleImageDimensions.push(loadedImage);
|
|
8840
8956
|
}
|
|
8841
8957
|
const resolvedImages = style.styleImages.map((image) => {
|
|
8842
|
-
const hit = styleImageCache.get(
|
|
8958
|
+
const hit = styleImageCache.get(import_node_path6.default.resolve(root, image.path));
|
|
8843
8959
|
return { base64: hit.base64, width: hit.width, height: hit.height, format: hit.format };
|
|
8844
8960
|
});
|
|
8845
8961
|
const styleSpecs = /* @__PURE__ */ new Map();
|
|
@@ -8940,9 +9056,9 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8940
9056
|
} else {
|
|
8941
9057
|
prompt = [style.promptPrefix, subject, style.promptSuffix].map((p) => p.trim()).filter(Boolean).join(", ");
|
|
8942
9058
|
}
|
|
8943
|
-
const relFile = asset.file ??
|
|
8944
|
-
const outFile =
|
|
8945
|
-
const qualityOutFile = style.quality ?
|
|
9059
|
+
const relFile = asset.file ?? import_node_path6.default.join(asset.category ?? "", `${assetId}.png`);
|
|
9060
|
+
const outFile = import_node_path6.default.resolve(root, style.outDir, relFile);
|
|
9061
|
+
const qualityOutFile = style.quality ? import_node_path6.default.resolve(root, style.quality.outDir, pngPath(relFile)) : void 0;
|
|
8946
9062
|
const tileSize = generator === "tiles" ? size : style.tileSize ?? 32;
|
|
8947
9063
|
const tileVariations = tileFeatureOutputCount(generator === "tiles" ? style.tileFeature : void 0) ?? tileVariationCount(countNumberedDescriptions(prompt));
|
|
8948
9064
|
const terrainTiles = generator === "terrain" ? terrainTileCount(style.terrainTransitionSize) : 0;
|
|
@@ -9037,7 +9153,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9037
9153
|
minGridConfidence: style.quality.minGridConfidence,
|
|
9038
9154
|
...style.quality.minTransparency == null ? {} : { minTransparency: style.quality.minTransparency },
|
|
9039
9155
|
...style.quality.fixerRevision ? { fixerRevision: style.quality.fixerRevision } : {},
|
|
9040
|
-
...style.quality.fixerPython ? { fixerPython:
|
|
9156
|
+
...style.quality.fixerPython ? { fixerPython: import_node_path6.default.resolve(root, style.quality.fixerPython) } : {},
|
|
9041
9157
|
...style.quality.fps == null ? {} : { fps: style.quality.fps }
|
|
9042
9158
|
}
|
|
9043
9159
|
} : {},
|
|
@@ -9120,7 +9236,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9120
9236
|
const file = memberPath(anchorSpec.outFile, "south", 0, anchorSpec.character.directions, MediaType.PNG);
|
|
9121
9237
|
resolved.character = {
|
|
9122
9238
|
...resolved.character,
|
|
9123
|
-
styleAnchor: { assetId: anchorId, spec: anchorSpec, file, sha256: (0,
|
|
9239
|
+
styleAnchor: { assetId: anchorId, spec: anchorSpec, file, sha256: (0, import_node_fs6.existsSync)(file) ? await sha256File(file) : null }
|
|
9124
9240
|
};
|
|
9125
9241
|
}
|
|
9126
9242
|
const characterParent = asset.state?.of ?? asset.animation?.of ?? asset.portrait?.of;
|
|
@@ -9133,7 +9249,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9133
9249
|
parentAssetId: characterParent,
|
|
9134
9250
|
parentSpec,
|
|
9135
9251
|
parentFile,
|
|
9136
|
-
parentSha256: (0,
|
|
9252
|
+
parentSha256: (0, import_node_fs6.existsSync)(parentFile) ? await sha256File(parentFile) : null,
|
|
9137
9253
|
// A state and its animations keep the engine and rotations of the base.
|
|
9138
9254
|
mode: resolved.character.kind === "base" ? resolved.character.mode : parentSpec.character?.mode ?? resolved.character.mode,
|
|
9139
9255
|
directions: resolved.character.kind === "base" ? resolved.character.directions : parentSpec.character?.directions ?? resolved.character.directions
|
|
@@ -9151,7 +9267,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9151
9267
|
parentAssetId: characterParent,
|
|
9152
9268
|
parentSpec,
|
|
9153
9269
|
parentFile,
|
|
9154
|
-
parentSha256: (0,
|
|
9270
|
+
parentSha256: (0, import_node_fs6.existsSync)(parentFile) ? await sha256File(parentFile) : null,
|
|
9155
9271
|
// A state and its animations keep the base's own rotation count.
|
|
9156
9272
|
directions: resolved.objectPro.kind === "base" ? resolved.objectPro.directions : parentSpec.objectPro.directions
|
|
9157
9273
|
};
|
|
@@ -9173,7 +9289,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9173
9289
|
animation: sourceSpec.character.animation,
|
|
9174
9290
|
outfit: {
|
|
9175
9291
|
reference: {
|
|
9176
|
-
path:
|
|
9292
|
+
path: import_node_path6.default.resolve(root, asset.outfit.reference),
|
|
9177
9293
|
sha256: referenceImage.hash,
|
|
9178
9294
|
width: referenceImage.width,
|
|
9179
9295
|
height: referenceImage.height,
|
|
@@ -9195,23 +9311,23 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9195
9311
|
);
|
|
9196
9312
|
}
|
|
9197
9313
|
const sourceSpec = await finalize(asset.revision.from);
|
|
9198
|
-
const sourceStem = sourceSpec.quality?.outFile ?? (sourceSpec.source ?
|
|
9314
|
+
const sourceStem = sourceSpec.quality?.outFile ?? (sourceSpec.source ? import_node_path6.default.resolve(root, sourceSpec.source) : sourceSpec.outFile);
|
|
9199
9315
|
const sourceMembers = await revisionSourceMembers(sourceStem, `revision source ${asset.revision.from}`);
|
|
9200
9316
|
const sourceFile = sourceMembers?.[0]?.file ?? sourceStem;
|
|
9201
9317
|
const sourceImage = sourceMembers ? { ...sourceMembers[0], hash: sha256(JSON.stringify(sourceMembers.map((member) => member.sha256))) } : await optionalRevisionImage(sourceFile, "revision source");
|
|
9202
9318
|
const sourceFps = sourceSpec.character?.animation?.fps ?? sourceSpec.objectPro?.animation?.fps ?? sourceSpec.revision?.fps ?? sourceSpec.revision?.sourceFps;
|
|
9203
|
-
const maskFile = asset.revision.mask ?
|
|
9319
|
+
const maskFile = asset.revision.mask ? import_node_path6.default.resolve(root, asset.revision.mask) : void 0;
|
|
9204
9320
|
const maskImage = maskFile ? await optionalRevisionImage(maskFile, "revision mask", "png") : null;
|
|
9205
9321
|
if (sourceImage && maskImage && (sourceImage.width !== maskImage.width || sourceImage.height !== maskImage.height)) {
|
|
9206
9322
|
throw new Error(
|
|
9207
9323
|
`Revision mask for ${styleId}/${assetId} is ${maskImage.width}x${maskImage.height}; source ${asset.revision.from} is ${sourceImage.width}x${sourceImage.height}`
|
|
9208
9324
|
);
|
|
9209
9325
|
}
|
|
9210
|
-
const paletteImageFile = asset.revision.paletteImage ?
|
|
9326
|
+
const paletteImageFile = asset.revision.paletteImage ? import_node_path6.default.resolve(root, asset.revision.paletteImage) : void 0;
|
|
9211
9327
|
const paletteImage = paletteImageFile ? await optionalRevisionImage(paletteImageFile, "revision palette image", "png") : null;
|
|
9212
|
-
const lastFrameFile = asset.revision.lastFrame ?
|
|
9328
|
+
const lastFrameFile = asset.revision.lastFrame ? import_node_path6.default.resolve(root, asset.revision.lastFrame) : void 0;
|
|
9213
9329
|
const lastFrame = lastFrameFile ? await optionalRevisionImage(lastFrameFile, "revision last frame") : null;
|
|
9214
|
-
const keypointsFile = asset.revision.keypointsFile ?
|
|
9330
|
+
const keypointsFile = asset.revision.keypointsFile ? import_node_path6.default.resolve(root, asset.revision.keypointsFile) : void 0;
|
|
9215
9331
|
const keypoints = keypointsFile ? await optionalSkeletonSet(keypointsFile) : null;
|
|
9216
9332
|
resolved.revision = {
|
|
9217
9333
|
mode: asset.revision.mode,
|
|
@@ -9378,7 +9494,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
9378
9494
|
return specs;
|
|
9379
9495
|
}
|
|
9380
9496
|
function pngPath(file) {
|
|
9381
|
-
const extension =
|
|
9497
|
+
const extension = import_node_path6.default.extname(file);
|
|
9382
9498
|
return extension ? `${file.slice(0, -extension.length)}.png` : `${file}.png`;
|
|
9383
9499
|
}
|
|
9384
9500
|
async function resolveMapScene(root, styleId, assetId, generator, scene, provider) {
|
|
@@ -9388,12 +9504,12 @@ async function resolveMapScene(root, styleId, assetId, generator, scene, provide
|
|
|
9388
9504
|
if (!provider.supportsMapScene) {
|
|
9389
9505
|
throw new Error(`assets.${assetId}.scene: provider "${provider.id}" cannot draw into a scene; PixelLab's /map-objects can`);
|
|
9390
9506
|
}
|
|
9391
|
-
const file =
|
|
9507
|
+
const file = import_node_path6.default.resolve(root, scene.image);
|
|
9392
9508
|
const image = await optionalRevisionImage(file, "scene image", "png");
|
|
9393
9509
|
const base = { file, sha256: image?.hash ?? null, width: image?.width ?? null, height: image?.height ?? null };
|
|
9394
9510
|
const placement = scene.placement;
|
|
9395
9511
|
if ("mask" in placement) {
|
|
9396
|
-
const maskFile =
|
|
9512
|
+
const maskFile = import_node_path6.default.resolve(root, placement.mask);
|
|
9397
9513
|
const mask = await optionalRevisionImage(maskFile, "scene mask", "png");
|
|
9398
9514
|
if (image && mask && (image.width !== mask.width || image.height !== mask.height)) {
|
|
9399
9515
|
throw new Error(
|
|
@@ -9408,7 +9524,7 @@ async function resolveMapScene(root, styleId, assetId, generator, scene, provide
|
|
|
9408
9524
|
return "oval" in placement ? { ...base, placement: { type: "oval", fraction: placement.oval } } : { ...base, placement: { type: "rectangle", fraction: placement.rectangle } };
|
|
9409
9525
|
}
|
|
9410
9526
|
async function optionalRevisionImage(file, label, requiredFormat) {
|
|
9411
|
-
if (!(0,
|
|
9527
|
+
if (!(0, import_node_fs6.existsSync)(file)) return null;
|
|
9412
9528
|
const bytes = await (0, import_promises3.readFile)(file);
|
|
9413
9529
|
const metadata = imageMetadata(bytes);
|
|
9414
9530
|
if (!metadata) throw new Error(`${label} is not a readable PNG or JPEG: ${file}`);
|
|
@@ -9418,7 +9534,7 @@ async function optionalRevisionImage(file, label, requiredFormat) {
|
|
|
9418
9534
|
return { hash: sha256(bytes), ...metadata };
|
|
9419
9535
|
}
|
|
9420
9536
|
async function optionalSkeletonSet(file) {
|
|
9421
|
-
if (!(0,
|
|
9537
|
+
if (!(0, import_node_fs6.existsSync)(file)) return null;
|
|
9422
9538
|
const bytes = await (0, import_promises3.readFile)(file);
|
|
9423
9539
|
let json;
|
|
9424
9540
|
try {
|
|
@@ -9429,17 +9545,17 @@ async function optionalSkeletonSet(file) {
|
|
|
9429
9545
|
return { hash: sha256(bytes), skeleton: parseSkeletonSet(json, file) };
|
|
9430
9546
|
}
|
|
9431
9547
|
async function revisionSourceMembers(stem, label) {
|
|
9432
|
-
if ((0,
|
|
9548
|
+
if ((0, import_node_fs6.existsSync)(stem)) return null;
|
|
9433
9549
|
const at = (role) => memberPath(stem, role, 0, 2, MediaType.PNG);
|
|
9434
9550
|
let roles = [];
|
|
9435
9551
|
for (const set of [CHARACTER_DIRECTIONS_8, CHARACTER_DIRECTIONS_4]) {
|
|
9436
|
-
if (set.every((direction) => (0,
|
|
9552
|
+
if (set.every((direction) => (0, import_node_fs6.existsSync)(at(direction)))) {
|
|
9437
9553
|
roles = [...set];
|
|
9438
9554
|
break;
|
|
9439
9555
|
}
|
|
9440
9556
|
}
|
|
9441
9557
|
if (!roles.length) {
|
|
9442
|
-
for (let index = 0; (0,
|
|
9558
|
+
for (let index = 0; (0, import_node_fs6.existsSync)(at(`frame-${String(index).padStart(2, "0")}`)); index++) {
|
|
9443
9559
|
roles.push(`frame-${String(index).padStart(2, "0")}`);
|
|
9444
9560
|
}
|
|
9445
9561
|
}
|
|
@@ -9465,7 +9581,7 @@ async function resolveStyleImages(loaded, styleId) {
|
|
|
9465
9581
|
if (!style) throw new Error(`Unknown style "${styleId}"`);
|
|
9466
9582
|
const out = [];
|
|
9467
9583
|
for (const img of style.styleImages) {
|
|
9468
|
-
const file =
|
|
9584
|
+
const file = import_node_path6.default.resolve(loaded.root, img.path);
|
|
9469
9585
|
const buf = await (0, import_promises3.readFile)(file);
|
|
9470
9586
|
const metadata = imageMetadata(buf);
|
|
9471
9587
|
if (!metadata) throw new Error(`Style image is not a readable PNG or JPEG: ${file}`);
|
|
@@ -9496,7 +9612,7 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
9496
9612
|
};
|
|
9497
9613
|
if (asset.concept) {
|
|
9498
9614
|
const image = await files.load(asset.concept, "Concept image");
|
|
9499
|
-
shape.concept = { path:
|
|
9615
|
+
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
9616
|
}
|
|
9501
9617
|
if (asset.reference) {
|
|
9502
9618
|
const byDirection = typeof asset.reference === "string" ? { south: asset.reference } : asset.reference;
|
|
@@ -9504,7 +9620,7 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
9504
9620
|
for (const [direction, rel] of Object.entries(byDirection)) {
|
|
9505
9621
|
const image = await files.load(rel, `Reference sprite (${direction})`);
|
|
9506
9622
|
shape.reference[direction] = {
|
|
9507
|
-
path:
|
|
9623
|
+
path: import_node_path6.default.resolve(files.root, rel),
|
|
9508
9624
|
sha256: image.hash,
|
|
9509
9625
|
width: image.width,
|
|
9510
9626
|
height: image.height,
|
|
@@ -9525,7 +9641,7 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
9525
9641
|
const animationMode = asset.animation.mode ?? (asset.animation.template ? "template" : "v3");
|
|
9526
9642
|
const pose = async (rel, what) => {
|
|
9527
9643
|
const image = await files.load(rel, what);
|
|
9528
|
-
return { path:
|
|
9644
|
+
return { path: import_node_path6.default.resolve(files.root, rel), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
9529
9645
|
};
|
|
9530
9646
|
shape.animation = {
|
|
9531
9647
|
mode: animationMode,
|
|
@@ -9556,7 +9672,7 @@ async function resolveObjectProShape(assetId, asset, style, kind, files) {
|
|
|
9556
9672
|
const south = typeof asset.reference === "string" ? asset.reference : asset.reference.south;
|
|
9557
9673
|
if (south) {
|
|
9558
9674
|
const image = await files.load(south, "Reference sprite");
|
|
9559
|
-
shape.reference = { path:
|
|
9675
|
+
shape.reference = { path: import_node_path6.default.resolve(files.root, south), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
9560
9676
|
}
|
|
9561
9677
|
}
|
|
9562
9678
|
if (asset.state) {
|
|
@@ -9584,7 +9700,7 @@ async function resolveObjectProShape(assetId, asset, style, kind, files) {
|
|
|
9584
9700
|
}
|
|
9585
9701
|
const pose = async (rel, what) => {
|
|
9586
9702
|
const image = await files.load(rel, what);
|
|
9587
|
-
return { path:
|
|
9703
|
+
return { path: import_node_path6.default.resolve(files.root, rel), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
9588
9704
|
};
|
|
9589
9705
|
shape.animation = {
|
|
9590
9706
|
mode: asset.animation.mode ?? "v3",
|
|
@@ -9604,11 +9720,11 @@ function characterDirections(directions) {
|
|
|
9604
9720
|
}
|
|
9605
9721
|
|
|
9606
9722
|
// src/project.ts
|
|
9607
|
-
var
|
|
9723
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
9608
9724
|
|
|
9609
9725
|
// src/env.ts
|
|
9610
|
-
var
|
|
9611
|
-
var
|
|
9726
|
+
var import_node_fs7 = require("fs");
|
|
9727
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
9612
9728
|
function loadEnvFiles(dir) {
|
|
9613
9729
|
const loaded = [];
|
|
9614
9730
|
for (const [file, vars] of envFiles(dir)) {
|
|
@@ -9622,10 +9738,10 @@ function loadEnvFiles(dir) {
|
|
|
9622
9738
|
function envFiles(dir) {
|
|
9623
9739
|
const out = [];
|
|
9624
9740
|
for (const name of [".env.local", ".env"]) {
|
|
9625
|
-
const file =
|
|
9626
|
-
if (!(0,
|
|
9741
|
+
const file = import_node_path7.default.join(dir, name);
|
|
9742
|
+
if (!(0, import_node_fs7.existsSync)(file)) continue;
|
|
9627
9743
|
try {
|
|
9628
|
-
out.push([file, parseEnv((0,
|
|
9744
|
+
out.push([file, parseEnv((0, import_node_fs7.readFileSync)(file, "utf8"))]);
|
|
9629
9745
|
} catch {
|
|
9630
9746
|
}
|
|
9631
9747
|
}
|
|
@@ -9651,12 +9767,12 @@ function parseEnv(contents) {
|
|
|
9651
9767
|
}
|
|
9652
9768
|
|
|
9653
9769
|
// src/pipeline/plan.ts
|
|
9654
|
-
var
|
|
9655
|
-
var
|
|
9770
|
+
var import_node_fs10 = require("fs");
|
|
9771
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
9656
9772
|
|
|
9657
9773
|
// src/pipeline/mirror.ts
|
|
9658
9774
|
var import_promises4 = require("fs/promises");
|
|
9659
|
-
var
|
|
9775
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
9660
9776
|
|
|
9661
9777
|
// src/palette.ts
|
|
9662
9778
|
function normalizePalette(values) {
|
|
@@ -9795,7 +9911,7 @@ async function deriveMirror(spec, lock, lockPath, opts = {}) {
|
|
|
9795
9911
|
for (const [index, member] of members.entries()) {
|
|
9796
9912
|
const bytes = flipHorizontal(await (0, import_promises4.readFile)(member.file));
|
|
9797
9913
|
const target = expectedOutputPath(spec, member.role, index, members.length, MediaType.PNG);
|
|
9798
|
-
await (0, import_promises4.mkdir)(
|
|
9914
|
+
await (0, import_promises4.mkdir)(import_node_path8.default.dirname(target), { recursive: true });
|
|
9799
9915
|
const tmp = `${target}.pixelkiln.tmp`;
|
|
9800
9916
|
await (0, import_promises4.writeFile)(tmp, bytes);
|
|
9801
9917
|
await (0, import_promises4.rename)(tmp, target);
|
|
@@ -9857,29 +9973,29 @@ async function deriveMirror(spec, lock, lockPath, opts = {}) {
|
|
|
9857
9973
|
}
|
|
9858
9974
|
|
|
9859
9975
|
// src/pipeline/quality-profile.ts
|
|
9860
|
-
var
|
|
9861
|
-
var
|
|
9976
|
+
var import_node_fs8 = require("fs");
|
|
9977
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
9862
9978
|
|
|
9863
9979
|
// src/pipeline/refine.ts
|
|
9864
|
-
var
|
|
9980
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
9865
9981
|
var import_node_child_process2 = require("child_process");
|
|
9866
9982
|
var import_promises6 = require("fs/promises");
|
|
9867
|
-
var
|
|
9983
|
+
var import_node_os3 = require("os");
|
|
9868
9984
|
var import_node_util = require("util");
|
|
9869
9985
|
|
|
9870
9986
|
// src/artifacts.ts
|
|
9871
|
-
var
|
|
9872
|
-
var
|
|
9987
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
9988
|
+
var import_node_crypto6 = require("crypto");
|
|
9873
9989
|
var import_promises5 = require("fs/promises");
|
|
9874
9990
|
var activeTransactions = /* @__PURE__ */ new Set();
|
|
9875
9991
|
function message(error) {
|
|
9876
9992
|
return error instanceof Error ? error.message : String(error);
|
|
9877
9993
|
}
|
|
9878
9994
|
function digest(data) {
|
|
9879
|
-
return (0,
|
|
9995
|
+
return (0, import_node_crypto6.createHash)("sha256").update(data).digest("hex");
|
|
9880
9996
|
}
|
|
9881
9997
|
function portableRelative(from, to) {
|
|
9882
|
-
return
|
|
9998
|
+
return import_node_path9.default.relative(from, import_node_path9.default.resolve(to)).split(import_node_path9.default.sep).join("/") || ".";
|
|
9883
9999
|
}
|
|
9884
10000
|
function canonical2(value) {
|
|
9885
10001
|
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
@@ -9929,7 +10045,7 @@ function parseArtifactManifest(absolute, data) {
|
|
|
9929
10045
|
return raw;
|
|
9930
10046
|
}
|
|
9931
10047
|
async function readArtifactBundleManifest(manifestPath) {
|
|
9932
|
-
const absolute =
|
|
10048
|
+
const absolute = import_node_path9.default.resolve(manifestPath);
|
|
9933
10049
|
return parseArtifactManifest(absolute, await (0, import_promises5.readFile)(absolute, "utf8"));
|
|
9934
10050
|
}
|
|
9935
10051
|
async function readOptional(file) {
|
|
@@ -9953,8 +10069,8 @@ function processIsAlive(pid) {
|
|
|
9953
10069
|
}
|
|
9954
10070
|
}
|
|
9955
10071
|
function validTemporaryPath(candidate, destination, type) {
|
|
9956
|
-
return
|
|
9957
|
-
`.${
|
|
10072
|
+
return import_node_path9.default.dirname(candidate) === import_node_path9.default.dirname(destination) && import_node_path9.default.basename(candidate).startsWith(
|
|
10073
|
+
`.${import_node_path9.default.basename(destination)}.pixelkiln-${type}-`
|
|
9958
10074
|
);
|
|
9959
10075
|
}
|
|
9960
10076
|
function parseTransaction(journal, data) {
|
|
@@ -9974,7 +10090,7 @@ async function removeTransactionFiles(journal) {
|
|
|
9974
10090
|
await (0, import_promises5.rm)(transactionMarker(journal), { force: true });
|
|
9975
10091
|
}
|
|
9976
10092
|
async function recoverArtifactTransaction(recoveryFile, allowedDestinations) {
|
|
9977
|
-
const journal =
|
|
10093
|
+
const journal = import_node_path9.default.resolve(recoveryFile);
|
|
9978
10094
|
const bytes = await readOptional(journal);
|
|
9979
10095
|
if (!bytes) {
|
|
9980
10096
|
await (0, import_promises5.rm)(transactionMarker(journal), { force: true });
|
|
@@ -9990,7 +10106,7 @@ async function recoverArtifactTransaction(recoveryFile, allowedDestinations) {
|
|
|
9990
10106
|
);
|
|
9991
10107
|
}
|
|
9992
10108
|
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(
|
|
10109
|
+
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
10110
|
throw new Error(
|
|
9995
10111
|
`Refusing unsafe artifact recovery from ${journal}; its destinations or temporary paths do not match the current bundle.`
|
|
9996
10112
|
);
|
|
@@ -10036,7 +10152,7 @@ async function recoverArtifactTransaction(recoveryFile, allowedDestinations) {
|
|
|
10036
10152
|
await removeTransactionFiles(journal);
|
|
10037
10153
|
}
|
|
10038
10154
|
function createArtifactBundleManifest(manifestPath, outputs, provenance) {
|
|
10039
|
-
const root =
|
|
10155
|
+
const root = import_node_path9.default.dirname(import_node_path9.default.resolve(manifestPath));
|
|
10040
10156
|
const sources = provenance.sources.map((source) => ({
|
|
10041
10157
|
...source,
|
|
10042
10158
|
path: portableRelative(root, source.path)
|
|
@@ -10066,11 +10182,11 @@ function withArtifactManifest(manifestPath, outputs, provenance) {
|
|
|
10066
10182
|
];
|
|
10067
10183
|
}
|
|
10068
10184
|
async function writeManagedArtifactBundle(manifestPath, outputs, provenance, options = {}) {
|
|
10069
|
-
const absoluteManifest =
|
|
10185
|
+
const absoluteManifest = import_node_path9.default.resolve(manifestPath);
|
|
10070
10186
|
const recoveryFile = `${absoluteManifest}.transaction`;
|
|
10071
10187
|
const allowedDestinations = /* @__PURE__ */ new Set([
|
|
10072
10188
|
absoluteManifest,
|
|
10073
|
-
...outputs.map((output) =>
|
|
10189
|
+
...outputs.map((output) => import_node_path9.default.resolve(output.path))
|
|
10074
10190
|
]);
|
|
10075
10191
|
await recoverArtifactTransaction(recoveryFile, allowedDestinations);
|
|
10076
10192
|
const existingManifestBytes = await readOptional(absoluteManifest);
|
|
@@ -10088,13 +10204,13 @@ async function writeManagedArtifactBundle(manifestPath, outputs, provenance, opt
|
|
|
10088
10204
|
}
|
|
10089
10205
|
}
|
|
10090
10206
|
if (!options.force) {
|
|
10091
|
-
const root =
|
|
10207
|
+
const root = import_node_path9.default.dirname(absoluteManifest);
|
|
10092
10208
|
const recorded = new Map(
|
|
10093
|
-
previous?.outputs.map((output) => [
|
|
10209
|
+
previous?.outputs.map((output) => [import_node_path9.default.resolve(root, output.path), output.sha256]) ?? []
|
|
10094
10210
|
);
|
|
10095
10211
|
const conflicts = [];
|
|
10096
10212
|
for (const output of outputs) {
|
|
10097
|
-
const destination =
|
|
10213
|
+
const destination = import_node_path9.default.resolve(output.path);
|
|
10098
10214
|
const current = await readOptional(destination);
|
|
10099
10215
|
if (!current || digest(current) === digest(output.data)) continue;
|
|
10100
10216
|
const expected = recorded.get(destination);
|
|
@@ -10116,15 +10232,15 @@ Pass { force: true } (CLI: --force) to take ownership and replace the complete b
|
|
|
10116
10232
|
);
|
|
10117
10233
|
}
|
|
10118
10234
|
async function verifyArtifactBundle(manifestPath) {
|
|
10119
|
-
const absolute =
|
|
10235
|
+
const absolute = import_node_path9.default.resolve(manifestPath);
|
|
10120
10236
|
const raw = await readArtifactBundleManifest(absolute);
|
|
10121
|
-
const root =
|
|
10237
|
+
const root = import_node_path9.default.dirname(absolute);
|
|
10122
10238
|
const changedSources = [];
|
|
10123
10239
|
const changedOutputs = [];
|
|
10124
10240
|
for (const source of raw.sources) {
|
|
10125
10241
|
let actual = null;
|
|
10126
10242
|
try {
|
|
10127
|
-
actual = digest(await (0, import_promises5.readFile)(
|
|
10243
|
+
actual = digest(await (0, import_promises5.readFile)(import_node_path9.default.resolve(root, source.path)));
|
|
10128
10244
|
} catch (error) {
|
|
10129
10245
|
if (!isCode(error, "ENOENT")) throw error;
|
|
10130
10246
|
}
|
|
@@ -10132,7 +10248,7 @@ async function verifyArtifactBundle(manifestPath) {
|
|
|
10132
10248
|
}
|
|
10133
10249
|
for (const output of raw.outputs) {
|
|
10134
10250
|
try {
|
|
10135
|
-
if (digest(await (0, import_promises5.readFile)(
|
|
10251
|
+
if (digest(await (0, import_promises5.readFile)(import_node_path9.default.resolve(root, output.path))) !== output.sha256) {
|
|
10136
10252
|
changedOutputs.push(output.path);
|
|
10137
10253
|
}
|
|
10138
10254
|
} catch (error) {
|
|
@@ -10188,7 +10304,7 @@ async function writeArtifactBundle(files, options = {}) {
|
|
|
10188
10304
|
if (!files.length) throw new Error("An artifact bundle must contain at least one file.");
|
|
10189
10305
|
const normalized = files.map((file) => {
|
|
10190
10306
|
if (!file.path.trim()) throw new Error("Artifact paths cannot be empty.");
|
|
10191
|
-
return { destination:
|
|
10307
|
+
return { destination: import_node_path9.default.resolve(file.path), data: Buffer.from(file.data) };
|
|
10192
10308
|
});
|
|
10193
10309
|
const destinations = /* @__PURE__ */ new Set();
|
|
10194
10310
|
for (const artifact of normalized) {
|
|
@@ -10216,23 +10332,23 @@ async function writeArtifactBundle(files, options = {}) {
|
|
|
10216
10332
|
}
|
|
10217
10333
|
}
|
|
10218
10334
|
if (!changed.length) return { changed: [], unchanged };
|
|
10219
|
-
const token = (0,
|
|
10335
|
+
const token = (0, import_node_crypto6.randomUUID)();
|
|
10220
10336
|
for (const [index, artifact] of changed.entries()) {
|
|
10221
|
-
const basename =
|
|
10222
|
-
artifact.stage =
|
|
10223
|
-
|
|
10337
|
+
const basename = import_node_path9.default.basename(artifact.destination);
|
|
10338
|
+
artifact.stage = import_node_path9.default.join(
|
|
10339
|
+
import_node_path9.default.dirname(artifact.destination),
|
|
10224
10340
|
`.${basename}.pixelkiln-stage-${token}-${index}`
|
|
10225
10341
|
);
|
|
10226
10342
|
if (artifact.existed) {
|
|
10227
|
-
artifact.backup =
|
|
10228
|
-
|
|
10343
|
+
artifact.backup = import_node_path9.default.join(
|
|
10344
|
+
import_node_path9.default.dirname(artifact.destination),
|
|
10229
10345
|
`.${basename}.pixelkiln-backup-${token}-${index}`
|
|
10230
10346
|
);
|
|
10231
10347
|
}
|
|
10232
10348
|
}
|
|
10233
|
-
const journal = options.recoveryFile ?
|
|
10349
|
+
const journal = options.recoveryFile ? import_node_path9.default.resolve(options.recoveryFile) : null;
|
|
10234
10350
|
if (journal) {
|
|
10235
|
-
await (0, import_promises5.mkdir)(
|
|
10351
|
+
await (0, import_promises5.mkdir)(import_node_path9.default.dirname(journal), { recursive: true });
|
|
10236
10352
|
const transaction = {
|
|
10237
10353
|
format: "pixelkiln-artifact-transaction",
|
|
10238
10354
|
version: 1,
|
|
@@ -10257,7 +10373,7 @@ async function writeArtifactBundle(files, options = {}) {
|
|
|
10257
10373
|
}
|
|
10258
10374
|
try {
|
|
10259
10375
|
for (const [index, artifact] of changed.entries()) {
|
|
10260
|
-
await (0, import_promises5.mkdir)(
|
|
10376
|
+
await (0, import_promises5.mkdir)(import_node_path9.default.dirname(artifact.destination), { recursive: true });
|
|
10261
10377
|
await options.beforeStage?.(artifact.destination, index);
|
|
10262
10378
|
await (0, import_promises5.writeFile)(artifact.stage, artifact.data, { flag: "wx" });
|
|
10263
10379
|
}
|
|
@@ -10523,10 +10639,10 @@ function sameFrameGrid(first, next) {
|
|
|
10523
10639
|
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
10640
|
}
|
|
10525
10641
|
async function refineAsset(options) {
|
|
10526
|
-
const source =
|
|
10527
|
-
const output =
|
|
10642
|
+
const source = import_node_path10.default.resolve(options.source);
|
|
10643
|
+
const output = import_node_path10.default.resolve(options.output);
|
|
10528
10644
|
if (source === output) throw new Error("Refinement source and output must be different files.");
|
|
10529
|
-
const record =
|
|
10645
|
+
const record = import_node_path10.default.resolve(qualityRecordPath(output));
|
|
10530
10646
|
const palette = normalizePalette(options.palette).map((color) => color.hex);
|
|
10531
10647
|
const minGridConfidence = requireConfidence(options.minGridConfidence ?? "high");
|
|
10532
10648
|
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 +10665,8 @@ async function refineAsset(options) {
|
|
|
10549
10665
|
);
|
|
10550
10666
|
}
|
|
10551
10667
|
}
|
|
10552
|
-
const temp = await (0, import_promises6.mkdtemp)(
|
|
10553
|
-
const recovered =
|
|
10668
|
+
const temp = await (0, import_promises6.mkdtemp)(import_node_path10.default.join((0, import_node_os3.tmpdir)(), "pixelkiln-refine-"));
|
|
10669
|
+
const recovered = import_node_path10.default.join(temp, "native.png");
|
|
10554
10670
|
const fixerCommand = compatibilityCommand ?? options.fixerPython ?? process.env.PIXELKILN_PIXEL_FIXER_PYTHON ?? "python3";
|
|
10555
10671
|
const args = compatibilityCommand ? [...options.fixerArgsPrefix ?? [], source, "--extract", recovered] : ["-c", PYTHON_FIXER_BRIDGE, source, recovered];
|
|
10556
10672
|
try {
|
|
@@ -10635,18 +10751,18 @@ async function refineFrameSet(options) {
|
|
|
10635
10751
|
if (roles.some((role) => !role.trim()) || new Set(roles).size !== roles.length) {
|
|
10636
10752
|
throw new Error("Frame-set roles must be non-empty and unique.");
|
|
10637
10753
|
}
|
|
10638
|
-
const output =
|
|
10639
|
-
const record =
|
|
10754
|
+
const output = import_node_path10.default.resolve(options.output);
|
|
10755
|
+
const record = import_node_path10.default.resolve(qualityRecordPath(output));
|
|
10640
10756
|
const palette = normalizeRefinementPalette(options.palette);
|
|
10641
10757
|
const finalOutputs = options.sources.map((source, index) => qualityFrameOutputPath(output, source.role, index));
|
|
10642
10758
|
if (new Set(finalOutputs).size !== finalOutputs.length) {
|
|
10643
10759
|
throw new Error("Frame-set roles resolve to duplicate output filenames.");
|
|
10644
10760
|
}
|
|
10645
|
-
const sourceSet = new Set(options.sources.map((source) =>
|
|
10761
|
+
const sourceSet = new Set(options.sources.map((source) => import_node_path10.default.resolve(source.path)));
|
|
10646
10762
|
if (finalOutputs.some((file) => sourceSet.has(file))) {
|
|
10647
10763
|
throw new Error("Frame-set refinement sources and outputs must be different files.");
|
|
10648
10764
|
}
|
|
10649
|
-
const temp = await (0, import_promises6.mkdtemp)(
|
|
10765
|
+
const temp = await (0, import_promises6.mkdtemp)(import_node_path10.default.join((0, import_node_os3.tmpdir)(), "pixelkiln-refine-frames-"));
|
|
10650
10766
|
try {
|
|
10651
10767
|
const frames = [];
|
|
10652
10768
|
const outputs = [];
|
|
@@ -10654,8 +10770,8 @@ async function refineFrameSet(options) {
|
|
|
10654
10770
|
const sourcePaths = [];
|
|
10655
10771
|
let firstOptions = null;
|
|
10656
10772
|
for (const [index, sourceEntry] of options.sources.entries()) {
|
|
10657
|
-
const source =
|
|
10658
|
-
const temporaryOutput =
|
|
10773
|
+
const source = import_node_path10.default.resolve(sourceEntry.path);
|
|
10774
|
+
const temporaryOutput = import_node_path10.default.join(temp, `${String(index).padStart(2, "0")}.png`);
|
|
10659
10775
|
const refined = await refineAsset({
|
|
10660
10776
|
source,
|
|
10661
10777
|
output: temporaryOutput,
|
|
@@ -10730,7 +10846,7 @@ async function refineFrameSet(options) {
|
|
|
10730
10846
|
);
|
|
10731
10847
|
return {
|
|
10732
10848
|
sources: sourcePaths,
|
|
10733
|
-
outputs: outputs.map((item) =>
|
|
10849
|
+
outputs: outputs.map((item) => import_node_path10.default.resolve(item.path)),
|
|
10734
10850
|
record,
|
|
10735
10851
|
frames,
|
|
10736
10852
|
palette,
|
|
@@ -10741,12 +10857,12 @@ async function refineFrameSet(options) {
|
|
|
10741
10857
|
}
|
|
10742
10858
|
}
|
|
10743
10859
|
async function checkQualityRecord(recordPath) {
|
|
10744
|
-
const record =
|
|
10860
|
+
const record = import_node_path10.default.resolve(recordPath);
|
|
10745
10861
|
const manifest = await readArtifactBundleManifest(record);
|
|
10746
10862
|
const options = parseRecordOptions(manifest, record);
|
|
10747
10863
|
const verification = await verifyArtifactBundle(record);
|
|
10748
|
-
const sources = manifest.sources.map((source2) =>
|
|
10749
|
-
const outputs = manifest.outputs.map((output2) =>
|
|
10864
|
+
const sources = manifest.sources.map((source2) => import_node_path10.default.resolve(import_node_path10.default.dirname(record), source2.path));
|
|
10865
|
+
const outputs = manifest.outputs.map((output2) => import_node_path10.default.resolve(import_node_path10.default.dirname(record), output2.path));
|
|
10750
10866
|
const source = sources[0] ?? "";
|
|
10751
10867
|
const output = outputs[0] ?? "";
|
|
10752
10868
|
const expectedCount = options.frameSet?.count ?? 1;
|
|
@@ -10779,7 +10895,7 @@ async function checkQualityRecord(recordPath) {
|
|
|
10779
10895
|
async function approveQualityRecord(recordPath, approval) {
|
|
10780
10896
|
const reviewer = approval.reviewer.trim();
|
|
10781
10897
|
if (!reviewer) throw new Error("A human reviewer name is required.");
|
|
10782
|
-
const record =
|
|
10898
|
+
const record = import_node_path10.default.resolve(recordPath);
|
|
10783
10899
|
const manifest = await readArtifactBundleManifest(record);
|
|
10784
10900
|
const options = parseRecordOptions(manifest, record);
|
|
10785
10901
|
const verification = await verifyArtifactBundle(record);
|
|
@@ -10800,14 +10916,14 @@ async function approveQualityRecord(recordPath, approval) {
|
|
|
10800
10916
|
`A refinement record must contain exactly ${expectedCount} source(s) and output(s).`
|
|
10801
10917
|
);
|
|
10802
10918
|
}
|
|
10803
|
-
const root =
|
|
10919
|
+
const root = import_node_path10.default.dirname(record);
|
|
10804
10920
|
const outputs = await Promise.all(manifest.outputs.map(async (entry) => {
|
|
10805
|
-
const outputPath =
|
|
10921
|
+
const outputPath = import_node_path10.default.resolve(root, entry.path);
|
|
10806
10922
|
return { path: outputPath, data: await (0, import_promises6.readFile)(outputPath) };
|
|
10807
10923
|
}));
|
|
10808
10924
|
const sources = manifest.sources.map((source) => ({
|
|
10809
10925
|
...source,
|
|
10810
|
-
path:
|
|
10926
|
+
path: import_node_path10.default.resolve(root, source.path)
|
|
10811
10927
|
}));
|
|
10812
10928
|
const approved = {
|
|
10813
10929
|
...options,
|
|
@@ -10839,8 +10955,8 @@ async function qualitySource(spec, lock) {
|
|
|
10839
10955
|
if (spec.generator === "frames") {
|
|
10840
10956
|
return { items: null, reason: "frame-set quality requires ordered downloaded provider outputs" };
|
|
10841
10957
|
}
|
|
10842
|
-
const source =
|
|
10843
|
-
return (0,
|
|
10958
|
+
const source = import_node_path11.default.resolve(spec.root, spec.source);
|
|
10959
|
+
return (0, import_node_fs8.existsSync)(source) ? { items: [{ role: "source", path: source }] } : { items: null, reason: `declared source is missing: ${spec.source}` };
|
|
10844
10960
|
}
|
|
10845
10961
|
const entry = lock.entries[lockKey(spec.styleId, spec.assetId)];
|
|
10846
10962
|
if (!entry) return { items: null, reason: "raw provider output is not in the lockfile" };
|
|
@@ -10869,7 +10985,7 @@ async function qualitySource(spec, lock) {
|
|
|
10869
10985
|
}
|
|
10870
10986
|
if (role) roles.add(role);
|
|
10871
10987
|
const source = currentEntryOutputPath(entry, spec, index);
|
|
10872
|
-
if (!(0,
|
|
10988
|
+
if (!(0, import_node_fs8.existsSync)(source)) return { items: null, reason: `raw provider output is missing: ${source}` };
|
|
10873
10989
|
if (await sha256File(source) !== output.sha256) {
|
|
10874
10990
|
return { items: null, reason: "raw provider output was modified after download" };
|
|
10875
10991
|
}
|
|
@@ -10888,7 +11004,7 @@ async function inspectQualityProfile(spec, lock) {
|
|
|
10888
11004
|
}
|
|
10889
11005
|
const sourcePaths = source.items.map((item) => item.path);
|
|
10890
11006
|
const expectedOutputs = spec.generator === "frames" ? source.items.map((item, index) => qualityFrameOutputPath(output, item.role, index)) : [output];
|
|
10891
|
-
if (!(0,
|
|
11007
|
+
if (!(0, import_node_fs8.existsSync)(record)) {
|
|
10892
11008
|
return {
|
|
10893
11009
|
key,
|
|
10894
11010
|
state: "needs-refinement",
|
|
@@ -10906,7 +11022,7 @@ async function inspectQualityProfile(spec, lock) {
|
|
|
10906
11022
|
const expectedRevision = spec.quality.fixerRevision ?? PIXEL_ART_FIXER_REVISION;
|
|
10907
11023
|
const expectedTransparency = spec.quality.minTransparency ?? null;
|
|
10908
11024
|
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) =>
|
|
11025
|
+
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
11026
|
if (contractChanged) {
|
|
10911
11027
|
return {
|
|
10912
11028
|
key,
|
|
@@ -11069,8 +11185,8 @@ async function requireApprovedQualitySources(specs, lock, assetIds, outputRoles)
|
|
|
11069
11185
|
}
|
|
11070
11186
|
|
|
11071
11187
|
// src/pipeline/revision.ts
|
|
11072
|
-
var
|
|
11073
|
-
var
|
|
11188
|
+
var import_node_fs9 = require("fs");
|
|
11189
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
11074
11190
|
async function inspectRevisionReadiness(spec, lock) {
|
|
11075
11191
|
if (spec.mirror) return inspectMirror(spec, lock, /* @__PURE__ */ new Set());
|
|
11076
11192
|
if (spec.character?.kind === "outfit") return inspectOutfit(spec, lock, /* @__PURE__ */ new Set());
|
|
@@ -11086,7 +11202,7 @@ async function inspectStyleAnchor(spec, lock, seen) {
|
|
|
11086
11202
|
const label = `style anchor ${spec.styleId}/${anchor.assetId}`;
|
|
11087
11203
|
const dependency = await inspectParent(anchor.spec, lock, seen);
|
|
11088
11204
|
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
11089
|
-
if (!anchor.sha256 || !(0,
|
|
11205
|
+
if (!anchor.sha256 || !(0, import_node_fs9.existsSync)(anchor.file)) return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
11090
11206
|
if (await sha256File(anchor.file) !== anchor.sha256) {
|
|
11091
11207
|
return { ready: false, reason: `${label} changed after the manifest was resolved` };
|
|
11092
11208
|
}
|
|
@@ -11100,7 +11216,7 @@ async function inspectScene(spec) {
|
|
|
11100
11216
|
const files = [{ label: "scene image", file: scene.file, sha256: scene.sha256 }];
|
|
11101
11217
|
if (scene.placement.type === "mask") files.push({ label: "scene mask", file: scene.placement.file, sha256: scene.placement.sha256 });
|
|
11102
11218
|
for (const { label, file, sha256: sha2562 } of files) {
|
|
11103
|
-
if (!sha2562 || !(0,
|
|
11219
|
+
if (!sha2562 || !(0, import_node_fs9.existsSync)(file)) return { ready: false, reason: `${label} does not exist yet: ${file}` };
|
|
11104
11220
|
if (await sha256File(file) !== sha2562) return { ready: false, reason: `${label} changed after the manifest was resolved` };
|
|
11105
11221
|
}
|
|
11106
11222
|
return { ready: true, reason: "scene is current" };
|
|
@@ -11117,7 +11233,7 @@ async function inspectOutfit(spec, lock, seen) {
|
|
|
11117
11233
|
const label = `outfit source ${spec.styleId}/${character.parentAssetId}`;
|
|
11118
11234
|
const dependency = await inspectParent(character.parentSpec, lock, seen);
|
|
11119
11235
|
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
11120
|
-
if (!(0,
|
|
11236
|
+
if (!(0, import_node_fs9.existsSync)(outfit.reference.path)) {
|
|
11121
11237
|
return { ready: false, reason: `outfit reference is missing: ${outfit.reference.path}` };
|
|
11122
11238
|
}
|
|
11123
11239
|
if (await sha256File(outfit.reference.path) !== outfit.reference.sha256) {
|
|
@@ -11131,7 +11247,7 @@ async function inspectCharacterParent(spec, lock, seen) {
|
|
|
11131
11247
|
const label = `${character.kind} parent ${spec.styleId}/${character.parentAssetId}`;
|
|
11132
11248
|
const dependency = await inspectParent(parentSpec, lock, seen);
|
|
11133
11249
|
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
11134
|
-
if (!character.parentSha256 || !character.parentFile || !(0,
|
|
11250
|
+
if (!character.parentSha256 || !character.parentFile || !(0, import_node_fs9.existsSync)(character.parentFile)) {
|
|
11135
11251
|
return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
11136
11252
|
}
|
|
11137
11253
|
if (await sha256File(character.parentFile) !== character.parentSha256) {
|
|
@@ -11149,7 +11265,7 @@ async function inspectObjectProParent(spec, lock, seen) {
|
|
|
11149
11265
|
const label = `${object.kind} parent ${spec.styleId}/${object.parentAssetId}`;
|
|
11150
11266
|
const dependency = await inspectParent(parentSpec, lock, seen);
|
|
11151
11267
|
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
11152
|
-
if (!object.parentSha256 || !object.parentFile || !(0,
|
|
11268
|
+
if (!object.parentSha256 || !object.parentFile || !(0, import_node_fs9.existsSync)(object.parentFile)) {
|
|
11153
11269
|
return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
11154
11270
|
}
|
|
11155
11271
|
if (await sha256File(object.parentFile) !== object.parentSha256) {
|
|
@@ -11175,7 +11291,7 @@ async function inspectRevision(spec, lock, seen) {
|
|
|
11175
11291
|
const members = await inspectSourceMembers(spec, lock);
|
|
11176
11292
|
if (!members.ready) return members;
|
|
11177
11293
|
} else {
|
|
11178
|
-
if (!revision.sourceSha256 || !(0,
|
|
11294
|
+
if (!revision.sourceSha256 || !(0, import_node_fs9.existsSync)(revision.sourceFile)) {
|
|
11179
11295
|
return { ready: false, reason: `revision source is missing: ${revision.sourceFile}` };
|
|
11180
11296
|
}
|
|
11181
11297
|
if (await sha256File(revision.sourceFile) !== revision.sourceSha256) {
|
|
@@ -11183,7 +11299,7 @@ async function inspectRevision(spec, lock, seen) {
|
|
|
11183
11299
|
}
|
|
11184
11300
|
}
|
|
11185
11301
|
if (revision.maskFile) {
|
|
11186
|
-
if (!revision.maskSha256 || !(0,
|
|
11302
|
+
if (!revision.maskSha256 || !(0, import_node_fs9.existsSync)(revision.maskFile)) {
|
|
11187
11303
|
return { ready: false, reason: `revision mask is missing: ${revision.maskFile}` };
|
|
11188
11304
|
}
|
|
11189
11305
|
if (await sha256File(revision.maskFile) !== revision.maskSha256) {
|
|
@@ -11191,7 +11307,7 @@ async function inspectRevision(spec, lock, seen) {
|
|
|
11191
11307
|
}
|
|
11192
11308
|
}
|
|
11193
11309
|
if (revision.lastFrameFile) {
|
|
11194
|
-
if (!revision.lastFrameSha256 || !(0,
|
|
11310
|
+
if (!revision.lastFrameSha256 || !(0, import_node_fs9.existsSync)(revision.lastFrameFile)) {
|
|
11195
11311
|
return { ready: false, reason: `revision last frame is missing: ${revision.lastFrameFile}` };
|
|
11196
11312
|
}
|
|
11197
11313
|
if (await sha256File(revision.lastFrameFile) !== revision.lastFrameSha256) {
|
|
@@ -11204,7 +11320,7 @@ async function inspectSourceMembers(spec, lock) {
|
|
|
11204
11320
|
const revision = spec.revision;
|
|
11205
11321
|
const members = revision.sourceMembers;
|
|
11206
11322
|
for (const member of members) {
|
|
11207
|
-
if (!(0,
|
|
11323
|
+
if (!(0, import_node_fs9.existsSync)(member.file)) return { ready: false, reason: `revision source member is missing: ${member.file}` };
|
|
11208
11324
|
if (await sha256File(member.file) !== member.sha256) {
|
|
11209
11325
|
return { ready: false, reason: `revision source member changed after the manifest was resolved: ${member.file}` };
|
|
11210
11326
|
}
|
|
@@ -11213,8 +11329,8 @@ async function inspectSourceMembers(spec, lock) {
|
|
|
11213
11329
|
if (parent.source) return { ready: true, reason: "declared source set is present" };
|
|
11214
11330
|
const entry = lock.entries[lockKey(parent.styleId, parent.assetId)];
|
|
11215
11331
|
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) =>
|
|
11332
|
+
const recorded = entry.outputs.map((_, index) => import_node_path12.default.resolve(currentEntryOutputPath(entry, parent, index))).sort();
|
|
11333
|
+
const found = members.map((member) => import_node_path12.default.resolve(member.file)).sort();
|
|
11218
11334
|
if (recorded.length !== found.length || recorded.some((file, index) => file !== found[index])) {
|
|
11219
11335
|
return {
|
|
11220
11336
|
ready: false,
|
|
@@ -11263,8 +11379,8 @@ async function inspectParent(spec, lock, seen) {
|
|
|
11263
11379
|
};
|
|
11264
11380
|
}
|
|
11265
11381
|
if (spec.source) {
|
|
11266
|
-
const source =
|
|
11267
|
-
return (0,
|
|
11382
|
+
const source = import_node_path12.default.resolve(spec.root, spec.source);
|
|
11383
|
+
return (0, import_node_fs9.existsSync)(source) ? { ready: true, reason: "declared source is present" } : { ready: false, reason: `declared source is missing: ${spec.source}` };
|
|
11268
11384
|
}
|
|
11269
11385
|
const entry = lock.entries[key];
|
|
11270
11386
|
if (!entry) return { ready: false, reason: "parent is not in the lockfile" };
|
|
@@ -11278,7 +11394,7 @@ async function inspectParent(spec, lock, seen) {
|
|
|
11278
11394
|
for (let index = 0; index < entry.outputs.length; index++) {
|
|
11279
11395
|
const output = entry.outputs[index];
|
|
11280
11396
|
const file = currentEntryOutputPath(entry, spec, index);
|
|
11281
|
-
if (!(0,
|
|
11397
|
+
if (!(0, import_node_fs9.existsSync)(file)) return { ready: false, reason: `parent output is missing: ${file}` };
|
|
11282
11398
|
if (await sha256File(file) !== output.sha256) {
|
|
11283
11399
|
return { ready: false, reason: `parent output was modified: ${file}` };
|
|
11284
11400
|
}
|
|
@@ -11342,7 +11458,7 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
11342
11458
|
state = "missing";
|
|
11343
11459
|
reason = "--force";
|
|
11344
11460
|
} else if (!entry && spec.source && spec.generator !== "frames") {
|
|
11345
|
-
if ((0,
|
|
11461
|
+
if ((0, import_node_fs10.existsSync)(import_node_path13.default.resolve(spec.root, spec.source))) {
|
|
11346
11462
|
state = "ok";
|
|
11347
11463
|
reason = `placed from ${spec.source}; not generated`;
|
|
11348
11464
|
} else {
|
|
@@ -11350,7 +11466,7 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
11350
11466
|
reason = `declared source is missing: ${spec.source}`;
|
|
11351
11467
|
}
|
|
11352
11468
|
} else if (!entry) {
|
|
11353
|
-
if ((0,
|
|
11469
|
+
if ((0, import_node_fs10.existsSync)(spec.outFile)) {
|
|
11354
11470
|
state = "untracked";
|
|
11355
11471
|
reason = "file on disk, no provenance recorded";
|
|
11356
11472
|
} else {
|
|
@@ -11363,7 +11479,7 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
11363
11479
|
} else if (spec.mirror && !mirrorCurrent(entry, lock.entries[lockKey(spec.styleId, spec.mirror.sourceAssetId)])) {
|
|
11364
11480
|
state = "stale";
|
|
11365
11481
|
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,
|
|
11482
|
+
} else if (spec.mirror && entry.status === "downloaded" && entry.outputs.some((_output, index) => !(0, import_node_fs10.existsSync)(currentEntryOutputPath(entry, spec, index)))) {
|
|
11367
11483
|
state = "stale";
|
|
11368
11484
|
reason = "mirror output missing on disk; flip it again (no generation cost)";
|
|
11369
11485
|
} else if (spec.character?.kind === "outfit" && entry.outfit?.sourceSha256 !== mirrorSourceHash(lock.entries[lockKey(spec.styleId, spec.character.parentAssetId)] ?? { outputs: [] })) {
|
|
@@ -11391,9 +11507,9 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
11391
11507
|
} else if (entry.status !== "downloaded") {
|
|
11392
11508
|
state = "in-flight";
|
|
11393
11509
|
reason = `awaiting ${entry.status}`;
|
|
11394
|
-
} else if (entry.outputs.length === 0 || entry.outputs.some((_output, index) => !(0,
|
|
11510
|
+
} else if (entry.outputs.length === 0 || entry.outputs.some((_output, index) => !(0, import_node_fs10.existsSync)(currentEntryOutputPath(entry, spec, index)))) {
|
|
11395
11511
|
state = "orphaned";
|
|
11396
|
-
reason = entry.outputs.length === 0 ? "no recorded output files" : `missing on disk: ${entry.outputs.filter((_output, index) => !(0,
|
|
11512
|
+
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
11513
|
} else if (await anyOutputModified(entry, spec)) {
|
|
11398
11514
|
state = "orphaned";
|
|
11399
11515
|
reason = "output modified since download";
|
|
@@ -11484,13 +11600,13 @@ function summarize(plan) {
|
|
|
11484
11600
|
|
|
11485
11601
|
// src/project.ts
|
|
11486
11602
|
async function openProject(manifestPath = "pixelkiln.manifest.json", opts = {}) {
|
|
11487
|
-
const absolute =
|
|
11488
|
-
const root =
|
|
11603
|
+
const absolute = import_node_path14.default.resolve(manifestPath);
|
|
11604
|
+
const root = import_node_path14.default.dirname(absolute);
|
|
11489
11605
|
if (opts.env !== false) {
|
|
11490
11606
|
loadEnvFiles(root);
|
|
11491
|
-
if (
|
|
11607
|
+
if (import_node_path14.default.resolve(process.cwd()) !== root) loadEnvFiles(process.cwd());
|
|
11492
11608
|
}
|
|
11493
|
-
const lockPath =
|
|
11609
|
+
const lockPath = import_node_path14.default.resolve(opts.lockPath ?? import_node_path14.default.join(root, "pixelkiln.lock.json"));
|
|
11494
11610
|
const loaded = await loadManifest(absolute);
|
|
11495
11611
|
const specs = await resolveSpecs(loaded, { styles: opts.styles ?? [], assets: opts.assets ?? [] });
|
|
11496
11612
|
const lock = await loadLock(lockPath);
|
|
@@ -11557,8 +11673,8 @@ function shouldPersistSourceUrl(value) {
|
|
|
11557
11673
|
}
|
|
11558
11674
|
|
|
11559
11675
|
// src/recipes.ts
|
|
11560
|
-
var
|
|
11561
|
-
var
|
|
11676
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
11677
|
+
var import_node_fs11 = require("fs");
|
|
11562
11678
|
var import_promises7 = require("fs/promises");
|
|
11563
11679
|
var import_node_url = require("url");
|
|
11564
11680
|
var import_zod4 = require("zod");
|
|
@@ -11568,7 +11684,7 @@ var VERSION_RE = /^\d+\.\d+\.\d+$/;
|
|
|
11568
11684
|
var RECIPE_FILE = "pixelkiln.recipe.json";
|
|
11569
11685
|
var RECIPE_DIR_TOKEN = "{{recipeDir}}";
|
|
11570
11686
|
var PortablePathSchema = import_zod4.z.string().min(1).refine((value) => {
|
|
11571
|
-
if (value.includes("\\") ||
|
|
11687
|
+
if (value.includes("\\") || import_node_path15.default.posix.isAbsolute(value)) return false;
|
|
11572
11688
|
const parts = value.split("/");
|
|
11573
11689
|
return !parts.includes("") && !parts.includes(".") && !parts.includes("..");
|
|
11574
11690
|
}, "expected a portable relative path without '.', '..', or backslashes");
|
|
@@ -11744,7 +11860,7 @@ function message3(error) {
|
|
|
11744
11860
|
return error instanceof Error ? error.message : String(error);
|
|
11745
11861
|
}
|
|
11746
11862
|
async function readRecipeFile(recipePath, bundled) {
|
|
11747
|
-
const absolute =
|
|
11863
|
+
const absolute = import_node_path15.default.resolve(recipePath);
|
|
11748
11864
|
let raw;
|
|
11749
11865
|
try {
|
|
11750
11866
|
raw = JSON.parse(await (0, import_promises7.readFile)(absolute, "utf8"));
|
|
@@ -11758,13 +11874,13 @@ async function readRecipeFile(recipePath, bundled) {
|
|
|
11758
11874
|
` + parsed.error.issues.map((issue) => ` ${issue.path.join(".") || "$"}: ${issue.message}`).join("\n")
|
|
11759
11875
|
);
|
|
11760
11876
|
}
|
|
11761
|
-
return { recipe: parsed.data, authored: raw, path: absolute, dir:
|
|
11877
|
+
return { recipe: parsed.data, authored: raw, path: absolute, dir: import_node_path15.default.dirname(absolute), bundled };
|
|
11762
11878
|
}
|
|
11763
11879
|
async function walkRecipeFiles(root) {
|
|
11764
|
-
if (!(0,
|
|
11880
|
+
if (!(0, import_node_fs11.existsSync)(root)) return [];
|
|
11765
11881
|
const out = [];
|
|
11766
11882
|
for (const entry of await (0, import_promises7.readdir)(root, { withFileTypes: true })) {
|
|
11767
|
-
const resolved =
|
|
11883
|
+
const resolved = import_node_path15.default.join(root, entry.name);
|
|
11768
11884
|
if (entry.isDirectory()) out.push(...await walkRecipeFiles(resolved));
|
|
11769
11885
|
else if (entry.isFile() && entry.name === RECIPE_FILE) out.push(resolved);
|
|
11770
11886
|
}
|
|
@@ -11775,7 +11891,7 @@ function bundledRecipeRoot() {
|
|
|
11775
11891
|
}
|
|
11776
11892
|
async function listBundledRecipes(root = bundledRecipeRoot()) {
|
|
11777
11893
|
const recipes = await Promise.all(
|
|
11778
|
-
(await walkRecipeFiles(
|
|
11894
|
+
(await walkRecipeFiles(import_node_path15.default.resolve(root))).map((file) => readRecipeFile(file, true))
|
|
11779
11895
|
);
|
|
11780
11896
|
return recipes.sort(
|
|
11781
11897
|
(a, b) => a.recipe.id.localeCompare(b.recipe.id) || compareVersions(b.recipe.version, a.recipe.version)
|
|
@@ -11798,9 +11914,9 @@ function parseSelector(selector) {
|
|
|
11798
11914
|
return { id: match[1], version: match[2] };
|
|
11799
11915
|
}
|
|
11800
11916
|
async function resolveRecipe(target, bundledRoot = bundledRecipeRoot()) {
|
|
11801
|
-
const local =
|
|
11802
|
-
if ((0,
|
|
11803
|
-
const recipePath =
|
|
11917
|
+
const local = import_node_path15.default.resolve(target);
|
|
11918
|
+
if ((0, import_node_fs11.existsSync)(local)) {
|
|
11919
|
+
const recipePath = import_node_path15.default.basename(local) === RECIPE_FILE ? local : import_node_path15.default.join(local, RECIPE_FILE);
|
|
11804
11920
|
return readRecipeFile(recipePath, false);
|
|
11805
11921
|
}
|
|
11806
11922
|
if (target.startsWith(".") || target.startsWith("/") || target.includes("\\")) {
|
|
@@ -11814,8 +11930,8 @@ async function resolveRecipe(target, bundledRoot = bundledRecipeRoot()) {
|
|
|
11814
11930
|
return matches[0];
|
|
11815
11931
|
}
|
|
11816
11932
|
async function verifyFile(root, file) {
|
|
11817
|
-
const absolute =
|
|
11818
|
-
if (!(0,
|
|
11933
|
+
const absolute = import_node_path15.default.resolve(root, ...file.path.split("/"));
|
|
11934
|
+
if (!(0, import_node_fs11.existsSync)(absolute)) {
|
|
11819
11935
|
return { path: file.path, expectedSha256: file.sha256, actualSha256: null, status: "missing" };
|
|
11820
11936
|
}
|
|
11821
11937
|
const actualSha256 = await sha256File(absolute);
|
|
@@ -11830,7 +11946,7 @@ async function verifyRecipe(target, options = {}) {
|
|
|
11830
11946
|
const loaded = await resolveRecipe(target, options.bundledRoot);
|
|
11831
11947
|
const actualIntegrity = recipeDigest(loaded.authored);
|
|
11832
11948
|
const files = await Promise.all(loaded.recipe.files.map((file) => verifyFile(loaded.dir, file)));
|
|
11833
|
-
const modelRoot = options.modelRoot ?
|
|
11949
|
+
const modelRoot = options.modelRoot ? import_node_path15.default.resolve(options.modelRoot) : null;
|
|
11834
11950
|
const models = modelRoot ? await Promise.all(loaded.recipe.models.map(async (model) => ({
|
|
11835
11951
|
...await verifyFile(modelRoot, model),
|
|
11836
11952
|
source: model.source,
|
|
@@ -11864,7 +11980,7 @@ async function verifyRecipe(target, options = {}) {
|
|
|
11864
11980
|
};
|
|
11865
11981
|
}
|
|
11866
11982
|
function renderStyle(style, destination, cwd) {
|
|
11867
|
-
const portableDir =
|
|
11983
|
+
const portableDir = import_node_path15.default.relative(cwd, destination).split(import_node_path15.default.sep).join("/") || ".";
|
|
11868
11984
|
const replace = (value) => {
|
|
11869
11985
|
if (typeof value === "string") return value.replaceAll(RECIPE_DIR_TOKEN, portableDir);
|
|
11870
11986
|
if (Array.isArray(value)) return value.map(replace);
|
|
@@ -11876,24 +11992,24 @@ function renderStyle(style, destination, cwd) {
|
|
|
11876
11992
|
return replace(style);
|
|
11877
11993
|
}
|
|
11878
11994
|
async function installRecipe(target, options = {}) {
|
|
11879
|
-
const cwd =
|
|
11995
|
+
const cwd = import_node_path15.default.resolve(options.cwd ?? process.cwd());
|
|
11880
11996
|
const loaded = await resolveRecipe(target, options.bundledRoot);
|
|
11881
11997
|
const verification = await verifyRecipe(loaded.path);
|
|
11882
11998
|
if (!verification.ok) {
|
|
11883
11999
|
throw new Error(`Recipe ${loaded.recipe.id}@${loaded.recipe.version} failed integrity verification.`);
|
|
11884
12000
|
}
|
|
11885
|
-
const destination = options.out ?
|
|
12001
|
+
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
12002
|
const sources = [
|
|
11887
12003
|
{ path: RECIPE_FILE, source: loaded.path },
|
|
11888
|
-
...loaded.recipe.files.map((file) => ({ path: file.path, source:
|
|
12004
|
+
...loaded.recipe.files.map((file) => ({ path: file.path, source: import_node_path15.default.join(loaded.dir, ...file.path.split("/")) }))
|
|
11889
12005
|
];
|
|
11890
12006
|
const files = await Promise.all(sources.map(async (file) => ({
|
|
11891
|
-
path:
|
|
12007
|
+
path: import_node_path15.default.join(destination, ...file.path.split("/")),
|
|
11892
12008
|
data: await (0, import_promises7.readFile)(file.source)
|
|
11893
12009
|
})));
|
|
11894
12010
|
if (!options.force) {
|
|
11895
12011
|
for (const file of files) {
|
|
11896
|
-
if (!(0,
|
|
12012
|
+
if (!(0, import_node_fs11.existsSync)(file.path)) continue;
|
|
11897
12013
|
const current = await (0, import_promises7.readFile)(file.path);
|
|
11898
12014
|
if (!current.equals(file.data)) {
|
|
11899
12015
|
throw new OverwriteRefusedError(
|
|
@@ -11919,8 +12035,8 @@ async function installRecipe(target, options = {}) {
|
|
|
11919
12035
|
|
|
11920
12036
|
// src/pipeline/audit.ts
|
|
11921
12037
|
var import_promises8 = require("fs/promises");
|
|
11922
|
-
var
|
|
11923
|
-
var
|
|
12038
|
+
var import_node_fs12 = require("fs");
|
|
12039
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
11924
12040
|
function paletteDistance(asset, reference) {
|
|
11925
12041
|
if (!asset.length || !reference.length) return 0;
|
|
11926
12042
|
let total = 0;
|
|
@@ -11956,7 +12072,7 @@ async function auditStyle(loaded, specs, styleId, lock) {
|
|
|
11956
12072
|
const unreadable = [];
|
|
11957
12073
|
for (const spec of mine) {
|
|
11958
12074
|
for (const output of resolveSpecOutputs(spec, lock, loaded.root)) {
|
|
11959
|
-
if (!(0,
|
|
12075
|
+
if (!(0, import_node_fs12.existsSync)(output.absolutePath)) {
|
|
11960
12076
|
missing.push(output.id);
|
|
11961
12077
|
continue;
|
|
11962
12078
|
}
|
|
@@ -11985,8 +12101,8 @@ async function auditStyle(loaded, specs, styleId, lock) {
|
|
|
11985
12101
|
let referenceFromStyleImages = false;
|
|
11986
12102
|
const refPalettes = [];
|
|
11987
12103
|
for (const img of style.styleImages) {
|
|
11988
|
-
const abs =
|
|
11989
|
-
if (!(0,
|
|
12104
|
+
const abs = import_node_path16.default.resolve(loaded.root, img.path);
|
|
12105
|
+
if (!(0, import_node_fs12.existsSync)(abs)) continue;
|
|
11990
12106
|
try {
|
|
11991
12107
|
refPalettes.push(extractPalette(decodePng(await (0, import_promises8.readFile)(abs)), 12));
|
|
11992
12108
|
} catch {
|
|
@@ -12064,19 +12180,19 @@ function hex(c) {
|
|
|
12064
12180
|
}
|
|
12065
12181
|
|
|
12066
12182
|
// src/pixellab-utils.ts
|
|
12067
|
-
var
|
|
12183
|
+
var import_node_fs13 = require("fs");
|
|
12068
12184
|
var import_promises9 = require("fs/promises");
|
|
12069
|
-
var
|
|
12185
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
12070
12186
|
var UNZOOM_MIN_SIDE = 256;
|
|
12071
12187
|
var UNZOOM_MAX_AREA = 2048 * 2048;
|
|
12072
12188
|
function defaultUnzoomOut(input) {
|
|
12073
|
-
const ext =
|
|
12189
|
+
const ext = import_node_path17.default.extname(input);
|
|
12074
12190
|
return `${ext ? input.slice(0, -ext.length) : input}.unzoomed.png`;
|
|
12075
12191
|
}
|
|
12076
12192
|
async function unzoomFile(client2, input, opts = {}) {
|
|
12077
|
-
const out =
|
|
12078
|
-
if (
|
|
12079
|
-
if ((0,
|
|
12193
|
+
const out = import_node_path17.default.resolve(opts.out ?? defaultUnzoomOut(input));
|
|
12194
|
+
if (import_node_path17.default.resolve(input) === out) throw new Error("unzoom will not overwrite its own input; pass a different --out");
|
|
12195
|
+
if ((0, import_node_fs13.existsSync)(out) && !opts.force) throw new Error(`${out} already exists; pass --force to replace it`);
|
|
12080
12196
|
const bytes = await (0, import_promises9.readFile)(input);
|
|
12081
12197
|
const metadata = imageMetadata(bytes);
|
|
12082
12198
|
if (!metadata) throw new Error(`${input} is not a readable PNG or JPEG`);
|
|
@@ -12090,7 +12206,7 @@ async function unzoomFile(client2, input, opts = {}) {
|
|
|
12090
12206
|
throw new Error(`${input} is ${width}x${height}; PixelLab's unzoom takes at most 2048x2048 worth of pixels`);
|
|
12091
12207
|
}
|
|
12092
12208
|
const res = await client2.unzoom({ image: { base64: bytes.toString("base64"), format }, quantize: opts.quantize });
|
|
12093
|
-
await (0, import_promises9.mkdir)(
|
|
12209
|
+
await (0, import_promises9.mkdir)(import_node_path17.default.dirname(out), { recursive: true });
|
|
12094
12210
|
await (0, import_promises9.writeFile)(out, res.png);
|
|
12095
12211
|
return { out, original: res.originalSize, unzoomed: res.unzoomedSize, zoomFactor: res.zoomFactor, usage: res.usage };
|
|
12096
12212
|
}
|
|
@@ -12098,14 +12214,14 @@ var FONT_WEIGHTS = ["Bold", "Regular"];
|
|
|
12098
12214
|
var FONT_GLYPH_SIZES = [8, 16, 32, 64];
|
|
12099
12215
|
var FONT_COST_GENERATIONS = 25;
|
|
12100
12216
|
function fontOutputPaths(out) {
|
|
12101
|
-
const base =
|
|
12217
|
+
const base = import_node_path17.default.resolve(out.replace(/\.(ttf|png)$/i, ""));
|
|
12102
12218
|
return { ttf: `${base}.ttf`, atlas: `${base}.png` };
|
|
12103
12219
|
}
|
|
12104
12220
|
async function generateFont(client2, opts) {
|
|
12105
12221
|
const { ttf, atlas } = fontOutputPaths(opts.out);
|
|
12106
12222
|
if (!opts.force) {
|
|
12107
12223
|
for (const file of [ttf, atlas]) {
|
|
12108
|
-
if ((0,
|
|
12224
|
+
if ((0, import_node_fs13.existsSync)(file)) throw new Error(`${file} already exists; pass --force to replace it`);
|
|
12109
12225
|
}
|
|
12110
12226
|
}
|
|
12111
12227
|
const sleep3 = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
@@ -12132,7 +12248,7 @@ async function generateFont(client2, opts) {
|
|
|
12132
12248
|
client2.download(job.download_ttf_url),
|
|
12133
12249
|
client2.download(job.download_atlas_url)
|
|
12134
12250
|
]);
|
|
12135
|
-
await (0, import_promises9.mkdir)(
|
|
12251
|
+
await (0, import_promises9.mkdir)(import_node_path17.default.dirname(ttf), { recursive: true });
|
|
12136
12252
|
await (0, import_promises9.writeFile)(ttf, ttfBytes);
|
|
12137
12253
|
await (0, import_promises9.writeFile)(atlas, atlasBytes);
|
|
12138
12254
|
return { jobId, ttf, atlas, glyphPx: job.glyph_px ?? null, usage: job.usage };
|
|
@@ -12147,15 +12263,15 @@ async function generateFont(client2, opts) {
|
|
|
12147
12263
|
}
|
|
12148
12264
|
|
|
12149
12265
|
// src/pipeline/quality-regression.ts
|
|
12150
|
-
var
|
|
12151
|
-
var
|
|
12266
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
12267
|
+
var import_node_fs14 = require("fs");
|
|
12152
12268
|
var import_promises10 = require("fs/promises");
|
|
12153
12269
|
var import_zod5 = require("zod");
|
|
12154
12270
|
var SHA256_RE2 = /^[0-9a-f]{64}$/;
|
|
12155
12271
|
var HEX_RE = /^#[0-9a-f]{6}$/;
|
|
12156
12272
|
var MAX_REDMEAN_DISTANCE = 765;
|
|
12157
12273
|
var QualityPathSchema = import_zod5.z.string().min(1).refine(
|
|
12158
|
-
(value) => !
|
|
12274
|
+
(value) => !import_node_path18.default.posix.isAbsolute(value) && !value.includes("\\"),
|
|
12159
12275
|
"expected a portable relative path"
|
|
12160
12276
|
);
|
|
12161
12277
|
var DEFAULT_QUALITY_TOLERANCES = {
|
|
@@ -12302,7 +12418,7 @@ function measureDecodedImageQuality(png, digest2) {
|
|
|
12302
12418
|
};
|
|
12303
12419
|
}
|
|
12304
12420
|
async function measureImageQuality(file) {
|
|
12305
|
-
const absolute =
|
|
12421
|
+
const absolute = import_node_path18.default.resolve(file);
|
|
12306
12422
|
let bytes;
|
|
12307
12423
|
try {
|
|
12308
12424
|
bytes = await (0, import_promises10.readFile)(absolute);
|
|
@@ -12324,11 +12440,11 @@ function resolveQualityInputs(raw, inputsFilePath) {
|
|
|
12324
12440
|
}
|
|
12325
12441
|
const ids = parsed.data.map((entry) => entry.id);
|
|
12326
12442
|
if (new Set(ids).size !== ids.length) throw new Error("--inputs contains a duplicate quality case id.");
|
|
12327
|
-
const root =
|
|
12443
|
+
const root = import_node_path18.default.dirname(import_node_path18.default.resolve(inputsFilePath));
|
|
12328
12444
|
return parsed.data.map((entry) => ({
|
|
12329
12445
|
id: entry.id,
|
|
12330
|
-
file:
|
|
12331
|
-
...entry.record ? { record:
|
|
12446
|
+
file: import_node_path18.default.resolve(root, entry.path),
|
|
12447
|
+
...entry.record ? { record: import_node_path18.default.resolve(root, entry.record) } : {},
|
|
12332
12448
|
tolerances: QualityToleranceSchema.parse({
|
|
12333
12449
|
...DEFAULT_QUALITY_TOLERANCES,
|
|
12334
12450
|
...entry.tolerances
|
|
@@ -12336,7 +12452,7 @@ function resolveQualityInputs(raw, inputsFilePath) {
|
|
|
12336
12452
|
}));
|
|
12337
12453
|
}
|
|
12338
12454
|
function portableRelative2(from, to) {
|
|
12339
|
-
return
|
|
12455
|
+
return import_node_path18.default.relative(from, import_node_path18.default.resolve(to)).split(import_node_path18.default.sep).join("/") || ".";
|
|
12340
12456
|
}
|
|
12341
12457
|
async function verifyRecordForImage(recordPath, imagePath) {
|
|
12342
12458
|
const verification = await verifyArtifactBundle(recordPath);
|
|
@@ -12351,20 +12467,20 @@ async function verifyRecordForImage(recordPath, imagePath) {
|
|
|
12351
12467
|
if (record.kind !== "refine" || options === null || typeof options !== "object" || options.schema !== "pixelkiln-quality") {
|
|
12352
12468
|
throw new Error(`Quality record ${recordPath} is not a PixelKiln refinement record.`);
|
|
12353
12469
|
}
|
|
12354
|
-
const outputs = record.outputs.map((output) =>
|
|
12355
|
-
if (!outputs.includes(
|
|
12470
|
+
const outputs = record.outputs.map((output) => import_node_path18.default.resolve(import_node_path18.default.dirname(recordPath), output.path));
|
|
12471
|
+
if (!outputs.includes(import_node_path18.default.resolve(imagePath))) {
|
|
12356
12472
|
throw new Error(`Quality record ${recordPath} does not own ${imagePath}.`);
|
|
12357
12473
|
}
|
|
12358
12474
|
}
|
|
12359
12475
|
async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
12360
12476
|
if (!inputs.length) throw new Error("Cannot snapshot an empty quality baseline.");
|
|
12361
|
-
const absolute =
|
|
12477
|
+
const absolute = import_node_path18.default.resolve(baselinePath);
|
|
12362
12478
|
for (const input of inputs) {
|
|
12363
|
-
if (
|
|
12479
|
+
if (import_node_path18.default.resolve(input.file) === absolute || input.record && import_node_path18.default.resolve(input.record) === absolute) {
|
|
12364
12480
|
throw new Error(`Quality baseline output would overwrite an input: ${absolute}.`);
|
|
12365
12481
|
}
|
|
12366
12482
|
}
|
|
12367
|
-
const root =
|
|
12483
|
+
const root = import_node_path18.default.dirname(absolute);
|
|
12368
12484
|
const cases = [];
|
|
12369
12485
|
for (const input of [...inputs].sort((a, b) => a.id.localeCompare(b.id))) {
|
|
12370
12486
|
const expected = await measureImageQuality(input.file);
|
|
@@ -12391,7 +12507,7 @@ async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
|
12391
12507
|
cases
|
|
12392
12508
|
});
|
|
12393
12509
|
const data = Buffer.from(JSON.stringify(baseline, null, 2) + "\n");
|
|
12394
|
-
if ((0,
|
|
12510
|
+
if ((0, import_node_fs14.existsSync)(absolute) && !options.force) {
|
|
12395
12511
|
const current = await (0, import_promises10.readFile)(absolute);
|
|
12396
12512
|
if (!current.equals(data)) {
|
|
12397
12513
|
throw new OverwriteRefusedError(
|
|
@@ -12404,7 +12520,7 @@ async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
|
12404
12520
|
return { baseline, path: absolute, changed: result.changed.length > 0 };
|
|
12405
12521
|
}
|
|
12406
12522
|
async function readQualityBaseline(baselinePath) {
|
|
12407
|
-
const absolute =
|
|
12523
|
+
const absolute = import_node_path18.default.resolve(baselinePath);
|
|
12408
12524
|
let raw;
|
|
12409
12525
|
try {
|
|
12410
12526
|
raw = JSON.parse(await (0, import_promises10.readFile)(absolute, "utf8"));
|
|
@@ -12477,12 +12593,12 @@ function regressionViolations(expected, actual, tolerances) {
|
|
|
12477
12593
|
return { violations, warnings };
|
|
12478
12594
|
}
|
|
12479
12595
|
async function checkQualityBaseline(baselinePath) {
|
|
12480
|
-
const absolute =
|
|
12596
|
+
const absolute = import_node_path18.default.resolve(baselinePath);
|
|
12481
12597
|
const baseline = await readQualityBaseline(absolute);
|
|
12482
|
-
const root =
|
|
12598
|
+
const root = import_node_path18.default.dirname(absolute);
|
|
12483
12599
|
const cases = [];
|
|
12484
12600
|
for (const entry of baseline.cases) {
|
|
12485
|
-
const file =
|
|
12601
|
+
const file = import_node_path18.default.resolve(root, entry.file);
|
|
12486
12602
|
let actual = null;
|
|
12487
12603
|
const violations = [];
|
|
12488
12604
|
const warnings = [];
|
|
@@ -12495,7 +12611,7 @@ async function checkQualityBaseline(baselinePath) {
|
|
|
12495
12611
|
violations.push(message4(error));
|
|
12496
12612
|
}
|
|
12497
12613
|
if (entry.record) {
|
|
12498
|
-
const recordPath =
|
|
12614
|
+
const recordPath = import_node_path18.default.resolve(root, entry.record.path);
|
|
12499
12615
|
try {
|
|
12500
12616
|
const recordHash = await sha256File(recordPath);
|
|
12501
12617
|
if (recordHash !== entry.record.sha256) violations.push("quality record hash changed");
|
|
@@ -12531,14 +12647,14 @@ async function checkQualityBaseline(baselinePath) {
|
|
|
12531
12647
|
}
|
|
12532
12648
|
|
|
12533
12649
|
// src/pipeline/cache-health.ts
|
|
12534
|
-
var
|
|
12650
|
+
var import_node_fs18 = require("fs");
|
|
12535
12651
|
var import_promises13 = require("fs/promises");
|
|
12536
|
-
var
|
|
12652
|
+
var import_node_path22 = __toESM(require("path"), 1);
|
|
12537
12653
|
|
|
12538
12654
|
// src/cache.ts
|
|
12539
12655
|
var import_promises11 = require("fs/promises");
|
|
12540
|
-
var
|
|
12541
|
-
var
|
|
12656
|
+
var import_node_fs15 = require("fs");
|
|
12657
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
12542
12658
|
var import_zod6 = require("zod");
|
|
12543
12659
|
var HashCacheSchema = import_zod6.z.object({
|
|
12544
12660
|
version: import_zod6.z.literal(1),
|
|
@@ -12552,10 +12668,10 @@ function isSha256Hash(value) {
|
|
|
12552
12668
|
function parseCache(value) {
|
|
12553
12669
|
return HashCacheSchema.parse(value);
|
|
12554
12670
|
}
|
|
12555
|
-
async function loadCache(
|
|
12556
|
-
if (!(0,
|
|
12671
|
+
async function loadCache(path38) {
|
|
12672
|
+
if (!(0, import_node_fs15.existsSync)(path38)) return { version: 1, hashes: {} };
|
|
12557
12673
|
try {
|
|
12558
|
-
const parsed = HashCacheSchema.safeParse(JSON.parse(await (0, import_promises11.readFile)(
|
|
12674
|
+
const parsed = HashCacheSchema.safeParse(JSON.parse(await (0, import_promises11.readFile)(path38, "utf8")));
|
|
12559
12675
|
if (!parsed.success) return { version: 1, hashes: {} };
|
|
12560
12676
|
return {
|
|
12561
12677
|
version: 1,
|
|
@@ -12567,18 +12683,18 @@ async function loadCache(path37) {
|
|
|
12567
12683
|
return { version: 1, hashes: {} };
|
|
12568
12684
|
}
|
|
12569
12685
|
}
|
|
12570
|
-
async function saveCache(
|
|
12686
|
+
async function saveCache(path38, cache) {
|
|
12571
12687
|
const sorted = {};
|
|
12572
12688
|
for (const key of Object.keys(cache.hashes).sort()) {
|
|
12573
12689
|
const hash = cache.hashes[key];
|
|
12574
12690
|
if (!isSha256Hash(hash)) throw new Error(`Refusing to cache invalid SHA-256 for ${key}`);
|
|
12575
12691
|
sorted[key] = hash;
|
|
12576
12692
|
}
|
|
12577
|
-
await (0, import_promises11.mkdir)(
|
|
12578
|
-
const tmp = `${
|
|
12693
|
+
await (0, import_promises11.mkdir)(import_node_path19.default.dirname(import_node_path19.default.resolve(path38)), { recursive: true });
|
|
12694
|
+
const tmp = `${path38}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
12579
12695
|
try {
|
|
12580
12696
|
await (0, import_promises11.writeFile)(tmp, JSON.stringify({ version: 1, hashes: sorted }, null, 2) + "\n");
|
|
12581
|
-
await (0, import_promises11.rename)(tmp,
|
|
12697
|
+
await (0, import_promises11.rename)(tmp, path38);
|
|
12582
12698
|
} finally {
|
|
12583
12699
|
await (0, import_promises11.rm)(tmp, { force: true });
|
|
12584
12700
|
}
|
|
@@ -12602,20 +12718,20 @@ function cachePathFor(lockPath) {
|
|
|
12602
12718
|
}
|
|
12603
12719
|
|
|
12604
12720
|
// src/pipeline/history.ts
|
|
12605
|
-
var
|
|
12606
|
-
var
|
|
12721
|
+
var import_node_fs17 = require("fs");
|
|
12722
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
12607
12723
|
|
|
12608
12724
|
// src/pipeline/fetch.ts
|
|
12609
|
-
var
|
|
12725
|
+
var import_node_fs16 = require("fs");
|
|
12610
12726
|
var import_promises12 = require("fs/promises");
|
|
12611
|
-
var
|
|
12727
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
12612
12728
|
async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
12613
12729
|
const log = opts.onProgress ?? (() => {
|
|
12614
12730
|
});
|
|
12615
12731
|
const result = { downloaded: 0, skipped: 0, failed: 0, ...opts.refresh ? { unchanged: 0 } : {} };
|
|
12616
12732
|
const specByKey = new Map(specs.map((s) => [lockKey(s.styleId, s.assetId), s]));
|
|
12617
12733
|
normalizeLockOutputPaths(lock, specs);
|
|
12618
|
-
const cacheDir = opts.cacheDir === false ? null :
|
|
12734
|
+
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
12735
|
const pending = Object.entries(lock.entries).filter(([key, e]) => {
|
|
12620
12736
|
if (e.provider !== provider.id || !specByKey.has(key)) return false;
|
|
12621
12737
|
if (specByKey.get(key)?.mirror) return false;
|
|
@@ -12626,15 +12742,15 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12626
12742
|
if (spec && e.specHash === spec.specHash && e.outputs.length && !postprocessCurrent(e, spec)) {
|
|
12627
12743
|
return Boolean(opts.force) || e.outputs.every((output) => {
|
|
12628
12744
|
const file = resolveOutputPath(output.path, spec.root);
|
|
12629
|
-
return (0,
|
|
12745
|
+
return (0, import_node_fs16.existsSync)(file) && sha256((0, import_node_fs16.readFileSync)(file)) === output.sha256;
|
|
12630
12746
|
});
|
|
12631
12747
|
}
|
|
12632
12748
|
if (!opts.repair) return false;
|
|
12633
12749
|
if (e.outputs.length === 0 || !spec) return true;
|
|
12634
12750
|
return e.outputs.some((output) => {
|
|
12635
12751
|
const file = resolveOutputPath(output.path, spec.root);
|
|
12636
|
-
if (!(0,
|
|
12637
|
-
const current = sha256((0,
|
|
12752
|
+
if (!(0, import_node_fs16.existsSync)(file)) return true;
|
|
12753
|
+
const current = sha256((0, import_node_fs16.readFileSync)(file));
|
|
12638
12754
|
if (current === output.sha256) return false;
|
|
12639
12755
|
return Boolean(opts.force) || (e.supersededOutputs ?? []).some((old) => old.sha256 === current);
|
|
12640
12756
|
});
|
|
@@ -12717,14 +12833,14 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12717
12833
|
if (!source.url) throw new Error(`no durable source URL to refresh ${source.role ?? "asset"} from`);
|
|
12718
12834
|
fresh = await provider.download(source.url);
|
|
12719
12835
|
if (recorded && sha256(fresh) === recordedRaw && !reapply) {
|
|
12720
|
-
log(` current ${
|
|
12836
|
+
log(` current ${import_node_path20.default.relative(process.cwd(), target)}`);
|
|
12721
12837
|
outputs.push({ ...recorded, path: portableOutputPath(target, spec.root) });
|
|
12722
12838
|
continue;
|
|
12723
12839
|
}
|
|
12724
12840
|
}
|
|
12725
12841
|
let raw = null;
|
|
12726
12842
|
let final = null;
|
|
12727
|
-
if ((0,
|
|
12843
|
+
if ((0, import_node_fs16.existsSync)(target)) {
|
|
12728
12844
|
const currentHash = await sha256File(target);
|
|
12729
12845
|
if (recorded && currentHash === recorded.sha256) {
|
|
12730
12846
|
if (!fresh && !reapply) {
|
|
@@ -12741,9 +12857,9 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12741
12857
|
}
|
|
12742
12858
|
if (reapply && !fresh) {
|
|
12743
12859
|
if (!recorded.raw) raw = await (0, import_promises12.readFile)(target);
|
|
12744
|
-
log(` reapply ${
|
|
12860
|
+
log(` reapply ${import_node_path20.default.relative(process.cwd(), target)}`);
|
|
12745
12861
|
} else {
|
|
12746
|
-
log(` changed ${
|
|
12862
|
+
log(` changed ${import_node_path20.default.relative(process.cwd(), target)} (upstream)`);
|
|
12747
12863
|
}
|
|
12748
12864
|
} else {
|
|
12749
12865
|
const superseded = (entry.supersededOutputs ?? []).find(
|
|
@@ -12771,7 +12887,7 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12771
12887
|
}
|
|
12772
12888
|
}
|
|
12773
12889
|
log(
|
|
12774
|
-
` replace ${
|
|
12890
|
+
` replace ${import_node_path20.default.relative(process.cwd(), target)}` + (opts.force ? " (--force)" : " (previous tracked generation)")
|
|
12775
12891
|
);
|
|
12776
12892
|
}
|
|
12777
12893
|
}
|
|
@@ -12780,7 +12896,7 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12780
12896
|
} else if (!raw && recorded && cacheDir) {
|
|
12781
12897
|
if (!reapply) final = await readCachedMedia(cacheDir, recorded.sha256, expectedMediaType);
|
|
12782
12898
|
if (!final && recordedRaw) raw = await readCachedMedia(cacheDir, recordedRaw, expectedMediaType);
|
|
12783
|
-
if (final || raw) log(` cached ${
|
|
12899
|
+
if (final || raw) log(` cached ${import_node_path20.default.relative(process.cwd(), target)}`);
|
|
12784
12900
|
}
|
|
12785
12901
|
if (!final && !raw) {
|
|
12786
12902
|
if (!source.url) {
|
|
@@ -12807,11 +12923,11 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12807
12923
|
buf = processed.bytes;
|
|
12808
12924
|
if (processed.changed) {
|
|
12809
12925
|
rawHash = sha256(raw);
|
|
12810
|
-
log(` snapped ${
|
|
12926
|
+
log(` snapped ${import_node_path20.default.relative(process.cwd(), target)} to ${wanted.palette.colors.length} colours`);
|
|
12811
12927
|
}
|
|
12812
12928
|
}
|
|
12813
12929
|
if (cacheDir) await cacheMedia(cacheDir, buf, mediaType);
|
|
12814
|
-
await (0, import_promises12.mkdir)(
|
|
12930
|
+
await (0, import_promises12.mkdir)(import_node_path20.default.dirname(target), { recursive: true });
|
|
12815
12931
|
const tmp = `${target}.pixelkiln.tmp`;
|
|
12816
12932
|
await (0, import_promises12.writeFile)(tmp, buf);
|
|
12817
12933
|
outputs.push({
|
|
@@ -12825,7 +12941,7 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12825
12941
|
await saveLock(lockPath, lock);
|
|
12826
12942
|
await (0, import_promises12.rename)(tmp, target);
|
|
12827
12943
|
wrote = true;
|
|
12828
|
-
log(` wrote ${
|
|
12944
|
+
log(` wrote ${import_node_path20.default.relative(process.cwd(), target)}`);
|
|
12829
12945
|
}
|
|
12830
12946
|
if (opts.refresh && !wrote) {
|
|
12831
12947
|
if (rotated) {
|
|
@@ -12869,8 +12985,8 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
12869
12985
|
return result;
|
|
12870
12986
|
}
|
|
12871
12987
|
async function readCachedMedia(cacheDir, hash, mediaType) {
|
|
12872
|
-
const file =
|
|
12873
|
-
if (!(0,
|
|
12988
|
+
const file = import_node_path20.default.join(cacheDir, cacheFileName(hash, mediaType));
|
|
12989
|
+
if (!(0, import_node_fs16.existsSync)(file)) return null;
|
|
12874
12990
|
try {
|
|
12875
12991
|
const buf = await (0, import_promises12.readFile)(file);
|
|
12876
12992
|
if (sha256(buf) !== hash) return null;
|
|
@@ -12883,7 +12999,7 @@ async function readCachedMedia(cacheDir, hash, mediaType) {
|
|
|
12883
12999
|
async function cacheMedia(cacheDir, buf, mediaType, knownHash) {
|
|
12884
13000
|
const hash = knownHash ?? sha256(buf);
|
|
12885
13001
|
validateMedia(buf, mediaType);
|
|
12886
|
-
const file =
|
|
13002
|
+
const file = import_node_path20.default.join(cacheDir, cacheFileName(hash, mediaType));
|
|
12887
13003
|
if (await readCachedMedia(cacheDir, hash, mediaType)) return;
|
|
12888
13004
|
await (0, import_promises12.mkdir)(cacheDir, { recursive: true });
|
|
12889
13005
|
const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
@@ -12891,7 +13007,7 @@ async function cacheMedia(cacheDir, buf, mediaType, knownHash) {
|
|
|
12891
13007
|
try {
|
|
12892
13008
|
await (0, import_promises12.rename)(tmp, file);
|
|
12893
13009
|
} catch (err) {
|
|
12894
|
-
if (!(0,
|
|
13010
|
+
if (!(0, import_node_fs16.existsSync)(file)) throw err;
|
|
12895
13011
|
} finally {
|
|
12896
13012
|
await (0, import_promises12.rm)(tmp, { force: true });
|
|
12897
13013
|
}
|
|
@@ -13010,10 +13126,10 @@ async function revertGeneration(provider, spec, lock, lockPath, opts) {
|
|
|
13010
13126
|
}
|
|
13011
13127
|
for (let i = 0; i < entry.outputs.length; i++) {
|
|
13012
13128
|
const file = currentEntryOutputPath(entry, spec, i);
|
|
13013
|
-
if (!(0,
|
|
13129
|
+
if (!(0, import_node_fs17.existsSync)(file)) continue;
|
|
13014
13130
|
if (await sha256File(file) !== entry.outputs[i].sha256 && !opts.force) {
|
|
13015
13131
|
throw new OverwriteRefusedError(
|
|
13016
|
-
`${
|
|
13132
|
+
`${import_node_path21.default.relative(process.cwd(), file)} was changed after download; keep it with pixelkiln edit, or pass --force to replace it`,
|
|
13017
13133
|
[file]
|
|
13018
13134
|
);
|
|
13019
13135
|
}
|
|
@@ -13065,11 +13181,11 @@ async function revertGeneration(provider, spec, lock, lockPath, opts) {
|
|
|
13065
13181
|
|
|
13066
13182
|
// src/pipeline/cache-health.ts
|
|
13067
13183
|
async function inspectCaches(lock, lockPath, options = {}) {
|
|
13068
|
-
if (options.prune && !(0,
|
|
13069
|
-
throw new Error(`Refusing to prune without an existing lockfile at ${
|
|
13184
|
+
if (options.prune && !(0, import_node_fs18.existsSync)(lockPath)) {
|
|
13185
|
+
throw new Error(`Refusing to prune without an existing lockfile at ${import_node_path22.default.resolve(lockPath)}`);
|
|
13070
13186
|
}
|
|
13071
|
-
const contentDir =
|
|
13072
|
-
const remotePath =
|
|
13187
|
+
const contentDir = import_node_path22.default.resolve(import_node_path22.default.dirname(lockPath), ".pixelkiln", "cache");
|
|
13188
|
+
const remotePath = import_node_path22.default.resolve(cachePathFor(lockPath));
|
|
13073
13189
|
const referenced = referencedHashes(lock);
|
|
13074
13190
|
let content = await inspectContentCache(contentDir, referenced);
|
|
13075
13191
|
let remoteHashes = await inspectRemoteHashCache(remotePath);
|
|
@@ -13081,7 +13197,7 @@ async function inspectCaches(lock, lockPath, options = {}) {
|
|
|
13081
13197
|
]);
|
|
13082
13198
|
for (const name of names) {
|
|
13083
13199
|
try {
|
|
13084
|
-
await (0, import_promises13.rm)(
|
|
13200
|
+
await (0, import_promises13.rm)(import_node_path22.default.join(contentDir, name), { force: true });
|
|
13085
13201
|
removed.contentFiles++;
|
|
13086
13202
|
} catch {
|
|
13087
13203
|
}
|
|
@@ -13108,7 +13224,7 @@ async function inspectCaches(lock, lockPath, options = {}) {
|
|
|
13108
13224
|
async function inspectContentCache(contentDir, referenced) {
|
|
13109
13225
|
const report = {
|
|
13110
13226
|
path: contentDir,
|
|
13111
|
-
exists: (0,
|
|
13227
|
+
exists: (0, import_node_fs18.existsSync)(contentDir),
|
|
13112
13228
|
files: 0,
|
|
13113
13229
|
bytes: 0,
|
|
13114
13230
|
valid: 0,
|
|
@@ -13129,7 +13245,7 @@ async function inspectContentCache(contentDir, referenced) {
|
|
|
13129
13245
|
continue;
|
|
13130
13246
|
}
|
|
13131
13247
|
report.files++;
|
|
13132
|
-
const file =
|
|
13248
|
+
const file = import_node_path22.default.join(contentDir, entry.name);
|
|
13133
13249
|
const mediaType = mediaTypeFromExtension(entry.name);
|
|
13134
13250
|
const expected = mediaType ? entry.name.slice(0, -4) : "";
|
|
13135
13251
|
let bytes;
|
|
@@ -13172,7 +13288,7 @@ async function inspectContentCache(contentDir, referenced) {
|
|
|
13172
13288
|
async function inspectRemoteHashCache(remotePath) {
|
|
13173
13289
|
const report = {
|
|
13174
13290
|
path: remotePath,
|
|
13175
|
-
exists: (0,
|
|
13291
|
+
exists: (0, import_node_fs18.existsSync)(remotePath),
|
|
13176
13292
|
entries: 0,
|
|
13177
13293
|
valid: 0,
|
|
13178
13294
|
invalidIds: [],
|
|
@@ -13196,20 +13312,20 @@ async function inspectRemoteHashCache(remotePath) {
|
|
|
13196
13312
|
}
|
|
13197
13313
|
|
|
13198
13314
|
// src/pipeline/pack.ts
|
|
13199
|
-
var
|
|
13200
|
-
var
|
|
13315
|
+
var import_node_fs19 = require("fs");
|
|
13316
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
13201
13317
|
var DEFAULT_FRAME_SET_FPS = 12;
|
|
13202
13318
|
function resolvePackInputs(raw, inputsFilePath) {
|
|
13203
13319
|
if (!Array.isArray(raw) || !raw.length) {
|
|
13204
13320
|
throw new Error("--inputs must be a non-empty JSON array of { id, path }");
|
|
13205
13321
|
}
|
|
13206
|
-
const baseDir =
|
|
13322
|
+
const baseDir = import_node_path23.default.dirname(import_node_path23.default.resolve(inputsFilePath));
|
|
13207
13323
|
return raw.map((entry, i) => {
|
|
13208
13324
|
const e = entry;
|
|
13209
13325
|
if (typeof e.id !== "string" || typeof e.path !== "string") {
|
|
13210
13326
|
throw new Error(`--inputs[${i}] needs string "id" and "path"`);
|
|
13211
13327
|
}
|
|
13212
|
-
return { id: e.id, path:
|
|
13328
|
+
return { id: e.id, path: import_node_path23.default.resolve(baseDir, e.path) };
|
|
13213
13329
|
});
|
|
13214
13330
|
}
|
|
13215
13331
|
function packSprites(inputs, options = {}) {
|
|
@@ -13230,7 +13346,7 @@ function packSprites(inputs, options = {}) {
|
|
|
13230
13346
|
for (const input of ordered) {
|
|
13231
13347
|
let sourceHash = null;
|
|
13232
13348
|
try {
|
|
13233
|
-
const bytes = (0,
|
|
13349
|
+
const bytes = (0, import_node_fs19.readFileSync)(input.path);
|
|
13234
13350
|
sourceHash = sha256(bytes);
|
|
13235
13351
|
const png = decodePng(bytes);
|
|
13236
13352
|
sprites.push({ id: input.id, width: png.width, height: png.height, pixels: png.pixels });
|
|
@@ -13293,7 +13409,7 @@ function packStyle(lock, styleId, manifestDir, options = {}) {
|
|
|
13293
13409
|
}
|
|
13294
13410
|
const inputs2 = Object.entries(options.sourceOverrides).map(([id, source]) => ({
|
|
13295
13411
|
id,
|
|
13296
|
-
path:
|
|
13412
|
+
path: import_node_path23.default.resolve(source)
|
|
13297
13413
|
}));
|
|
13298
13414
|
if (!inputs2.length) throw new Error(`No approved quality outputs for style "${styleId}".`);
|
|
13299
13415
|
const packed2 = packSprites(inputs2, options);
|
|
@@ -13317,7 +13433,7 @@ function packStyle(lock, styleId, manifestDir, options = {}) {
|
|
|
13317
13433
|
let outputs = resolveEntryOutputs(entry, id, manifestDir);
|
|
13318
13434
|
const source = sources[id];
|
|
13319
13435
|
if (source && outputs.length <= 1) {
|
|
13320
|
-
inputs.push({ id, path:
|
|
13436
|
+
inputs.push({ id, path: import_node_path23.default.resolve(manifestDir, source) });
|
|
13321
13437
|
continue;
|
|
13322
13438
|
}
|
|
13323
13439
|
if (source && sourceIsStem(source, entry, manifestDir)) {
|
|
@@ -13351,7 +13467,7 @@ function packStyle(lock, styleId, manifestDir, options = {}) {
|
|
|
13351
13467
|
}
|
|
13352
13468
|
}
|
|
13353
13469
|
for (const [id, source] of Object.entries(sources)) {
|
|
13354
|
-
if (!locked.has(id)) inputs.push({ id, path:
|
|
13470
|
+
if (!locked.has(id)) inputs.push({ id, path: import_node_path23.default.resolve(manifestDir, source) });
|
|
13355
13471
|
}
|
|
13356
13472
|
if (!inputs.length) {
|
|
13357
13473
|
throw new Error(`No readable sprites for style "${styleId}"; ${noOutput.length} skipped.`);
|
|
@@ -13388,7 +13504,7 @@ function mountSprites(placements, options) {
|
|
|
13388
13504
|
for (const p of placements) {
|
|
13389
13505
|
let sourceHash = null;
|
|
13390
13506
|
try {
|
|
13391
|
-
const bytes = (0,
|
|
13507
|
+
const bytes = (0, import_node_fs19.readFileSync)(p.path);
|
|
13392
13508
|
sourceHash = sha256(bytes);
|
|
13393
13509
|
const png = decodePng(bytes);
|
|
13394
13510
|
if (png.width > cellWidth || png.height > cellHeight) {
|
|
@@ -13459,7 +13575,7 @@ function mountStyle(lock, styleId, manifestDir, mount, cells, sources = {}, outp
|
|
|
13459
13575
|
const source = sources[id];
|
|
13460
13576
|
const entry = lock.entries[`${styleId}/${id}`];
|
|
13461
13577
|
if (source && !(entry && sourceIsStem(source, entry, manifestDir))) {
|
|
13462
|
-
placements.push({ id, path:
|
|
13578
|
+
placements.push({ id, path: import_node_path23.default.resolve(manifestDir, source), cell });
|
|
13463
13579
|
continue;
|
|
13464
13580
|
}
|
|
13465
13581
|
if (source && entry) {
|
|
@@ -13492,7 +13608,7 @@ function mountStyle(lock, styleId, manifestDir, mount, cells, sources = {}, outp
|
|
|
13492
13608
|
`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
13609
|
);
|
|
13494
13610
|
}
|
|
13495
|
-
const basePng = mount.base ? (0,
|
|
13611
|
+
const basePng = mount.base ? (0, import_node_fs19.readFileSync)(import_node_path23.default.resolve(manifestDir, mount.base)) : void 0;
|
|
13496
13612
|
const mounted = mountSprites(placements, {
|
|
13497
13613
|
cellWidth: mount.cellWidth,
|
|
13498
13614
|
cellHeight: mount.cellHeight,
|
|
@@ -13502,7 +13618,7 @@ function mountStyle(lock, styleId, manifestDir, mount, cells, sources = {}, outp
|
|
|
13502
13618
|
...mounted,
|
|
13503
13619
|
sources: mount.base ? [{
|
|
13504
13620
|
id: "$base",
|
|
13505
|
-
path:
|
|
13621
|
+
path: import_node_path23.default.resolve(manifestDir, mount.base),
|
|
13506
13622
|
sha256: sha256(basePng),
|
|
13507
13623
|
included: true
|
|
13508
13624
|
}, ...mounted.sources] : mounted.sources,
|
|
@@ -14312,9 +14428,9 @@ async function poll(provider, lock, lockPath, opts = {}) {
|
|
|
14312
14428
|
}
|
|
14313
14429
|
|
|
14314
14430
|
// src/pipeline/doctor.ts
|
|
14315
|
-
var
|
|
14431
|
+
var import_node_fs20 = require("fs");
|
|
14316
14432
|
var import_promises14 = require("fs/promises");
|
|
14317
|
-
var
|
|
14433
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
14318
14434
|
async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
14319
14435
|
const checks = [];
|
|
14320
14436
|
const add = (id, level, message6) => checks.push({ id, level, message: message6 });
|
|
@@ -14325,14 +14441,14 @@ async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
|
14325
14441
|
);
|
|
14326
14442
|
add("lock", "ok", `${Object.keys(lock.entries).length} valid v2 lock entr(ies)`);
|
|
14327
14443
|
const dirs = [...new Set(specs.flatMap((spec) => [
|
|
14328
|
-
|
|
14329
|
-
...spec.quality ? [
|
|
14444
|
+
import_node_path24.default.dirname(spec.outFile),
|
|
14445
|
+
...spec.quality ? [import_node_path24.default.dirname(spec.quality.outFile)] : []
|
|
14330
14446
|
]))];
|
|
14331
14447
|
const unwritable = [];
|
|
14332
14448
|
for (const dir of dirs) {
|
|
14333
14449
|
const ancestor = await nearestExistingDirectory(dir);
|
|
14334
14450
|
try {
|
|
14335
|
-
await (0, import_promises14.access)(ancestor,
|
|
14451
|
+
await (0, import_promises14.access)(ancestor, import_node_fs20.constants.W_OK);
|
|
14336
14452
|
} catch {
|
|
14337
14453
|
unwritable.push(dir);
|
|
14338
14454
|
}
|
|
@@ -14349,7 +14465,7 @@ async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
|
14349
14465
|
for (let index = 0; index < entry.outputs.length; index++) {
|
|
14350
14466
|
const output = entry.outputs[index];
|
|
14351
14467
|
const spec = specByKey.get(key);
|
|
14352
|
-
const absolute = spec ? currentEntryOutputPath(entry, spec, index) : resolveOutputPath(output.path,
|
|
14468
|
+
const absolute = spec ? currentEntryOutputPath(entry, spec, index) : resolveOutputPath(output.path, import_node_path24.default.dirname(lockPath));
|
|
14353
14469
|
const owner = outputOwners.get(absolute);
|
|
14354
14470
|
if (owner && owner !== key) duplicateOutputs.push(`${output.path} (${owner}, ${key})`);
|
|
14355
14471
|
outputOwners.set(absolute, key);
|
|
@@ -14364,7 +14480,7 @@ async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
|
14364
14480
|
} else {
|
|
14365
14481
|
add("lock-outputs", "ok", `${outputOwners.size} recorded output path(s) are uniquely owned`);
|
|
14366
14482
|
}
|
|
14367
|
-
const cacheDir =
|
|
14483
|
+
const cacheDir = import_node_path24.default.join(import_node_path24.default.dirname(lockPath), ".pixelkiln", "cache");
|
|
14368
14484
|
const stranded = [];
|
|
14369
14485
|
const invalidState = [];
|
|
14370
14486
|
const staleMapUrls = [];
|
|
@@ -14380,11 +14496,11 @@ async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
|
14380
14496
|
const hasSource = Boolean(entry.sourceUrl || entry.sourceUrls?.length);
|
|
14381
14497
|
let hasCache = false;
|
|
14382
14498
|
for (const output of entry.outputs) {
|
|
14383
|
-
const cached =
|
|
14499
|
+
const cached = import_node_path24.default.join(
|
|
14384
14500
|
cacheDir,
|
|
14385
14501
|
cacheFileName(output.sha256, output.mediaType ?? MediaType.PNG)
|
|
14386
14502
|
);
|
|
14387
|
-
if ((0,
|
|
14503
|
+
if ((0, import_node_fs20.existsSync)(cached) && await sha256File(cached) === output.sha256) {
|
|
14388
14504
|
hasCache = true;
|
|
14389
14505
|
break;
|
|
14390
14506
|
}
|
|
@@ -14480,16 +14596,16 @@ async function nearestExistingDirectory(start) {
|
|
|
14480
14596
|
if ((await (0, import_promises14.stat)(current)).isDirectory()) return current;
|
|
14481
14597
|
} catch {
|
|
14482
14598
|
}
|
|
14483
|
-
const parent =
|
|
14599
|
+
const parent = import_node_path24.default.dirname(current);
|
|
14484
14600
|
if (parent === current) return current;
|
|
14485
14601
|
current = parent;
|
|
14486
14602
|
}
|
|
14487
14603
|
}
|
|
14488
14604
|
|
|
14489
14605
|
// src/pipeline/adopt.ts
|
|
14490
|
-
var
|
|
14606
|
+
var import_node_fs21 = require("fs");
|
|
14491
14607
|
var import_promises15 = require("fs/promises");
|
|
14492
|
-
var
|
|
14608
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
14493
14609
|
async function adopt(provider, specs, lock, lockPath, opts = {}) {
|
|
14494
14610
|
const log = opts.onProgress ?? (() => {
|
|
14495
14611
|
});
|
|
@@ -14500,7 +14616,7 @@ async function adopt(provider, specs, lock, lockPath, opts = {}) {
|
|
|
14500
14616
|
const localByHash = /* @__PURE__ */ new Map();
|
|
14501
14617
|
const unmatchedLocal = [];
|
|
14502
14618
|
for (const spec of specs) {
|
|
14503
|
-
if (!(0,
|
|
14619
|
+
if (!(0, import_node_fs21.existsSync)(spec.outFile)) {
|
|
14504
14620
|
unmatchedLocal.push(`${spec.styleId}/${spec.assetId} (no file at ${spec.outFile})`);
|
|
14505
14621
|
continue;
|
|
14506
14622
|
}
|
|
@@ -14622,13 +14738,13 @@ async function tagAdopted(provider, specs, lock, opts = {}) {
|
|
|
14622
14738
|
}
|
|
14623
14739
|
|
|
14624
14740
|
// src/pick/server.ts
|
|
14625
|
-
var
|
|
14741
|
+
var import_node_fs23 = require("fs");
|
|
14626
14742
|
var import_promises18 = require("fs/promises");
|
|
14627
14743
|
|
|
14628
14744
|
// src/pipeline/init.ts
|
|
14629
14745
|
var import_promises16 = require("fs/promises");
|
|
14630
|
-
var
|
|
14631
|
-
var
|
|
14746
|
+
var import_node_fs22 = require("fs");
|
|
14747
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
14632
14748
|
function pngSize(buf) {
|
|
14633
14749
|
if (buf.length < 24) return null;
|
|
14634
14750
|
const isPng = buf.readUInt32BE(0) === 2303741511 && buf.readUInt32BE(4) === 218765834;
|
|
@@ -14642,7 +14758,7 @@ function slugify(value) {
|
|
|
14642
14758
|
async function walk(dir, exclude) {
|
|
14643
14759
|
const out = [];
|
|
14644
14760
|
for (const entry of await (0, import_promises16.readdir)(dir, { withFileTypes: true })) {
|
|
14645
|
-
const full =
|
|
14761
|
+
const full = import_node_path26.default.join(dir, entry.name);
|
|
14646
14762
|
if (exclude.some((x) => full.includes(x))) continue;
|
|
14647
14763
|
if (entry.isDirectory()) out.push(...await walk(full, exclude));
|
|
14648
14764
|
else if (entry.name.toLowerCase().endsWith(".png")) out.push(full);
|
|
@@ -14661,9 +14777,9 @@ async function scanAssets(root, opts = {}) {
|
|
|
14661
14777
|
skipped.push(`${file} (not a readable PNG)`);
|
|
14662
14778
|
continue;
|
|
14663
14779
|
}
|
|
14664
|
-
const rel =
|
|
14665
|
-
const category =
|
|
14666
|
-
let id = slugify(
|
|
14780
|
+
const rel = import_node_path26.default.relative(root, file);
|
|
14781
|
+
const category = import_node_path26.default.dirname(rel) === "." ? "" : import_node_path26.default.dirname(rel);
|
|
14782
|
+
let id = slugify(import_node_path26.default.basename(rel));
|
|
14667
14783
|
if (seen.has(id)) {
|
|
14668
14784
|
const n = seen.get(id) + 1;
|
|
14669
14785
|
seen.set(id, n);
|
|
@@ -15212,7 +15328,7 @@ async function currentArt(entry, spec) {
|
|
|
15212
15328
|
const previous = primaryOutput({ ...entry, outputs: entry.supersededOutputs });
|
|
15213
15329
|
if (!previous) return null;
|
|
15214
15330
|
const file = resolveOutputPath(previous.path, spec.root);
|
|
15215
|
-
if (!(0,
|
|
15331
|
+
if (!(0, import_node_fs23.existsSync)(file)) return null;
|
|
15216
15332
|
const contentType = previous.mediaType ?? mediaTypeFromExtension(file);
|
|
15217
15333
|
if (!contentType) return null;
|
|
15218
15334
|
let width = entry.width;
|
|
@@ -15386,15 +15502,15 @@ async function runPicker(provider, lock, lockPath, opts = {}) {
|
|
|
15386
15502
|
}
|
|
15387
15503
|
|
|
15388
15504
|
// src/gallery/snapshot.ts
|
|
15389
|
-
var
|
|
15390
|
-
var
|
|
15505
|
+
var import_node_crypto7 = require("crypto");
|
|
15506
|
+
var import_node_fs29 = require("fs");
|
|
15391
15507
|
var import_promises26 = require("fs/promises");
|
|
15392
|
-
var
|
|
15508
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
15393
15509
|
|
|
15394
15510
|
// src/workspace.ts
|
|
15395
15511
|
var import_promises19 = require("fs/promises");
|
|
15396
|
-
var
|
|
15397
|
-
var
|
|
15512
|
+
var import_node_fs24 = require("fs");
|
|
15513
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
15398
15514
|
var import_zod7 = require("zod");
|
|
15399
15515
|
var WorkspaceProjectSchema = import_zod7.z.object({
|
|
15400
15516
|
id: import_zod7.z.string().min(1),
|
|
@@ -15419,7 +15535,7 @@ ${parsed.error.issues.slice(0, 5).map((i) => ` ${i.path.join(".")}: ${i.message
|
|
|
15419
15535
|
);
|
|
15420
15536
|
}
|
|
15421
15537
|
async function loadWorkspace(workspacePath) {
|
|
15422
|
-
if (!(0,
|
|
15538
|
+
if (!(0, import_node_fs24.existsSync)(workspacePath)) return { version: 1, projects: [] };
|
|
15423
15539
|
let raw;
|
|
15424
15540
|
try {
|
|
15425
15541
|
raw = JSON.parse(await (0, import_promises19.readFile)(workspacePath, "utf8"));
|
|
@@ -15436,7 +15552,7 @@ async function saveWorkspace(workspacePath, ws) {
|
|
|
15436
15552
|
version: 1,
|
|
15437
15553
|
projects: [...ws.projects].sort((a, b) => a.id.localeCompare(b.id))
|
|
15438
15554
|
};
|
|
15439
|
-
await (0, import_promises19.mkdir)(
|
|
15555
|
+
await (0, import_promises19.mkdir)(import_node_path27.default.dirname(import_node_path27.default.resolve(workspacePath)), { recursive: true });
|
|
15440
15556
|
const tmp = `${workspacePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
15441
15557
|
try {
|
|
15442
15558
|
await (0, import_promises19.writeFile)(tmp, JSON.stringify(sorted, null, 2) + "\n");
|
|
@@ -15446,12 +15562,12 @@ async function saveWorkspace(workspacePath, ws) {
|
|
|
15446
15562
|
}
|
|
15447
15563
|
}
|
|
15448
15564
|
function toPortablePath(dir, absolute) {
|
|
15449
|
-
return
|
|
15565
|
+
return import_node_path27.default.relative(dir, absolute).split(import_node_path27.default.sep).join("/");
|
|
15450
15566
|
}
|
|
15451
15567
|
function resolveProject(dir, project) {
|
|
15452
15568
|
return {
|
|
15453
|
-
manifestPath:
|
|
15454
|
-
lockPath:
|
|
15569
|
+
manifestPath: import_node_path27.default.resolve(dir, project.manifest.split("/").join(import_node_path27.default.sep)),
|
|
15570
|
+
lockPath: import_node_path27.default.resolve(dir, project.lock.split("/").join(import_node_path27.default.sep))
|
|
15455
15571
|
};
|
|
15456
15572
|
}
|
|
15457
15573
|
function validateWorkspace(ws, dir) {
|
|
@@ -15464,21 +15580,21 @@ function validateWorkspace(ws, dir) {
|
|
|
15464
15580
|
const { manifestPath, lockPath } = resolveProject(dir, project);
|
|
15465
15581
|
lockOwners.set(lockPath, [...lockOwners.get(lockPath) ?? [], project.id]);
|
|
15466
15582
|
manifestOwners.set(manifestPath, [...manifestOwners.get(manifestPath) ?? [], project.id]);
|
|
15467
|
-
if (
|
|
15583
|
+
if (import_node_path27.default.isAbsolute(project.manifest) || import_node_path27.default.isAbsolute(project.lock)) {
|
|
15468
15584
|
diagnostics.push({
|
|
15469
15585
|
id: "absolute-path",
|
|
15470
15586
|
level: "warning",
|
|
15471
15587
|
message: `project "${project.id}" stores an absolute path; the catalog will not resolve correctly if this tree is cloned or moved elsewhere`
|
|
15472
15588
|
});
|
|
15473
15589
|
}
|
|
15474
|
-
if (!(0,
|
|
15590
|
+
if (!(0, import_node_fs24.existsSync)(manifestPath)) {
|
|
15475
15591
|
diagnostics.push({
|
|
15476
15592
|
id: "missing-manifest",
|
|
15477
15593
|
level: "error",
|
|
15478
15594
|
message: `project "${project.id}" manifest not found: ${manifestPath}`
|
|
15479
15595
|
});
|
|
15480
15596
|
}
|
|
15481
|
-
if (!(0,
|
|
15597
|
+
if (!(0, import_node_fs24.existsSync)(lockPath)) {
|
|
15482
15598
|
diagnostics.push({
|
|
15483
15599
|
id: "missing-lock",
|
|
15484
15600
|
level: "error",
|
|
@@ -15526,15 +15642,15 @@ function validateWorkspace(ws, dir) {
|
|
|
15526
15642
|
|
|
15527
15643
|
// src/gallery/edit.ts
|
|
15528
15644
|
var import_promises25 = require("fs/promises");
|
|
15529
|
-
var
|
|
15645
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
15530
15646
|
var import_zod13 = require("zod");
|
|
15531
15647
|
|
|
15532
15648
|
// src/manifest-edit.ts
|
|
15533
15649
|
var import_promises20 = require("fs/promises");
|
|
15534
|
-
var
|
|
15650
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
15535
15651
|
var import_zod8 = require("zod");
|
|
15536
15652
|
var HexSha = import_zod8.z.string().regex(/^[0-9a-f]{64}$/);
|
|
15537
|
-
var ProjectFileSchema = import_zod8.z.string().min(1).refine((file) => !
|
|
15653
|
+
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
15654
|
var AssetPatchSchema = import_zod8.z.object({
|
|
15539
15655
|
prompt: import_zod8.z.string().optional(),
|
|
15540
15656
|
/** Per-style override for the style being viewed; null removes it. */
|
|
@@ -15953,7 +16069,7 @@ function applyEdit(raw, edit) {
|
|
|
15953
16069
|
}
|
|
15954
16070
|
}
|
|
15955
16071
|
async function withCandidate(manifestPath, edit, use, unchanged) {
|
|
15956
|
-
const absolute =
|
|
16072
|
+
const absolute = import_node_path28.default.resolve(manifestPath);
|
|
15957
16073
|
const original = await (0, import_promises20.readFile)(absolute, "utf8");
|
|
15958
16074
|
if (sha256(original) !== edit.expectedSha256) throw new ManifestDriftError(absolute);
|
|
15959
16075
|
let raw;
|
|
@@ -15967,9 +16083,9 @@ async function withCandidate(manifestPath, edit, use, unchanged) {
|
|
|
15967
16083
|
applyEdit(raw, edit);
|
|
15968
16084
|
const next = serializeLike(original, raw);
|
|
15969
16085
|
if (next === original) return unchanged(absolute, original);
|
|
15970
|
-
const tmp =
|
|
15971
|
-
|
|
15972
|
-
`.${
|
|
16086
|
+
const tmp = import_node_path28.default.join(
|
|
16087
|
+
import_node_path28.default.dirname(absolute),
|
|
16088
|
+
`.${import_node_path28.default.basename(absolute)}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`
|
|
15973
16089
|
);
|
|
15974
16090
|
try {
|
|
15975
16091
|
await (0, import_promises20.writeFile)(tmp, next);
|
|
@@ -15998,18 +16114,18 @@ async function applyManifestEdit(manifestPath, edit) {
|
|
|
15998
16114
|
}
|
|
15999
16115
|
|
|
16000
16116
|
// src/pipeline/hand-edit.ts
|
|
16001
|
-
var
|
|
16117
|
+
var import_node_fs25 = require("fs");
|
|
16002
16118
|
var import_promises21 = require("fs/promises");
|
|
16003
|
-
var
|
|
16119
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
16004
16120
|
var import_zod9 = require("zod");
|
|
16005
16121
|
var HAND_EDIT_DIR = "edits";
|
|
16006
16122
|
function handEditPath(loaded, spec) {
|
|
16007
16123
|
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
|
|
16124
|
+
const outDir = import_node_path29.default.resolve(loaded.root, style?.outDir ?? ".");
|
|
16125
|
+
const relative = import_node_path29.default.relative(outDir, spec.outFile);
|
|
16126
|
+
const inside = relative && !relative.startsWith("..") && !import_node_path29.default.isAbsolute(relative);
|
|
16127
|
+
const file = inside ? relative : import_node_path29.default.basename(spec.outFile);
|
|
16128
|
+
return import_node_path29.default.join(outDir, HAND_EDIT_DIR, file.replace(/\.gif$/i, ".png"));
|
|
16013
16129
|
}
|
|
16014
16130
|
function handEditBases(spec, lock) {
|
|
16015
16131
|
const entry = lock.entries[lockKey(spec.styleId, spec.assetId)];
|
|
@@ -16022,10 +16138,10 @@ function handEditBases(spec, lock) {
|
|
|
16022
16138
|
}));
|
|
16023
16139
|
if (members.some((member) => member.mediaType && member.mediaType !== "image/png")) return null;
|
|
16024
16140
|
if (entry.outputs.length === 1 && !primaryOutput(entry)) return null;
|
|
16025
|
-
if (!members.every((member) => (0,
|
|
16141
|
+
if (!members.every((member) => (0, import_node_fs25.existsSync)(member.path))) return null;
|
|
16026
16142
|
return members.map(({ role, path: file }) => ({ role, path: file }));
|
|
16027
16143
|
}
|
|
16028
|
-
return (0,
|
|
16144
|
+
return (0, import_node_fs25.existsSync)(spec.outFile) ? [{ role: null, path: spec.outFile }] : null;
|
|
16029
16145
|
}
|
|
16030
16146
|
function handEditBase(spec, lock) {
|
|
16031
16147
|
const bases = handEditBases(spec, lock);
|
|
@@ -16055,18 +16171,18 @@ async function startHandEdit(loaded, lock, spec, opts = {}) {
|
|
|
16055
16171
|
`${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
16172
|
);
|
|
16057
16173
|
}
|
|
16058
|
-
const existing = spec.source ?
|
|
16174
|
+
const existing = spec.source ? import_node_path29.default.resolve(loaded.root, spec.source) : null;
|
|
16059
16175
|
const editPath = existing ?? handEditPath(loaded, spec);
|
|
16060
16176
|
const members = handEditMembers(loaded, spec, lock, editPath);
|
|
16061
16177
|
let created = false;
|
|
16062
16178
|
for (const member of members) {
|
|
16063
|
-
if ((0,
|
|
16179
|
+
if ((0, import_node_fs25.existsSync)(member.path)) continue;
|
|
16064
16180
|
if (!member.base) {
|
|
16065
16181
|
throw new ManifestEditError(
|
|
16066
16182
|
`${spec.styleId}/${spec.assetId} has no generated PNG on disk to start from; generate or restore it first`
|
|
16067
16183
|
);
|
|
16068
16184
|
}
|
|
16069
|
-
await (0, import_promises21.mkdir)(
|
|
16185
|
+
await (0, import_promises21.mkdir)(import_node_path29.default.dirname(member.path), { recursive: true });
|
|
16070
16186
|
await (0, import_promises21.copyFile)(member.base, member.path);
|
|
16071
16187
|
created = true;
|
|
16072
16188
|
}
|
|
@@ -16204,7 +16320,7 @@ async function saveHandEdit(loaded, lock, spec, input) {
|
|
|
16204
16320
|
editor: input.editor,
|
|
16205
16321
|
protocol: input.protocol,
|
|
16206
16322
|
savedAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
16207
|
-
project: projectPath ?
|
|
16323
|
+
project: projectPath ? import_node_path29.default.basename(projectPath) : null,
|
|
16208
16324
|
outputs: incoming.map((frame, index) => ({
|
|
16209
16325
|
role: frame.role,
|
|
16210
16326
|
basedOn: basedOnHash(entry, index),
|
|
@@ -16246,9 +16362,9 @@ function openInEditor(file, editor = process.env.PIXELKILN_EDITOR) {
|
|
|
16246
16362
|
}
|
|
16247
16363
|
|
|
16248
16364
|
// src/gallery/upload.ts
|
|
16249
|
-
var
|
|
16365
|
+
var import_node_fs26 = require("fs");
|
|
16250
16366
|
var import_promises22 = require("fs/promises");
|
|
16251
|
-
var
|
|
16367
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
16252
16368
|
var import_zod10 = require("zod");
|
|
16253
16369
|
var MAX_UPLOAD_BYTES = 8 * 1024 * 1024;
|
|
16254
16370
|
var UploadImageSchema = import_zod10.z.object({
|
|
@@ -16261,12 +16377,12 @@ var UploadImageSchema = import_zod10.z.object({
|
|
|
16261
16377
|
overwrite: import_zod10.z.boolean().optional()
|
|
16262
16378
|
}).strict();
|
|
16263
16379
|
async function saveProjectImage(root, request) {
|
|
16264
|
-
const file =
|
|
16265
|
-
const relative =
|
|
16266
|
-
if (relative.startsWith("..") ||
|
|
16380
|
+
const file = import_node_path30.default.resolve(root, request.path);
|
|
16381
|
+
const relative = import_node_path30.default.relative(root, file);
|
|
16382
|
+
if (relative.startsWith("..") || import_node_path30.default.isAbsolute(relative)) {
|
|
16267
16383
|
throw new ManifestEditError("an upload must stay inside the project");
|
|
16268
16384
|
}
|
|
16269
|
-
const extension =
|
|
16385
|
+
const extension = import_node_path30.default.extname(file).toLowerCase();
|
|
16270
16386
|
const expected = extension === ".png" ? "png" : extension === ".jpg" || extension === ".jpeg" ? "jpeg" : null;
|
|
16271
16387
|
if (!expected) throw new ManifestEditError("an upload must be a .png, .jpg, or .jpeg path");
|
|
16272
16388
|
const bytes = Buffer.from(request.base64, "base64");
|
|
@@ -16278,18 +16394,18 @@ async function saveProjectImage(root, request) {
|
|
|
16278
16394
|
if (metadata.format !== expected) {
|
|
16279
16395
|
throw new ManifestEditError(`the upload is a ${metadata.format.toUpperCase()}; name it ${metadata.format === "png" ? ".png" : ".jpg"}`);
|
|
16280
16396
|
}
|
|
16281
|
-
if ((0,
|
|
16282
|
-
throw new ManifestEditError(`${relative.split(
|
|
16397
|
+
if ((0, import_node_fs26.existsSync)(file) && !request.overwrite) {
|
|
16398
|
+
throw new ManifestEditError(`${relative.split(import_node_path30.default.sep).join("/")} already exists; choose another name, or replace it`);
|
|
16283
16399
|
}
|
|
16284
|
-
await (0, import_promises22.mkdir)(
|
|
16400
|
+
await (0, import_promises22.mkdir)(import_node_path30.default.dirname(file), { recursive: true });
|
|
16285
16401
|
await (0, import_promises22.writeFile)(file, bytes);
|
|
16286
|
-
return relative.split(
|
|
16402
|
+
return relative.split(import_node_path30.default.sep).join("/");
|
|
16287
16403
|
}
|
|
16288
16404
|
|
|
16289
16405
|
// src/gallery/image-revision.ts
|
|
16290
|
-
var
|
|
16406
|
+
var import_node_fs27 = require("fs");
|
|
16291
16407
|
var import_promises23 = require("fs/promises");
|
|
16292
|
-
var
|
|
16408
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
16293
16409
|
var import_zod11 = require("zod");
|
|
16294
16410
|
var InpaintRevisionRequestSchema = import_zod11.z.object({
|
|
16295
16411
|
action: import_zod11.z.literal("create-inpaint-revision"),
|
|
@@ -16308,15 +16424,15 @@ var InpaintRevisionRequestSchema = import_zod11.z.object({
|
|
|
16308
16424
|
overwrite: import_zod11.z.boolean().optional()
|
|
16309
16425
|
}).strict();
|
|
16310
16426
|
async function createInpaintRevision(manifestPath, request) {
|
|
16311
|
-
const root =
|
|
16312
|
-
if (
|
|
16427
|
+
const root = import_node_path31.default.dirname(import_node_path31.default.resolve(manifestPath));
|
|
16428
|
+
if (import_node_path31.default.extname(request.maskPath).toLowerCase() !== ".png") {
|
|
16313
16429
|
throw new ManifestEditError("the mask must be a .png path inside the project");
|
|
16314
16430
|
}
|
|
16315
16431
|
const bytes = Buffer.from(request.maskBase64, "base64");
|
|
16316
16432
|
const metadata = imageMetadata(bytes);
|
|
16317
16433
|
if (!metadata || metadata.format !== "png") throw new ManifestEditError("the mask is not a readable PNG");
|
|
16318
|
-
const file =
|
|
16319
|
-
const previous = (0,
|
|
16434
|
+
const file = import_node_path31.default.resolve(root, request.maskPath);
|
|
16435
|
+
const previous = (0, import_node_fs27.existsSync)(file) ? await (0, import_promises23.readFile)(file) : null;
|
|
16320
16436
|
const mask = await saveProjectImage(root, {
|
|
16321
16437
|
action: "upload-image",
|
|
16322
16438
|
path: request.maskPath,
|
|
@@ -16343,9 +16459,9 @@ async function createInpaintRevision(manifestPath, request) {
|
|
|
16343
16459
|
}
|
|
16344
16460
|
|
|
16345
16461
|
// src/gallery/skeleton.ts
|
|
16346
|
-
var
|
|
16462
|
+
var import_node_fs28 = require("fs");
|
|
16347
16463
|
var import_promises24 = require("fs/promises");
|
|
16348
|
-
var
|
|
16464
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
16349
16465
|
var import_zod12 = require("zod");
|
|
16350
16466
|
var SkeletonEstimateRequestSchema = import_zod12.z.object({
|
|
16351
16467
|
project: import_zod12.z.string().min(1).optional(),
|
|
@@ -16383,20 +16499,20 @@ var SkeletonKeypointsUpdateSchema = import_zod12.z.object({
|
|
|
16383
16499
|
set: SkeletonSetSchema
|
|
16384
16500
|
}).strict();
|
|
16385
16501
|
async function createSkeletonAnimation(manifestPath, request) {
|
|
16386
|
-
const root =
|
|
16387
|
-
const file =
|
|
16388
|
-
const relative =
|
|
16389
|
-
if (!request.keypointsFile.endsWith(".json") || relative.startsWith("..") ||
|
|
16502
|
+
const root = import_node_path32.default.dirname(import_node_path32.default.resolve(manifestPath));
|
|
16503
|
+
const file = import_node_path32.default.resolve(root, request.keypointsFile);
|
|
16504
|
+
const relative = import_node_path32.default.relative(root, file);
|
|
16505
|
+
if (!request.keypointsFile.endsWith(".json") || relative.startsWith("..") || import_node_path32.default.isAbsolute(relative)) {
|
|
16390
16506
|
throw new ManifestEditError("the keypoints file must be a .json path inside the project");
|
|
16391
16507
|
}
|
|
16392
|
-
const existed = (0,
|
|
16508
|
+
const existed = (0, import_node_fs28.existsSync)(file);
|
|
16393
16509
|
let previous = null;
|
|
16394
16510
|
if (request.set) {
|
|
16395
16511
|
if (existed && !request.overwrite) {
|
|
16396
16512
|
throw new ManifestEditError(`${request.keypointsFile} already exists; choose another path, or replace it`);
|
|
16397
16513
|
}
|
|
16398
16514
|
if (existed) previous = await (0, import_promises24.readFile)(file);
|
|
16399
|
-
await (0, import_promises24.mkdir)(
|
|
16515
|
+
await (0, import_promises24.mkdir)(import_node_path32.default.dirname(file), { recursive: true });
|
|
16400
16516
|
await (0, import_promises24.writeFile)(file, JSON.stringify(request.set, null, 2) + "\n");
|
|
16401
16517
|
} else if (!existed) {
|
|
16402
16518
|
throw new ManifestEditError(`${request.keypointsFile} does not exist; paste poses or estimate them from the sprite`);
|
|
@@ -16412,7 +16528,7 @@ async function createSkeletonAnimation(manifestPath, request) {
|
|
|
16412
16528
|
revision: {
|
|
16413
16529
|
mode: "animate-skeleton",
|
|
16414
16530
|
from: request.from,
|
|
16415
|
-
keypointsFile: relative.split(
|
|
16531
|
+
keypointsFile: relative.split(import_node_path32.default.sep).join("/"),
|
|
16416
16532
|
direction: request.direction,
|
|
16417
16533
|
...request.description ? { description: request.description } : {},
|
|
16418
16534
|
...request.skeletonTemplate ? { skeletonTemplate: request.skeletonTemplate } : {}
|
|
@@ -16433,19 +16549,19 @@ async function updateSkeletonKeypoints(ctx, request) {
|
|
|
16433
16549
|
if (!spec) throw new ManifestEditError(`"${key}" is not declared by the manifest`);
|
|
16434
16550
|
const file = spec.revision?.mode === "animate-skeleton" ? spec.revision.keypointsFile : void 0;
|
|
16435
16551
|
if (!file) throw new ManifestEditError(`${key} is not an animate-skeleton revision`);
|
|
16436
|
-
const relative =
|
|
16437
|
-
if (relative.startsWith("..") ||
|
|
16552
|
+
const relative = import_node_path32.default.relative(ctx.loaded.root, file);
|
|
16553
|
+
if (relative.startsWith("..") || import_node_path32.default.isAbsolute(relative)) {
|
|
16438
16554
|
throw new ManifestEditError(`${key}'s keypoints file is outside the project; edit it by hand`);
|
|
16439
16555
|
}
|
|
16440
|
-
const current = (0,
|
|
16556
|
+
const current = (0, import_node_fs28.existsSync)(file) ? sha256(await (0, import_promises24.readFile)(file)) : null;
|
|
16441
16557
|
if (current !== request.expectedSha256) {
|
|
16442
16558
|
throw Object.assign(
|
|
16443
|
-
new Error(`${relative.split(
|
|
16559
|
+
new Error(`${relative.split(import_node_path32.default.sep).join("/")} changed on disk since this page loaded it; press Refresh and edit again`),
|
|
16444
16560
|
{ status: 409 }
|
|
16445
16561
|
);
|
|
16446
16562
|
}
|
|
16447
16563
|
await (0, import_promises24.writeFile)(file, JSON.stringify(request.set, null, 2) + "\n");
|
|
16448
|
-
return relative.split(
|
|
16564
|
+
return relative.split(import_node_path32.default.sep).join("/");
|
|
16449
16565
|
}
|
|
16450
16566
|
|
|
16451
16567
|
// src/gallery/edit.ts
|
|
@@ -16499,7 +16615,7 @@ function createGalleryEditHandler(opts) {
|
|
|
16499
16615
|
const history = /* @__PURE__ */ new Map();
|
|
16500
16616
|
const manifestOf = async (project) => {
|
|
16501
16617
|
try {
|
|
16502
|
-
return
|
|
16618
|
+
return import_node_path33.default.resolve(await opts.manifestFor(typeof project === "string" ? project : void 0));
|
|
16503
16619
|
} catch {
|
|
16504
16620
|
return null;
|
|
16505
16621
|
}
|
|
@@ -16522,7 +16638,7 @@ function createGalleryEditHandler(opts) {
|
|
|
16522
16638
|
await (0, import_promises25.writeFile)(tmp, last.before);
|
|
16523
16639
|
await (0, import_promises25.rename)(tmp, manifestPath2);
|
|
16524
16640
|
stack.pop();
|
|
16525
|
-
log(` manifest edit undone in ${
|
|
16641
|
+
log(` manifest edit undone in ${import_node_path33.default.relative(process.cwd(), manifestPath2) || manifestPath2}`);
|
|
16526
16642
|
return opts.reload();
|
|
16527
16643
|
}
|
|
16528
16644
|
const manifestPath = await manifestOf(body?.project);
|
|
@@ -16584,7 +16700,7 @@ function createEditApplier(opts, log) {
|
|
|
16584
16700
|
} catch (error) {
|
|
16585
16701
|
throw new ManifestEditError(error instanceof Error ? error.message : String(error));
|
|
16586
16702
|
}
|
|
16587
|
-
const written = await saveProjectImage(
|
|
16703
|
+
const written = await saveProjectImage(import_node_path33.default.dirname(import_node_path33.default.resolve(manifestPath2)), upload.data);
|
|
16588
16704
|
log(` image added: ${written}`);
|
|
16589
16705
|
return opts.reload();
|
|
16590
16706
|
}
|
|
@@ -16651,7 +16767,7 @@ function createEditApplier(opts, log) {
|
|
|
16651
16767
|
}
|
|
16652
16768
|
const result = await applyManifestEdit(manifestPath, parsed.data);
|
|
16653
16769
|
if (result.changed) {
|
|
16654
|
-
const relative =
|
|
16770
|
+
const relative = import_node_path33.default.relative(process.cwd(), result.manifestPath);
|
|
16655
16771
|
const shown = !relative || relative.startsWith("..") ? result.manifestPath : relative;
|
|
16656
16772
|
log(` manifest edited: ${describeEdit(parsed.data)} in ${shown}`);
|
|
16657
16773
|
}
|
|
@@ -16679,7 +16795,7 @@ function describeEdit(edit) {
|
|
|
16679
16795
|
// src/gallery/snapshot.ts
|
|
16680
16796
|
var PROJECT_FILE_TYPE = "application/octet-stream";
|
|
16681
16797
|
function galleryMediaId(absolutePath) {
|
|
16682
|
-
return (0,
|
|
16798
|
+
return (0, import_node_crypto7.createHash)("sha256").update(import_node_path34.default.resolve(absolutePath)).digest("hex").slice(0, 24);
|
|
16683
16799
|
}
|
|
16684
16800
|
function galleryMediaRoute(id) {
|
|
16685
16801
|
return `/media/${id}`;
|
|
@@ -16698,9 +16814,9 @@ function describeHistory(media, entry, cacheDir) {
|
|
|
16698
16814
|
return (entry.history ?? []).map((generation, i) => {
|
|
16699
16815
|
const outputs = generation.outputs.map((output) => {
|
|
16700
16816
|
const mediaType = output.mediaType ?? MediaType.PNG;
|
|
16701
|
-
const file =
|
|
16817
|
+
const file = import_node_path34.default.join(cacheDir, cacheFileName(output.sha256, mediaType));
|
|
16702
16818
|
let url = null;
|
|
16703
|
-
if ((0,
|
|
16819
|
+
if ((0, import_node_fs29.existsSync)(file)) {
|
|
16704
16820
|
const id = galleryMediaId(file);
|
|
16705
16821
|
media.set(id, { path: file, contentType: mediaType });
|
|
16706
16822
|
url = `${galleryMediaRoute(id)}?v=${output.sha256.slice(0, 24)}`;
|
|
@@ -16830,10 +16946,10 @@ async function describeOutput(media, root, absolutePath, recorded = {}) {
|
|
|
16830
16946
|
async function describeQuality(media, root, inspection) {
|
|
16831
16947
|
const outputPaths = inspection.outputs ?? [inspection.output];
|
|
16832
16948
|
const outputs = await Promise.all(
|
|
16833
|
-
outputPaths.map((file) => describeOutput(media, root,
|
|
16949
|
+
outputPaths.map((file) => describeOutput(media, root, import_node_path34.default.resolve(root, file)))
|
|
16834
16950
|
);
|
|
16835
|
-
const recordPath =
|
|
16836
|
-
const recordExists = (0,
|
|
16951
|
+
const recordPath = import_node_path34.default.resolve(root, inspection.record);
|
|
16952
|
+
const recordExists = (0, import_node_fs29.existsSync)(recordPath);
|
|
16837
16953
|
let options = null;
|
|
16838
16954
|
let check = null;
|
|
16839
16955
|
if (recordExists) {
|
|
@@ -16852,7 +16968,7 @@ async function describeQuality(media, root, inspection) {
|
|
|
16852
16968
|
return {
|
|
16853
16969
|
state: inspection.state,
|
|
16854
16970
|
reason: inspection.reason,
|
|
16855
|
-
output: portableOutputPath(
|
|
16971
|
+
output: portableOutputPath(import_node_path34.default.resolve(root, inspection.output), root),
|
|
16856
16972
|
record: portableOutputPath(recordPath, root),
|
|
16857
16973
|
recordExists,
|
|
16858
16974
|
outputs,
|
|
@@ -16892,7 +17008,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
16892
17008
|
const items = [];
|
|
16893
17009
|
const manifestText = await (0, import_promises26.readFile)(loaded.path, "utf8");
|
|
16894
17010
|
const manifestSha256 = sha256(manifestText);
|
|
16895
|
-
const cacheDir =
|
|
17011
|
+
const cacheDir = import_node_path34.default.resolve(import_node_path34.default.dirname(opts.lockPath), ".pixelkiln", "cache");
|
|
16896
17012
|
let rawStyles = {};
|
|
16897
17013
|
try {
|
|
16898
17014
|
const raw = JSON.parse(manifestText);
|
|
@@ -16916,12 +17032,12 @@ async function buildGallerySnapshot(opts) {
|
|
|
16916
17032
|
)
|
|
16917
17033
|
);
|
|
16918
17034
|
if (spec.source) {
|
|
16919
|
-
const editPath =
|
|
17035
|
+
const editPath = import_node_path34.default.resolve(root, spec.source);
|
|
16920
17036
|
const memberCount = sourceIsStem(spec.source, entry, root) ? entry.outputs.length : 1;
|
|
16921
17037
|
const editShas = await Promise.all(
|
|
16922
17038
|
Array.from({ length: memberCount }, async (_, index) => {
|
|
16923
17039
|
const file = sourceOutputPath(spec.source, entry, index, root);
|
|
16924
|
-
return { file, sha256: (0,
|
|
17040
|
+
return { file, sha256: (0, import_node_fs29.existsSync)(file) ? await sha256File(file) : null };
|
|
16925
17041
|
})
|
|
16926
17042
|
);
|
|
16927
17043
|
edits = await Promise.all(editShas.map(({ file, sha256: editSha }) => describeOutput(media, root, file, { sha256: editSha })));
|
|
@@ -16929,7 +17045,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
16929
17045
|
const companion = await readHandEditCompanion(editPath);
|
|
16930
17046
|
if (companion) {
|
|
16931
17047
|
const projectPath = handEditProjectPath(editPath);
|
|
16932
|
-
const project = companion.project && (0,
|
|
17048
|
+
const project = companion.project && (0, import_node_fs29.existsSync)(projectPath) ? await fileInfo(projectPath) : null;
|
|
16933
17049
|
let projectUrl = null;
|
|
16934
17050
|
if (project) {
|
|
16935
17051
|
const id = galleryMediaId(projectPath);
|
|
@@ -16965,8 +17081,8 @@ async function buildGallerySnapshot(opts) {
|
|
|
16965
17081
|
editStatus = edits.some((member) => !member.exists) ? "missing" : editChanged.every((changed) => !changed) ? "same" : regenerated ? "regenerated-since" : "edited";
|
|
16966
17082
|
}
|
|
16967
17083
|
} else if (spec.source) {
|
|
16968
|
-
outputs = [await describeOutput(media, root,
|
|
16969
|
-
} else if (!entry && (0,
|
|
17084
|
+
outputs = [await describeOutput(media, root, import_node_path34.default.resolve(root, spec.source))];
|
|
17085
|
+
} else if (!entry && (0, import_node_fs29.existsSync)(spec.outFile)) {
|
|
16970
17086
|
outputs = [await describeOutput(media, root, spec.outFile)];
|
|
16971
17087
|
} else {
|
|
16972
17088
|
outputs = [];
|
|
@@ -17169,7 +17285,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
17169
17285
|
id: loaded.manifest.name,
|
|
17170
17286
|
name: loaded.manifest.name,
|
|
17171
17287
|
manifest: loaded.path,
|
|
17172
|
-
lock:
|
|
17288
|
+
lock: import_node_path34.default.resolve(opts.lockPath),
|
|
17173
17289
|
root,
|
|
17174
17290
|
provider: loaded.manifest.provider,
|
|
17175
17291
|
account: null,
|
|
@@ -17195,7 +17311,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
17195
17311
|
return { snapshot, media };
|
|
17196
17312
|
}
|
|
17197
17313
|
async function buildWorkspaceGallerySnapshot(opts) {
|
|
17198
|
-
const dir =
|
|
17314
|
+
const dir = import_node_path34.default.dirname(import_node_path34.default.resolve(opts.workspacePath));
|
|
17199
17315
|
const media = /* @__PURE__ */ new Map();
|
|
17200
17316
|
const projects = [];
|
|
17201
17317
|
const items = [];
|
|
@@ -17238,7 +17354,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
17238
17354
|
id: project.id,
|
|
17239
17355
|
name: loaded.manifest.name,
|
|
17240
17356
|
manifest: manifestPath,
|
|
17241
|
-
lock:
|
|
17357
|
+
lock: import_node_path34.default.resolve(lockPath),
|
|
17242
17358
|
root: loaded.root,
|
|
17243
17359
|
provider: loaded.manifest.provider,
|
|
17244
17360
|
account: project.account ?? null,
|
|
@@ -17254,8 +17370,8 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
17254
17370
|
id: project.id,
|
|
17255
17371
|
name: project.id,
|
|
17256
17372
|
manifest: manifestPath,
|
|
17257
|
-
lock:
|
|
17258
|
-
root:
|
|
17373
|
+
lock: import_node_path34.default.resolve(lockPath),
|
|
17374
|
+
root: import_node_path34.default.dirname(manifestPath),
|
|
17259
17375
|
provider: project.provider,
|
|
17260
17376
|
account: project.account ?? null,
|
|
17261
17377
|
manifestSha256: null,
|
|
@@ -17282,7 +17398,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
17282
17398
|
version: 1,
|
|
17283
17399
|
generatedAt: (opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
|
|
17284
17400
|
project: null,
|
|
17285
|
-
workspace: { path:
|
|
17401
|
+
workspace: { path: import_node_path34.default.resolve(opts.workspacePath), dir, projects },
|
|
17286
17402
|
filter,
|
|
17287
17403
|
totals: {
|
|
17288
17404
|
entries: projects.reduce((sum, project) => sum + project.entries, 0),
|
|
@@ -17299,7 +17415,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
17299
17415
|
}
|
|
17300
17416
|
|
|
17301
17417
|
// src/gallery/page.ts
|
|
17302
|
-
var
|
|
17418
|
+
var import_node_fs30 = require("fs");
|
|
17303
17419
|
|
|
17304
17420
|
// src/gallery/client-bundle.ts
|
|
17305
17421
|
var import_node_module = require("module");
|
|
@@ -17339,8 +17455,8 @@ var client;
|
|
|
17339
17455
|
function clientAssets() {
|
|
17340
17456
|
const prebuilt = new URL("gallery.js", CLIENT_DIR);
|
|
17341
17457
|
client ??= {
|
|
17342
|
-
css: (0,
|
|
17343
|
-
js: (0,
|
|
17458
|
+
css: (0, import_node_fs30.readFileSync)(new URL("gallery.css", CLIENT_DIR), "utf8"),
|
|
17459
|
+
js: (0, import_node_fs30.existsSync)(prebuilt) ? (0, import_node_fs30.readFileSync)(prebuilt, "utf8") : bundleGalleryClient()
|
|
17344
17460
|
};
|
|
17345
17461
|
return client;
|
|
17346
17462
|
}
|
|
@@ -17482,11 +17598,11 @@ var EDITOR_PIN = pin_default;
|
|
|
17482
17598
|
var EDITOR_RELEASE_BASE = "https://github.com/gfargo/pixelkiln/releases/download";
|
|
17483
17599
|
|
|
17484
17600
|
// src/editor/install.ts
|
|
17485
|
-
var
|
|
17486
|
-
var
|
|
17601
|
+
var import_node_crypto8 = require("crypto");
|
|
17602
|
+
var import_node_fs31 = require("fs");
|
|
17487
17603
|
var import_promises27 = require("fs/promises");
|
|
17488
|
-
var
|
|
17489
|
-
var
|
|
17604
|
+
var import_node_os4 = __toESM(require("os"), 1);
|
|
17605
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
17490
17606
|
var EditorInstallError = class extends Error {
|
|
17491
17607
|
constructor(message6, file) {
|
|
17492
17608
|
super(message6);
|
|
@@ -17497,15 +17613,15 @@ var EditorInstallError = class extends Error {
|
|
|
17497
17613
|
};
|
|
17498
17614
|
function editorToolsRoot() {
|
|
17499
17615
|
const override = process.env.PIXELKILN_TOOLS_DIR?.trim();
|
|
17500
|
-
if (override) return
|
|
17501
|
-
if (process.platform === "darwin") return
|
|
17616
|
+
if (override) return import_node_path35.default.resolve(override);
|
|
17617
|
+
if (process.platform === "darwin") return import_node_path35.default.join(import_node_os4.default.homedir(), "Library", "Caches", "pixelkiln", "tools");
|
|
17502
17618
|
if (process.platform === "win32") {
|
|
17503
|
-
return
|
|
17619
|
+
return import_node_path35.default.join(process.env.LOCALAPPDATA ?? import_node_path35.default.join(import_node_os4.default.homedir(), "AppData", "Local"), "pixelkiln", "tools");
|
|
17504
17620
|
}
|
|
17505
|
-
return
|
|
17621
|
+
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
17622
|
}
|
|
17507
17623
|
function editorDir(pin = EDITOR_PIN, root = editorToolsRoot()) {
|
|
17508
|
-
return
|
|
17624
|
+
return import_node_path35.default.join(root, "pixelorama", pin.release ?? "unpublished");
|
|
17509
17625
|
}
|
|
17510
17626
|
function editorBaseUrl(pin = EDITOR_PIN) {
|
|
17511
17627
|
const override = process.env.PIXELKILN_EDITOR_URL?.trim();
|
|
@@ -17515,7 +17631,7 @@ function editorBaseUrl(pin = EDITOR_PIN) {
|
|
|
17515
17631
|
}
|
|
17516
17632
|
async function sha256Of(file) {
|
|
17517
17633
|
try {
|
|
17518
|
-
return (0,
|
|
17634
|
+
return (0, import_node_crypto8.createHash)("sha256").update(await (0, import_promises27.readFile)(file)).digest("hex");
|
|
17519
17635
|
} catch {
|
|
17520
17636
|
return null;
|
|
17521
17637
|
}
|
|
@@ -17528,8 +17644,8 @@ async function editorStatus(opts = {}) {
|
|
|
17528
17644
|
let totalBytes = 0;
|
|
17529
17645
|
for (const [name, expected] of Object.entries(pin.files)) {
|
|
17530
17646
|
totalBytes += expected.bytes;
|
|
17531
|
-
const file =
|
|
17532
|
-
const info = (0,
|
|
17647
|
+
const file = import_node_path35.default.join(dir, name);
|
|
17648
|
+
const info = (0, import_node_fs31.existsSync)(file) ? await (0, import_promises27.stat)(file).catch(() => null) : null;
|
|
17533
17649
|
if (info && info.size === expected.bytes && await sha256Of(file) === expected.sha256) {
|
|
17534
17650
|
installedBytes += expected.bytes;
|
|
17535
17651
|
} else {
|
|
@@ -17582,11 +17698,11 @@ async function installEditor(opts = {}) {
|
|
|
17582
17698
|
if (bytes.length !== expected.bytes) {
|
|
17583
17699
|
throw new EditorInstallError(`${name}: expected ${expected.bytes} bytes, got ${bytes.length}`, name);
|
|
17584
17700
|
}
|
|
17585
|
-
const digest2 = (0,
|
|
17701
|
+
const digest2 = (0, import_node_crypto8.createHash)("sha256").update(bytes).digest("hex");
|
|
17586
17702
|
if (digest2 !== expected.sha256) {
|
|
17587
17703
|
throw new EditorInstallError(`${name}: sha256 ${digest2.slice(0, 12)}\u2026 does not match the pinned ${expected.sha256.slice(0, 12)}\u2026`, name);
|
|
17588
17704
|
}
|
|
17589
|
-
const final =
|
|
17705
|
+
const final = import_node_path35.default.join(dir, name);
|
|
17590
17706
|
const tmp = `${final}.${process.pid}.part`;
|
|
17591
17707
|
try {
|
|
17592
17708
|
await (0, import_promises27.writeFile)(tmp, bytes);
|
|
@@ -17632,12 +17748,12 @@ var EDITOR_CONTENT_TYPES = {
|
|
|
17632
17748
|
".json": "application/json; charset=utf-8"
|
|
17633
17749
|
};
|
|
17634
17750
|
function editorContentType(name) {
|
|
17635
|
-
return EDITOR_CONTENT_TYPES[
|
|
17751
|
+
return EDITOR_CONTENT_TYPES[import_node_path35.default.extname(name).toLowerCase()] ?? null;
|
|
17636
17752
|
}
|
|
17637
17753
|
|
|
17638
17754
|
// src/gallery/editor.ts
|
|
17639
17755
|
var import_promises28 = require("fs/promises");
|
|
17640
|
-
var
|
|
17756
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
17641
17757
|
function editorRoute(release, name = "index.html") {
|
|
17642
17758
|
return `/editor/${encodeURIComponent(release)}/${encodeURIComponent(name)}`;
|
|
17643
17759
|
}
|
|
@@ -17710,7 +17826,7 @@ function createGalleryEditorHandlers(opts = {}) {
|
|
|
17710
17826
|
if (!status.installed) return null;
|
|
17711
17827
|
let bytes;
|
|
17712
17828
|
try {
|
|
17713
|
-
bytes = await (0, import_promises28.readFile)(
|
|
17829
|
+
bytes = await (0, import_promises28.readFile)(import_node_path36.default.join(dir, name));
|
|
17714
17830
|
} catch {
|
|
17715
17831
|
verified = null;
|
|
17716
17832
|
return null;
|
|
@@ -17725,7 +17841,7 @@ function createGalleryEditorHandlers(opts = {}) {
|
|
|
17725
17841
|
}
|
|
17726
17842
|
|
|
17727
17843
|
// src/gallery/server.ts
|
|
17728
|
-
var
|
|
17844
|
+
var import_node_crypto9 = require("crypto");
|
|
17729
17845
|
var import_node_http2 = require("http");
|
|
17730
17846
|
var import_promises29 = require("fs/promises");
|
|
17731
17847
|
var MAX_EDIT_BYTES = 48 * 1024 * 1024;
|
|
@@ -17755,7 +17871,7 @@ async function readJsonBody(req) {
|
|
|
17755
17871
|
async function serveGallery(opts) {
|
|
17756
17872
|
const log = opts.onProgress ?? (() => {
|
|
17757
17873
|
});
|
|
17758
|
-
const session = opts.edit || opts.generate || opts.editor || opts.skeleton || opts.price ? (0,
|
|
17874
|
+
const session = opts.edit || opts.generate || opts.editor || opts.skeleton || opts.price ? (0, import_node_crypto9.randomBytes)(16).toString("hex") : null;
|
|
17759
17875
|
let media = /* @__PURE__ */ new Map();
|
|
17760
17876
|
let loading = null;
|
|
17761
17877
|
const reviewAssets = /* @__PURE__ */ new Map();
|
|
@@ -17959,7 +18075,7 @@ async function serveGallery(opts) {
|
|
|
17959
18075
|
try {
|
|
17960
18076
|
if (url2.pathname === "/") {
|
|
17961
18077
|
const { snapshot } = await load();
|
|
17962
|
-
const nonce = (0,
|
|
18078
|
+
const nonce = (0, import_node_crypto9.randomBytes)(16).toString("base64");
|
|
17963
18079
|
res.writeHead(200, {
|
|
17964
18080
|
"Content-Type": "text/html; charset=utf-8",
|
|
17965
18081
|
"Cache-Control": "no-store",
|
|
@@ -18034,7 +18150,7 @@ async function serveGallery(opts) {
|
|
|
18034
18150
|
}
|
|
18035
18151
|
|
|
18036
18152
|
// src/gallery/generate.ts
|
|
18037
|
-
var
|
|
18153
|
+
var import_node_crypto10 = require("crypto");
|
|
18038
18154
|
var import_zod14 = require("zod");
|
|
18039
18155
|
var GenerateRequestSchema = import_zod14.z.object({
|
|
18040
18156
|
project: import_zod14.z.string().min(1).optional(),
|
|
@@ -18334,7 +18450,7 @@ function createGenerateHandlers(opts) {
|
|
|
18334
18450
|
}
|
|
18335
18451
|
}
|
|
18336
18452
|
const job = {
|
|
18337
|
-
id: (0,
|
|
18453
|
+
id: (0, import_node_crypto10.randomBytes)(8).toString("hex"),
|
|
18338
18454
|
project: request.project ?? null,
|
|
18339
18455
|
keys: request.keys,
|
|
18340
18456
|
mode: request.refresh ? "refresh" : request.resume ? "resume" : request.restore ? "restore" : request.revert !== void 0 ? "revert" : "generate",
|
|
@@ -18402,15 +18518,15 @@ function createGenerateHandlers(opts) {
|
|
|
18402
18518
|
|
|
18403
18519
|
// src/pipeline/salvage.ts
|
|
18404
18520
|
var import_promises30 = require("fs/promises");
|
|
18405
|
-
var
|
|
18406
|
-
var
|
|
18521
|
+
var import_node_fs32 = require("fs");
|
|
18522
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
18407
18523
|
function providerClaimId(provider, objectId) {
|
|
18408
18524
|
return `${encodeURIComponent(provider)}:${encodeURIComponent(objectId)}`;
|
|
18409
18525
|
}
|
|
18410
18526
|
async function loadClaims(lockPaths, opts = {}) {
|
|
18411
18527
|
const claimed = /* @__PURE__ */ new Set();
|
|
18412
18528
|
for (const p of lockPaths) {
|
|
18413
|
-
if (!(0,
|
|
18529
|
+
if (!(0, import_node_fs32.existsSync)(p)) throw new Error(`Claim lockfile not found: ${p}`);
|
|
18414
18530
|
let parsed;
|
|
18415
18531
|
try {
|
|
18416
18532
|
parsed = parseLock(JSON.parse(await (0, import_promises30.readFile)(p, "utf8")));
|
|
@@ -18517,20 +18633,20 @@ function matchOrphanStyle(prompt, manifest) {
|
|
|
18517
18633
|
return matchStyleByPattern(prompt, manifest);
|
|
18518
18634
|
}
|
|
18519
18635
|
async function loadSiblingManifests(ownManifestPath, workspaceManifestPaths, claimPaths) {
|
|
18520
|
-
const own =
|
|
18636
|
+
const own = import_node_path37.default.resolve(ownManifestPath);
|
|
18521
18637
|
const siblingManifestPaths = [
|
|
18522
18638
|
.../* @__PURE__ */ new Set([
|
|
18523
18639
|
...workspaceManifestPaths,
|
|
18524
|
-
...claimPaths.map((c) =>
|
|
18640
|
+
...claimPaths.map((c) => import_node_path37.default.join(import_node_path37.default.dirname(import_node_path37.default.resolve(c)), "pixelkiln.manifest.json"))
|
|
18525
18641
|
])
|
|
18526
18642
|
];
|
|
18527
18643
|
const siblings = [];
|
|
18528
18644
|
for (const siblingManifestPath of siblingManifestPaths) {
|
|
18529
|
-
if (
|
|
18530
|
-
if (!(0,
|
|
18645
|
+
if (import_node_path37.default.resolve(siblingManifestPath) === own) continue;
|
|
18646
|
+
if (!(0, import_node_fs32.existsSync)(siblingManifestPath)) continue;
|
|
18531
18647
|
try {
|
|
18532
18648
|
const { manifest } = await loadManifest(siblingManifestPath);
|
|
18533
|
-
siblings.push({ label:
|
|
18649
|
+
siblings.push({ label: import_node_path37.default.basename(import_node_path37.default.dirname(siblingManifestPath)), manifest });
|
|
18534
18650
|
} catch {
|
|
18535
18651
|
}
|
|
18536
18652
|
}
|
|
@@ -18651,7 +18767,7 @@ async function applyTags(provider, decisions, existing, opts = {}) {
|
|
|
18651
18767
|
|
|
18652
18768
|
// src/pick/salvage-server.ts
|
|
18653
18769
|
var import_promises31 = require("fs/promises");
|
|
18654
|
-
var
|
|
18770
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
18655
18771
|
|
|
18656
18772
|
// src/pick/salvage-sheet.ts
|
|
18657
18773
|
var escapeHtml3 = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
@@ -18817,7 +18933,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
18817
18933
|
});
|
|
18818
18934
|
const html = renderSalvageSheet(orphans, {
|
|
18819
18935
|
styleId: ctx.styleId,
|
|
18820
|
-
importDir:
|
|
18936
|
+
importDir: import_node_path38.default.relative(process.cwd(), ctx.importDir) || "."
|
|
18821
18937
|
});
|
|
18822
18938
|
const byId = new Map(orphans.map((o) => [o.id, o]));
|
|
18823
18939
|
const existingTags = new Map(orphans.map((o) => [o.id, o.tags]));
|
|
@@ -18873,9 +18989,9 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
18873
18989
|
if (!buf.subarray(0, 8).equals(PNG_SIGNATURE2)) throw new Error("not a PNG");
|
|
18874
18990
|
decodePng(buf);
|
|
18875
18991
|
const assetId = idFromPrompt(orphan.prompt, taken);
|
|
18876
|
-
const rel =
|
|
18877
|
-
const outFile =
|
|
18878
|
-
await (0, import_promises31.mkdir)(
|
|
18992
|
+
const rel = import_node_path38.default.join("_salvaged", `${assetId}.png`);
|
|
18993
|
+
const outFile = import_node_path38.default.resolve(ctx.importDir, rel);
|
|
18994
|
+
await (0, import_promises31.mkdir)(import_node_path38.default.dirname(outFile), { recursive: true });
|
|
18879
18995
|
await (0, import_promises31.writeFile)(outFile, buf);
|
|
18880
18996
|
ctx.manifest.assets[assetId] = {
|
|
18881
18997
|
prompt: orphan.prompt,
|
|
@@ -18908,7 +19024,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
18908
19024
|
sourceUrl: durableSource,
|
|
18909
19025
|
sourceUrls: durableSource ? [{ url: durableSource }] : [],
|
|
18910
19026
|
outputs: [{
|
|
18911
|
-
path: portableOutputPath(outFile,
|
|
19027
|
+
path: portableOutputPath(outFile, import_node_path38.default.dirname(ctx.manifestPath)),
|
|
18912
19028
|
sha256: sha256(buf)
|
|
18913
19029
|
}],
|
|
18914
19030
|
submittedAt: orphan.createdAt,
|