pixelkiln 0.17.0 → 0.18.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/README.md +2 -2
- package/dist/cli.js +174 -34
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +174 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -3
- package/dist/index.d.ts +52 -3
- package/dist/index.js +174 -34
- package/dist/index.js.map +1 -1
- package/docs/ARCHITECTURE.md +14 -4
- package/docs/COMFYUI.md +70 -4
- package/docs/LIBRARY.md +12 -2
- package/docs/MANIFEST.md +41 -5
- package/package.json +1 -1
- package/schema/manifest.schema.json +17 -0
- package/skills/pixelkiln/references/comfyui.md +15 -1
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ PixelKiln keeps the missing record:
|
|
|
56
56
|
|---|---|
|
|
57
57
|
| Plan and budget | Offline manifest/lock/disk diff, provider-grouped estimates, keyed mixed-provider budget ceilings, JSON/CI gate. |
|
|
58
58
|
| Generate and review | Resumable submit/poll/pick/fetch pipeline, exact next-step hints, and a fast local candidate sheet. |
|
|
59
|
-
| Controlled
|
|
59
|
+
| Controlled inputs | Hashed image-to-image/inpaint lineage, fail-closed parent approval, source-versus-candidate review, and content-addressed per-asset ComfyUI bindings. |
|
|
60
60
|
| Existing-art onboarding | Manifest scaffolding, exact-hash account adoption, and prompt recovery. |
|
|
61
61
|
| Recovery | Safe stale-output replacement, validated caches, durable references, and resumable paid jobs. |
|
|
62
62
|
| Shared-account safety | Cross-project claim files or a registered workspace catalog, sibling-style exclusion, reviewed salvage, keep/discard tags, separate confirmed purge. |
|
|
@@ -356,7 +356,7 @@ exporters, managed artifact writes, and offline provenance verification. See
|
|
|
356
356
|
| [Getting started](./docs/GETTING_STARTED.md) | First project, existing-art onboarding, everyday workflow, and what to commit. |
|
|
357
357
|
| [Set up PixelLab](./docs/PIXELLAB.md) | Production-provider credentials, manifest, generators, and account workflows. |
|
|
358
358
|
| [Set up Retro Diffusion](./docs/RETRO_DIFFUSION.md) | Experimental-provider credentials, styles, formats, cost checks, and limits. |
|
|
359
|
-
| [Set up ComfyUI](./docs/COMFYUI.md) |
|
|
359
|
+
| [Set up ComfyUI](./docs/COMFYUI.md) | Self-hosted workflows, per-asset input uploads, local cost semantics, and quality limits. |
|
|
360
360
|
| [Set up Scenario](./docs/SCENARIO.md) | Experimental hosted models, two-part credentials, CU preflight, review, and durable downloads. |
|
|
361
361
|
| [Versioned recipes](./docs/RECIPES.md) | Pinned workflow packs, model hashes, manifest templates, and quality contracts. |
|
|
362
362
|
| [Controlled revisions](./docs/REVISIONS.md) | Image-to-image/inpaint parents, masks, fail-closed readiness, provenance, and ComfyUI bindings. |
|
package/dist/cli.js
CHANGED
|
@@ -123,13 +123,14 @@ async function sha256File(path25) {
|
|
|
123
123
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
124
124
|
});
|
|
125
125
|
}
|
|
126
|
-
function specHash(spec, styleImageHashes, providerOptionIdentity = spec.providerOptions) {
|
|
126
|
+
function specHash(spec, styleImageHashes, providerOptionIdentity = spec.providerOptions, providerInputIdentity = spec.providerInputs ?? {}) {
|
|
127
127
|
return sha256(
|
|
128
128
|
JSON.stringify({
|
|
129
129
|
// Preserve every existing PixelLab hash while making a provider switch
|
|
130
130
|
// invalidate the spec. Older manifests implicitly mean pixellab.
|
|
131
131
|
provider: spec.provider === "pixellab" ? void 0 : spec.provider,
|
|
132
132
|
providerOptions: providerOptionIdentity && (typeof providerOptionIdentity !== "object" || Object.keys(providerOptionIdentity).length > 0) ? providerOptionIdentity : void 0,
|
|
133
|
+
providerInputs: providerInputIdentity && (typeof providerInputIdentity !== "object" || Object.keys(providerInputIdentity).length > 0) ? providerInputIdentity : void 0,
|
|
133
134
|
generator: spec.generator,
|
|
134
135
|
prompt: spec.prompt,
|
|
135
136
|
width: spec.width,
|
|
@@ -604,6 +605,16 @@ function cacheFileName(hash, mediaType = MediaType.PNG) {
|
|
|
604
605
|
|
|
605
606
|
// src/providers/comfyui.ts
|
|
606
607
|
var DEFAULT_BASE_URL = "http://127.0.0.1:8188";
|
|
608
|
+
var BUILTIN_BINDINGS = /* @__PURE__ */ new Set([
|
|
609
|
+
"prompt",
|
|
610
|
+
"width",
|
|
611
|
+
"height",
|
|
612
|
+
"batchSize",
|
|
613
|
+
"seed",
|
|
614
|
+
"sourceImage",
|
|
615
|
+
"maskImage",
|
|
616
|
+
"strength"
|
|
617
|
+
]);
|
|
607
618
|
var ComfyUIClient = class {
|
|
608
619
|
constructor(baseUrl = process.env.COMFYUI_BASE_URL ?? DEFAULT_BASE_URL, request = fetch) {
|
|
609
620
|
this.request = request;
|
|
@@ -753,6 +764,63 @@ var ComfyUIProvider = class _ComfyUIProvider {
|
|
|
753
764
|
}
|
|
754
765
|
};
|
|
755
766
|
}
|
|
767
|
+
async resolveInputs(value, context) {
|
|
768
|
+
const options = parseOptions(context.providerOptions);
|
|
769
|
+
if (!options.workflow || !options.workflowSha256) {
|
|
770
|
+
throw new Error("ComfyUI provider inputs require resolved workflow options");
|
|
771
|
+
}
|
|
772
|
+
const inputs = {};
|
|
773
|
+
const identity = {};
|
|
774
|
+
for (const [name, input] of Object.entries(value)) {
|
|
775
|
+
if (BUILTIN_BINDINGS.has(name)) {
|
|
776
|
+
throw new Error(
|
|
777
|
+
`ComfyUI providerInputs.${name} is reserved; use PixelKiln's built-in ${name} field`
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
const binding = Object.hasOwn(options.bindings, name) ? options.bindings[name] : void 0;
|
|
781
|
+
if (!binding) {
|
|
782
|
+
throw new Error(
|
|
783
|
+
`ComfyUI providerInputs.${name} has no matching bindings.${name} target`
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
if (isImageBinding(options.workflow, binding)) {
|
|
787
|
+
if (typeof input !== "string") {
|
|
788
|
+
throw new Error(`ComfyUI providerInputs.${name} must be a manifest-relative PNG or JPEG path`);
|
|
789
|
+
}
|
|
790
|
+
const file = path2.resolve(context.root, input);
|
|
791
|
+
let bytes;
|
|
792
|
+
try {
|
|
793
|
+
bytes = await readFile(file);
|
|
794
|
+
} catch (error) {
|
|
795
|
+
throw new Error(
|
|
796
|
+
`ComfyUI providerInputs.${name} could not be read at ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
const format = inputImageFormat(bytes);
|
|
800
|
+
if (!format) {
|
|
801
|
+
throw new Error(`ComfyUI providerInputs.${name} is not a PNG or JPEG: ${file}`);
|
|
802
|
+
}
|
|
803
|
+
const hash = sha256(bytes);
|
|
804
|
+
inputs[name] = { kind: "image", path: file, sha256: hash, format };
|
|
805
|
+
identity[name] = { kind: "image", sha256: hash, format };
|
|
806
|
+
} else {
|
|
807
|
+
const current = options.workflow[binding.nodeId].inputs[binding.input];
|
|
808
|
+
if (typeof current !== "string" && typeof current !== "number" && typeof current !== "boolean") {
|
|
809
|
+
throw new Error(
|
|
810
|
+
`ComfyUI providerInputs.${name} cannot replace non-scalar ${binding.nodeId}.${binding.input}`
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
if (typeof current !== typeof input) {
|
|
814
|
+
throw new Error(
|
|
815
|
+
`ComfyUI providerInputs.${name} must be ${typeof current} to match ${binding.nodeId}.${binding.input}`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
inputs[name] = input;
|
|
819
|
+
identity[name] = input;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return { inputs, identity };
|
|
823
|
+
}
|
|
756
824
|
supports(generator) {
|
|
757
825
|
return generator === "map";
|
|
758
826
|
}
|
|
@@ -820,6 +888,20 @@ var ComfyUIProvider = class _ComfyUIProvider {
|
|
|
820
888
|
if (spec.seed != null && options.bindings.seed) {
|
|
821
889
|
setBinding(workflow, options.bindings.seed, spec.seed);
|
|
822
890
|
}
|
|
891
|
+
for (const [name, input] of Object.entries(spec.providerInputs ?? {})) {
|
|
892
|
+
const binding = Object.hasOwn(options.bindings, name) ? options.bindings[name] : void 0;
|
|
893
|
+
if (!binding) throw new Error(`ComfyUI provider input "${name}" has no binding`);
|
|
894
|
+
if (isComfyImageInput(input)) {
|
|
895
|
+
const bytes = await readFile(input.path);
|
|
896
|
+
if (sha256(bytes) !== input.sha256) {
|
|
897
|
+
throw new Error(`ComfyUI provider input "${name}" changed before upload`);
|
|
898
|
+
}
|
|
899
|
+
const uploaded = await this.client.uploadImage(bytes, input.sha256, input.format);
|
|
900
|
+
setBinding(workflow, binding, uploaded);
|
|
901
|
+
} else {
|
|
902
|
+
setBinding(workflow, binding, input);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
823
905
|
if (spec.revision) {
|
|
824
906
|
if (!spec.revision.sourceSha256 || !spec.revision.sourceFormat) {
|
|
825
907
|
throw new Error("ComfyUI revision source is not ready");
|
|
@@ -854,19 +936,22 @@ var ComfyUIProvider = class _ComfyUIProvider {
|
|
|
854
936
|
}
|
|
855
937
|
}
|
|
856
938
|
const promptId = await this.client.submit(workflow);
|
|
857
|
-
|
|
858
|
-
|
|
939
|
+
const inputs = providerInputProvenance(spec.providerInputs);
|
|
940
|
+
const metadata = {
|
|
941
|
+
...Object.keys(inputs).length ? { inputs } : {},
|
|
859
942
|
...spec.revision ? {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
...spec.revision.maskSha256 ? { maskSha256: spec.revision.maskSha256 } : {}
|
|
866
|
-
}
|
|
943
|
+
revision: {
|
|
944
|
+
mode: spec.revision.mode,
|
|
945
|
+
sourceAssetId: spec.revision.sourceAssetId,
|
|
946
|
+
sourceSha256: spec.revision.sourceSha256,
|
|
947
|
+
...spec.revision.maskSha256 ? { maskSha256: spec.revision.maskSha256 } : {}
|
|
867
948
|
}
|
|
868
949
|
} : {}
|
|
869
950
|
};
|
|
951
|
+
return {
|
|
952
|
+
jobId: encodeJob(promptId, options.outputNodeId),
|
|
953
|
+
...Object.keys(metadata).length ? { metadata } : {}
|
|
954
|
+
};
|
|
870
955
|
}
|
|
871
956
|
async poll(jobId, _generator, context) {
|
|
872
957
|
const { promptId, outputNodeId } = decodeJob(jobId);
|
|
@@ -941,28 +1026,10 @@ function parseOptions(value) {
|
|
|
941
1026
|
throw new Error("ComfyUI numImages must be a whole number from 1 to 16");
|
|
942
1027
|
}
|
|
943
1028
|
if (!isObject(value.bindings)) throw new Error("ComfyUI bindings must be an object");
|
|
944
|
-
const
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
"batchSize",
|
|
949
|
-
"seed",
|
|
950
|
-
"sourceImage",
|
|
951
|
-
"maskImage",
|
|
952
|
-
"strength"
|
|
953
|
-
]);
|
|
954
|
-
const extraBindings = Object.keys(value.bindings).filter((key) => !bindingKeys.has(key));
|
|
955
|
-
if (extraBindings.length) throw new Error(`Unknown ComfyUI binding(s): ${extraBindings.join(", ")}`);
|
|
956
|
-
const bindings = {
|
|
957
|
-
prompt: parseBinding(value.bindings.prompt, "prompt"),
|
|
958
|
-
...value.bindings.width == null ? {} : { width: parseBinding(value.bindings.width, "width") },
|
|
959
|
-
...value.bindings.height == null ? {} : { height: parseBinding(value.bindings.height, "height") },
|
|
960
|
-
...value.bindings.batchSize == null ? {} : { batchSize: parseBinding(value.bindings.batchSize, "batchSize") },
|
|
961
|
-
...value.bindings.seed == null ? {} : { seed: parseBinding(value.bindings.seed, "seed") },
|
|
962
|
-
...value.bindings.sourceImage == null ? {} : { sourceImage: parseBinding(value.bindings.sourceImage, "sourceImage") },
|
|
963
|
-
...value.bindings.maskImage == null ? {} : { maskImage: parseBinding(value.bindings.maskImage, "maskImage") },
|
|
964
|
-
...value.bindings.strength == null ? {} : { strength: parseBinding(value.bindings.strength, "strength") }
|
|
965
|
-
};
|
|
1029
|
+
const bindings = Object.fromEntries(
|
|
1030
|
+
Object.entries(value.bindings).map(([name, binding]) => [name, parseBinding(binding, name)])
|
|
1031
|
+
);
|
|
1032
|
+
if (!bindings.prompt) throw new Error("ComfyUI bindings.prompt must be an object");
|
|
966
1033
|
const workflow = value.workflow == null ? void 0 : parseWorkflow(value.workflow, workflowFile);
|
|
967
1034
|
const workflowSha256 = value.workflowSha256 == null ? void 0 : requiredString(value.workflowSha256, "workflowSha256");
|
|
968
1035
|
return {
|
|
@@ -1002,6 +1069,7 @@ function parseWorkflow(value, label) {
|
|
|
1002
1069
|
return value;
|
|
1003
1070
|
}
|
|
1004
1071
|
function validateWorkflowBindings(workflow, options) {
|
|
1072
|
+
const targets = /* @__PURE__ */ new Map();
|
|
1005
1073
|
for (const [name, binding] of Object.entries(options.bindings)) {
|
|
1006
1074
|
if (!binding) continue;
|
|
1007
1075
|
const node = workflow[binding.nodeId];
|
|
@@ -1011,6 +1079,12 @@ function validateWorkflowBindings(workflow, options) {
|
|
|
1011
1079
|
`ComfyUI ${name} binding refers to missing input "${binding.input}" on node "${binding.nodeId}"`
|
|
1012
1080
|
);
|
|
1013
1081
|
}
|
|
1082
|
+
const target = `${binding.nodeId}.${binding.input}`;
|
|
1083
|
+
const prior = targets.get(target);
|
|
1084
|
+
if (prior) {
|
|
1085
|
+
throw new Error(`ComfyUI bindings.${name} and bindings.${prior} target the same input ${target}`);
|
|
1086
|
+
}
|
|
1087
|
+
targets.set(target, name);
|
|
1014
1088
|
}
|
|
1015
1089
|
if (!workflow[options.outputNodeId]) {
|
|
1016
1090
|
throw new Error(`ComfyUI outputNodeId refers to missing node "${options.outputNodeId}"`);
|
|
@@ -1048,6 +1122,7 @@ function outputImages(entry, outputNodeId) {
|
|
|
1048
1122
|
}
|
|
1049
1123
|
function comfyMetadata(spec, promptId, outputNodeId, images) {
|
|
1050
1124
|
const options = resolvedOptions(spec);
|
|
1125
|
+
const inputs = providerInputProvenance(spec.providerInputs);
|
|
1051
1126
|
return {
|
|
1052
1127
|
promptId,
|
|
1053
1128
|
outputNodeId,
|
|
@@ -1055,6 +1130,7 @@ function comfyMetadata(spec, promptId, outputNodeId, images) {
|
|
|
1055
1130
|
workflowFile: options.workflowFile,
|
|
1056
1131
|
workflowSha256: options.workflowSha256,
|
|
1057
1132
|
files: images.map((image) => ({ ...image })),
|
|
1133
|
+
...Object.keys(inputs).length ? { inputs } : {},
|
|
1058
1134
|
...spec.revision ? {
|
|
1059
1135
|
revision: {
|
|
1060
1136
|
mode: spec.revision.mode,
|
|
@@ -1066,6 +1142,41 @@ function comfyMetadata(spec, promptId, outputNodeId, images) {
|
|
|
1066
1142
|
} : {}
|
|
1067
1143
|
};
|
|
1068
1144
|
}
|
|
1145
|
+
function isImageBinding(workflow, binding) {
|
|
1146
|
+
const node = workflow[binding.nodeId];
|
|
1147
|
+
return binding.input === "image" && (node?.class_type === "LoadImage" || node?.class_type === "LoadImageMask");
|
|
1148
|
+
}
|
|
1149
|
+
function isComfyImageInput(value) {
|
|
1150
|
+
return isObject(value) && value.kind === "image" && typeof value.path === "string" && typeof value.sha256 === "string" && (value.format === "png" || value.format === "jpeg");
|
|
1151
|
+
}
|
|
1152
|
+
function providerInputProvenance(inputs) {
|
|
1153
|
+
return Object.fromEntries(Object.entries(inputs ?? {}).map(([name, input]) => [
|
|
1154
|
+
name,
|
|
1155
|
+
isComfyImageInput(input) ? { kind: "image", sha256: input.sha256, format: input.format } : { kind: "value", value: input }
|
|
1156
|
+
]));
|
|
1157
|
+
}
|
|
1158
|
+
function inputImageFormat(bytes) {
|
|
1159
|
+
if (bytes.length >= 24 && bytes.readUInt32BE(0) === 2303741511 && bytes.readUInt32BE(4) === 218765834 && bytes.toString("ascii", 12, 16) === "IHDR" && bytes.readUInt32BE(16) > 0 && bytes.readUInt32BE(20) > 0) return "png";
|
|
1160
|
+
if (bytes.length < 4 || bytes[0] !== 255 || bytes[1] !== 216) return null;
|
|
1161
|
+
let offset = 2;
|
|
1162
|
+
while (offset + 3 < bytes.length) {
|
|
1163
|
+
if (bytes[offset] !== 255) return null;
|
|
1164
|
+
while (bytes[offset] === 255) offset++;
|
|
1165
|
+
const marker = bytes[offset++];
|
|
1166
|
+
if (marker == null || marker === 217 || marker === 218) break;
|
|
1167
|
+
if (marker === 1 || marker >= 208 && marker <= 215) continue;
|
|
1168
|
+
if (offset + 2 > bytes.length) return null;
|
|
1169
|
+
const length = bytes.readUInt16BE(offset);
|
|
1170
|
+
if (length < 2 || offset + length > bytes.length) return null;
|
|
1171
|
+
const isStartOfFrame = marker >= 192 && marker <= 207 && ![196, 200, 204].includes(marker);
|
|
1172
|
+
if (isStartOfFrame) {
|
|
1173
|
+
if (length < 7) return null;
|
|
1174
|
+
return bytes.readUInt16BE(offset + 3) > 0 && bytes.readUInt16BE(offset + 5) > 0 ? "jpeg" : null;
|
|
1175
|
+
}
|
|
1176
|
+
offset += length;
|
|
1177
|
+
}
|
|
1178
|
+
return null;
|
|
1179
|
+
}
|
|
1069
1180
|
function encodeJob(promptId, outputNodeId) {
|
|
1070
1181
|
return `${promptId}#${encodeURIComponent(outputNodeId)}`;
|
|
1071
1182
|
}
|
|
@@ -1768,7 +1879,13 @@ var AssetSchema = z2.object({
|
|
|
1768
1879
|
* exactly right for a colour style. Editing the shared prompt to suit one
|
|
1769
1880
|
* style would invalidate every other style's already-generated art.
|
|
1770
1881
|
*/
|
|
1771
|
-
promptByStyle: z2.record(z2.string()).default({})
|
|
1882
|
+
promptByStyle: z2.record(z2.string()).default({}),
|
|
1883
|
+
/**
|
|
1884
|
+
* Named per-asset values consumed by the active provider's declared
|
|
1885
|
+
* bindings. Values are JSON scalars; adapters may interpret a string as a
|
|
1886
|
+
* manifest-relative file when the target node accepts an uploaded input.
|
|
1887
|
+
*/
|
|
1888
|
+
providerInputs: z2.record(z2.union([z2.string(), z2.number().finite(), z2.boolean()])).default({})
|
|
1772
1889
|
}).strict().superRefine((asset, context) => {
|
|
1773
1890
|
if (asset.source && asset.revision) {
|
|
1774
1891
|
context.addIssue({
|
|
@@ -3211,6 +3328,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
3211
3328
|
return { base64: hit.base64, width: hit.width, height: hit.height, format: hit.format };
|
|
3212
3329
|
});
|
|
3213
3330
|
const styleSpecs = /* @__PURE__ */ new Map();
|
|
3331
|
+
const providerInputIdentities = /* @__PURE__ */ new Map();
|
|
3214
3332
|
for (const [assetId, asset] of Object.entries(manifest.assets)) {
|
|
3215
3333
|
if (!resolutionAssetIds.has(assetId)) continue;
|
|
3216
3334
|
if (asset.styles.length && !asset.styles.includes(styleId)) continue;
|
|
@@ -3218,6 +3336,20 @@ async function resolveSpecs(loaded, filter) {
|
|
|
3218
3336
|
if (!activeProvider.supports(generator)) {
|
|
3219
3337
|
throw new Error(`Provider "${activeProvider.id}" does not support generator "${generator}"`);
|
|
3220
3338
|
}
|
|
3339
|
+
if (Object.keys(asset.providerInputs).length && !activeProvider.resolveInputs) {
|
|
3340
|
+
throw new Error(
|
|
3341
|
+
`Provider "${activeProvider.id}" does not support asset providerInputs (${styleId}/${assetId})`
|
|
3342
|
+
);
|
|
3343
|
+
}
|
|
3344
|
+
const inputResolution = activeProvider.resolveInputs ? await activeProvider.resolveInputs(asset.providerInputs, {
|
|
3345
|
+
root,
|
|
3346
|
+
styleId,
|
|
3347
|
+
assetId,
|
|
3348
|
+
providerOptions
|
|
3349
|
+
}) : { inputs: {} };
|
|
3350
|
+
const providerInputs = inputResolution.inputs;
|
|
3351
|
+
const providerInputIdentity = inputResolution.identity ?? providerInputs;
|
|
3352
|
+
providerInputIdentities.set(assetId, providerInputIdentity);
|
|
3221
3353
|
let width;
|
|
3222
3354
|
let height;
|
|
3223
3355
|
let size;
|
|
@@ -3246,6 +3378,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
3246
3378
|
assetId,
|
|
3247
3379
|
provider: activeProvider.id,
|
|
3248
3380
|
providerOptions,
|
|
3381
|
+
providerInputs,
|
|
3249
3382
|
generator,
|
|
3250
3383
|
prompt,
|
|
3251
3384
|
width,
|
|
@@ -3344,7 +3477,12 @@ async function resolveSpecs(loaded, filter) {
|
|
|
3344
3477
|
...asset.revision.strength == null ? {} : { strength: asset.revision.strength }
|
|
3345
3478
|
};
|
|
3346
3479
|
}
|
|
3347
|
-
resolved.specHash = specHash(
|
|
3480
|
+
resolved.specHash = specHash(
|
|
3481
|
+
resolved,
|
|
3482
|
+
styleImageHashes,
|
|
3483
|
+
providerOptionIdentity,
|
|
3484
|
+
providerInputIdentities.get(assetId)
|
|
3485
|
+
);
|
|
3348
3486
|
activeProvider.validate?.(resolved, resolvedImages);
|
|
3349
3487
|
const estimate = validateCostEstimate(activeProvider.id, activeProvider.estimate(resolved));
|
|
3350
3488
|
resolved.cost = estimate.amount;
|
|
@@ -6767,6 +6905,7 @@ function buildManifest(name, styleId, generator, outDir, scanned) {
|
|
|
6767
6905
|
file: asset.file,
|
|
6768
6906
|
tags: [],
|
|
6769
6907
|
styles: [],
|
|
6908
|
+
providerInputs: {},
|
|
6770
6909
|
...generator === "map" ? { width: asset.width, height: asset.height } : asset.width === asset.height && asset.width !== commonSize ? { size: asset.width } : {}
|
|
6771
6910
|
};
|
|
6772
6911
|
}
|
|
@@ -7829,6 +7968,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
7829
7968
|
file: rel,
|
|
7830
7969
|
tags: ["salvaged"],
|
|
7831
7970
|
styles: [ctx.styleId],
|
|
7971
|
+
providerInputs: {},
|
|
7832
7972
|
...orphan.width === orphan.height ? { size: orphan.width } : { width: orphan.width, height: orphan.height }
|
|
7833
7973
|
};
|
|
7834
7974
|
const durableSource = shouldPersistSourceUrl(orphan.previewUrl) ? orphan.previewUrl : null;
|